1 /*
   2  * Copyright (c) 2004, 2006, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package sun.tools.jconsole;
  27 
  28 import java.awt.*;
  29 import java.awt.event.*;
  30 import java.beans.*;
  31 import java.io.*;
  32 import java.net.*;
  33 import java.util.*;
  34 import java.util.List;
  35 
  36 import javax.swing.*;
  37 import javax.swing.border.*;
  38 import javax.swing.event.*;
  39 import javax.swing.plaf.*;
  40 import javax.security.auth.login.FailedLoginException;
  41 import javax.net.ssl.SSLHandshakeException;
  42 
  43 import com.sun.tools.jconsole.JConsolePlugin;
  44 
  45 import sun.net.util.IPAddressUtil;
  46 import sun.tools.jconsole.resources.Messages;
  47 
  48 import static sun.tools.jconsole.Utilities.*;
  49 
  50 @SuppressWarnings("serial")
  51 public class JConsole extends JFrame
  52     implements ActionListener, InternalFrameListener {
  53 
  54     static /*final*/ boolean IS_GTK;
  55     static /*final*/ boolean IS_WIN;
  56 
  57     static {
  58         // Apply the system L&F if it is GTK or Windows, and
  59         // the L&F is not specified using a system property.
  60         if (System.getProperty("swing.defaultlaf") == null) {
  61             String systemLaF = UIManager.getSystemLookAndFeelClassName();
  62             if (systemLaF.equals("com.sun.java.swing.plaf.gtk.GTKLookAndFeel") ||
  63                 systemLaF.equals("com.sun.java.swing.plaf.windows.WindowsLookAndFeel")) {
  64 
  65                 try {
  66                     UIManager.setLookAndFeel(systemLaF);
  67                 } catch (Exception e) {
  68                     System.err.println(Resources.format(Messages.JCONSOLE_COLON_, e.getMessage()));
  69                 }
  70             }
  71         }
  72 
  73         updateLafValues();
  74     }
  75 
  76 
  77     static void updateLafValues() {
  78         String lafName = UIManager.getLookAndFeel().getClass().getName();
  79         IS_GTK = lafName.equals("com.sun.java.swing.plaf.gtk.GTKLookAndFeel");
  80         IS_WIN = lafName.equals("com.sun.java.swing.plaf.windows.WindowsLookAndFeel");
  81 
  82         //BorderedComponent.updateLafValues();
  83     }
  84 
  85 
  86     private final static String title =
  87         Messages.JAVA_MONITORING___MANAGEMENT_CONSOLE;
  88     public final static String ROOT_URL =
  89         "service:jmx:";
  90 
  91     private static int updateInterval = 4000;
  92     private static String pluginPath = "";
  93 
  94     private JMenuBar menuBar;
  95     private JMenuItem hotspotMI, connectMI, exitMI;
  96     private WindowMenu windowMenu;
  97     private JMenuItem tileMI, cascadeMI, minimizeAllMI, restoreAllMI;
  98     private JMenuItem userGuideMI, aboutMI;
  99 
 100     private JButton connectButton;
 101     private JDesktopPane desktop;
 102     private ConnectDialog connectDialog;
 103     private CreateMBeanDialog createDialog;
 104 
 105     private ArrayList<VMInternalFrame> windows =
 106         new ArrayList<VMInternalFrame>();
 107 
 108     private int frameLoc = 5;
 109     static boolean debug;
 110 
 111     public JConsole(boolean hotspot) {
 112         super(title);
 113 
 114         setRootPane(new FixedJRootPane());
 115         setAccessibleDescription(this,
 116                 Messages.JCONSOLE_ACCESSIBLE_DESCRIPTION);
 117         setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
 118 
 119         menuBar = new JMenuBar();
 120         setJMenuBar(menuBar);
 121 
 122         // TODO: Use Actions !
 123 
 124         JMenu connectionMenu = new JMenu(Messages.CONNECTION);
 125         connectionMenu.setMnemonic(Resources.getMnemonicInt(Messages.CONNECTION));
 126         menuBar.add(connectionMenu);
 127         if(hotspot) {
 128             hotspotMI = new JMenuItem(Messages.HOTSPOT_MBEANS_ELLIPSIS);
 129             hotspotMI.setMnemonic(Resources.getMnemonicInt(Messages.HOTSPOT_MBEANS_ELLIPSIS));
 130             hotspotMI.setAccelerator(KeyStroke.
 131                                      getKeyStroke(KeyEvent.VK_H,
 132                                                   InputEvent.CTRL_MASK));
 133             hotspotMI.addActionListener(this);
 134             connectionMenu.add(hotspotMI);
 135 
 136             connectionMenu.addSeparator();
 137         }
 138 
 139         connectMI = new JMenuItem(Messages.NEW_CONNECTION_ELLIPSIS);
 140         connectMI.setMnemonic(Resources.getMnemonicInt(Messages.NEW_CONNECTION_ELLIPSIS));
 141         connectMI.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_N,
 142                                                         InputEvent.CTRL_MASK));
 143         connectMI.addActionListener(this);
 144         connectionMenu.add(connectMI);
 145 
 146         connectionMenu.addSeparator();
 147 
 148         exitMI = new JMenuItem(Messages.EXIT);
 149         exitMI.setMnemonic(Resources.getMnemonicInt(Messages.EXIT));
 150         exitMI.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_F4,
 151                                                      InputEvent.ALT_MASK));
 152         exitMI.addActionListener(this);
 153         connectionMenu.add(exitMI);
 154 
 155 
 156         JMenu helpMenu = new JMenu(Messages.HELP_MENU_TITLE);
 157         helpMenu.setMnemonic(Resources.getMnemonicInt(Messages.HELP_MENU_TITLE));
 158         menuBar.add(helpMenu);
 159 
 160         if (AboutDialog.isBrowseSupported()) {
 161             userGuideMI = new JMenuItem(Messages.HELP_MENU_USER_GUIDE_TITLE);
 162             userGuideMI.setMnemonic(Resources.getMnemonicInt(Messages.HELP_MENU_USER_GUIDE_TITLE));
 163             userGuideMI.addActionListener(this);
 164             helpMenu.add(userGuideMI);
 165             helpMenu.addSeparator();
 166         }
 167         aboutMI = new JMenuItem(Messages.HELP_MENU_ABOUT_TITLE);
 168         aboutMI.setMnemonic(Resources.getMnemonicInt(Messages.HELP_MENU_ABOUT_TITLE));
 169         aboutMI.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_F1, 0));
 170         aboutMI.addActionListener(this);
 171         helpMenu.add(aboutMI);
 172     }
 173 
 174     public JDesktopPane getDesktopPane() {
 175         return desktop;
 176     }
 177 
 178     public List<VMInternalFrame> getInternalFrames() {
 179         return windows;
 180     }
 181 
 182     private void createMDI() {
 183         // Restore title - we now show connection name on internal frames
 184         setTitle(title);
 185 
 186         Container cp = getContentPane();
 187         Component oldCenter =
 188             ((BorderLayout)cp.getLayout()).
 189             getLayoutComponent(BorderLayout.CENTER);
 190 
 191         windowMenu = new WindowMenu(Messages.WINDOW);
 192         windowMenu.setMnemonic(Resources.getMnemonicInt(Messages.WINDOW));
 193         // Add Window menu before Help menu
 194         menuBar.add(windowMenu, menuBar.getComponentCount() - 1);
 195 
 196         desktop = new JDesktopPane();
 197         desktop.setBackground(new Color(235, 245, 255));
 198 
 199         cp.add(desktop, BorderLayout.CENTER);
 200 
 201         if (oldCenter instanceof VMPanel) {
 202             addFrame((VMPanel)oldCenter);
 203         }
 204     }
 205 
 206     private class WindowMenu extends JMenu {
 207         VMInternalFrame[] windowMenuWindows = new VMInternalFrame[0];
 208         int separatorPosition;
 209 
 210         // The width value of viewR is used to truncate long menu items.
 211         // The rest are placeholders and are ignored for this purpose.
 212         Rectangle viewR = new Rectangle(0, 0, 400, 20);
 213         Rectangle textR = new Rectangle(0, 0, 0, 0);
 214         Rectangle iconR = new Rectangle(0, 0, 0, 0);
 215 
 216         WindowMenu(String text) {
 217             super(text);
 218 
 219             cascadeMI = new JMenuItem(Messages.CASCADE);
 220             cascadeMI.setMnemonic(Resources.getMnemonicInt(Messages.CASCADE));
 221             cascadeMI.addActionListener(JConsole.this);
 222             add(cascadeMI);
 223 
 224             tileMI = new JMenuItem(Messages.TILE);
 225             tileMI.setMnemonic(Resources.getMnemonicInt(Messages.TILE));
 226             tileMI.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_T,
 227                                                          InputEvent.CTRL_MASK));
 228             tileMI.addActionListener(JConsole.this);
 229             add(tileMI);
 230 
 231             minimizeAllMI = new JMenuItem(Messages.MINIMIZE_ALL);
 232             minimizeAllMI.setMnemonic(Resources.getMnemonicInt(Messages.MINIMIZE_ALL));
 233             minimizeAllMI.addActionListener(JConsole.this);
 234             add(minimizeAllMI);
 235 
 236             restoreAllMI = new JMenuItem(Messages.RESTORE_ALL);
 237             restoreAllMI.setMnemonic(Resources.getMnemonicInt(Messages.RESTORE_ALL));
 238             restoreAllMI.addActionListener(JConsole.this);
 239             add(restoreAllMI);
 240 
 241             separatorPosition = getMenuComponentCount();
 242         }
 243 
 244         private void add(VMInternalFrame vmIF) {
 245             if (separatorPosition == getMenuComponentCount()) {
 246                 addSeparator();
 247             }
 248 
 249             int index = -1;
 250             int position = separatorPosition + 1;
 251             int n = windowMenuWindows.length;
 252 
 253             for (int i = 0; i < n; i++) {
 254                 if (windowMenuWindows[i] != null) {
 255                     // Slot is in use, try next
 256                     position++;
 257                 } else {
 258                     // Found a free slot
 259                     index = i;
 260                     break;
 261                 }
 262             }
 263 
 264             if (index == -1) {
 265                 // Create a slot at the end
 266                 VMInternalFrame[] newArray = new VMInternalFrame[n + 1];
 267                 System.arraycopy(windowMenuWindows, 0, newArray, 0, n);
 268                 windowMenuWindows = newArray;
 269                 index = n;
 270             }
 271 
 272             windowMenuWindows[index] = vmIF;
 273 
 274             String indexString = "" + (index+1);
 275             String vmName = vmIF.getVMPanel().getDisplayName();
 276             // Maybe truncate menu item string and end with "..."
 277             String text =
 278                 SwingUtilities.layoutCompoundLabel(this,
 279                                         getGraphics().getFontMetrics(getFont()),
 280                                         indexString +  " " + vmName,
 281                                         null, 0, 0, 0, 0,
 282                                         viewR, iconR, textR, 0);
 283             JMenuItem mi = new JMenuItem(text);
 284             if (text.endsWith("...")) {
 285                 mi.setToolTipText(vmName);
 286             }
 287 
 288             // Set mnemonic using last digit of number
 289             int nDigits = indexString.length();
 290             mi.setMnemonic(indexString.charAt(nDigits-1));
 291             mi.setDisplayedMnemonicIndex(nDigits-1);
 292 
 293             mi.putClientProperty("JConsole.vmIF", vmIF);
 294             mi.addActionListener(JConsole.this);
 295             vmIF.putClientProperty("JConsole.menuItem", mi);
 296             add(mi, position);
 297         }
 298 
 299         private void remove(VMInternalFrame vmIF) {
 300             for (int i = 0; i < windowMenuWindows.length; i++) {
 301                 if (windowMenuWindows[i] == vmIF) {
 302                     windowMenuWindows[i] = null;
 303                 }
 304             }
 305             JMenuItem mi = (JMenuItem)vmIF.getClientProperty("JConsole.menuItem");
 306             remove(mi);
 307             mi.putClientProperty("JConsole.vmIF", null);
 308             vmIF.putClientProperty("JConsole.menuItem", null);
 309 
 310             if (separatorPosition == getMenuComponentCount() - 1) {
 311                 remove(getMenuComponent(getMenuComponentCount() - 1));
 312             }
 313         }
 314     }
 315 
 316     public void actionPerformed(ActionEvent ev) {
 317         Object src = ev.getSource();
 318         if (src == hotspotMI) {
 319             showCreateMBeanDialog();
 320         }
 321 
 322         if (src == connectButton || src == connectMI) {
 323             VMPanel vmPanel = null;
 324             JInternalFrame vmIF = desktop.getSelectedFrame();
 325             if (vmIF instanceof VMInternalFrame) {
 326                 vmPanel = ((VMInternalFrame)vmIF).getVMPanel();
 327             }
 328                 String hostName = "";
 329                 String url = "";
 330                 if (vmPanel != null) {
 331                     hostName = vmPanel.getHostName();
 332                     if(vmPanel.getUrl() != null)
 333                         url = vmPanel.getUrl();
 334                 }
 335                 showConnectDialog(url, hostName, 0, null, null, null);
 336         } else if (src == tileMI) {
 337             tileWindows();
 338         } else if (src == cascadeMI) {
 339             cascadeWindows();
 340         } else if (src == minimizeAllMI) {
 341             for (VMInternalFrame vmIF : windows) {
 342                 try {
 343                     vmIF.setIcon(true);
 344                 } catch (PropertyVetoException ex) {
 345                     // Ignore
 346                 }
 347             }
 348         } else if (src == restoreAllMI) {
 349             for (VMInternalFrame vmIF : windows) {
 350                 try {
 351                     vmIF.setIcon(false);
 352                 } catch (PropertyVetoException ex) {
 353                     // Ignore
 354                 }
 355             }
 356         } else if (src == exitMI) {
 357             System.exit(0);
 358         } else if (src == userGuideMI) {
 359             AboutDialog.browseUserGuide(this);
 360         } else if (src == aboutMI) {
 361             AboutDialog.showAboutDialog(this);
 362         } else if (src instanceof JMenuItem) {
 363             JMenuItem mi = (JMenuItem)src;
 364             VMInternalFrame vmIF = (VMInternalFrame)mi.
 365                 getClientProperty("JConsole.vmIF");
 366             if (vmIF != null) {
 367                 try {
 368                     vmIF.setIcon(false);
 369                     vmIF.setSelected(true);
 370                 } catch (PropertyVetoException ex) {
 371                     // Ignore
 372                 }
 373                 vmIF.moveToFront();
 374             }
 375         }
 376     }
 377 
 378 
 379     public void tileWindows() {
 380         int w = -1;
 381         int h = -1;
 382         int n = 0;
 383         for (VMInternalFrame vmIF : windows) {
 384             if (!vmIF.isIcon()) {
 385                 n++;
 386                 if (w == -1) {
 387                     try {
 388                         vmIF.setMaximum(true);
 389                         w = vmIF.getWidth();
 390                         h = vmIF.getHeight();
 391                     } catch (PropertyVetoException ex) {
 392                         // Ignore
 393                     }
 394                 }
 395             }
 396         }
 397         if (n > 0 && w > 0 && h > 0) {
 398             int rows = (int)Math.ceil(Math.sqrt(n));
 399             int cols = n / rows;
 400             if (rows * cols < n) cols++;
 401             int x = 0;
 402             int y = 0;
 403             w /= cols;
 404             h /= rows;
 405             int col = 0;
 406             for (VMInternalFrame vmIF : windows) {
 407                 if (!vmIF.isIcon()) {
 408                     try {
 409                         vmIF.setMaximum(n==1);
 410                     } catch (PropertyVetoException ex) {
 411                         // Ignore
 412                     }
 413                     if (n > 1) {
 414                         vmIF.setBounds(x, y, w, h);
 415                     }
 416                     if (col < cols-1) {
 417                         col++;
 418                         x += w;
 419                     } else {
 420                         col = 0;
 421                         x = 0;
 422                         y += h;
 423                     }
 424                 }
 425             }
 426         }
 427     }
 428 
 429     public void cascadeWindows() {
 430         int n = 0;
 431         int w = -1;
 432         int h = -1;
 433         for (VMInternalFrame vmIF : windows) {
 434             if (!vmIF.isIcon()) {
 435                 try {
 436                     vmIF.setMaximum(false);
 437                 } catch (PropertyVetoException ex) {
 438                     // Ignore
 439                 }
 440                 n++;
 441                 vmIF.pack();
 442                 if (w == -1) {
 443                     try {
 444                         w = vmIF.getWidth();
 445                         h = vmIF.getHeight();
 446                         vmIF.setMaximum(true);
 447                         w = vmIF.getWidth() - w;
 448                         h = vmIF.getHeight() - h;
 449                         vmIF.pack();
 450                     } catch (PropertyVetoException ex) {
 451                         // Ignore
 452                     }
 453                 }
 454             }
 455         }
 456         int x = 0;
 457         int y = 0;
 458         int dX = (n > 1) ? (w / (n - 1)) : 0;
 459         int dY = (n > 1) ? (h / (n - 1)) : 0;
 460         for (VMInternalFrame vmIF : windows) {
 461             if (!vmIF.isIcon()) {
 462                 vmIF.setLocation(x, y);
 463                 vmIF.moveToFront();
 464                 x += dX;
 465                 y += dY;
 466             }
 467         }
 468     }
 469 
 470     // Call on EDT
 471     void addHost(String hostName, int port,
 472                  String userName, String password) {
 473         addHost(hostName, port, userName, password, false);
 474     }
 475 
 476     // Call on EDT
 477     void addVmid(LocalVirtualMachine lvm) {
 478         addVmid(lvm, false);
 479     }
 480 
 481     // Call on EDT
 482     void addVmid(final LocalVirtualMachine lvm, final boolean tile) {
 483         new Thread("JConsole.addVmid") {
 484             public void run() {
 485                 try {
 486                     addProxyClient(ProxyClient.getProxyClient(lvm), tile);
 487                 } catch (final SecurityException ex) {
 488                     failed(ex, null, null, null);
 489                 } catch (final IOException ex) {
 490                     failed(ex, null, null, null);
 491                 }
 492             }
 493         }.start();
 494     }
 495 
 496     // Call on EDT
 497     void addUrl(final String url,
 498                 final String userName,
 499                 final String password,
 500                 final boolean tile) {
 501         new Thread("JConsole.addUrl") {
 502             public void run() {
 503                 try {
 504                     addProxyClient(ProxyClient.getProxyClient(url, userName, password),
 505                                    tile);
 506                 } catch (final MalformedURLException ex) {
 507                     failed(ex, url, userName, password);
 508                 } catch (final SecurityException ex) {
 509                     failed(ex, url, userName, password);
 510                 } catch (final IOException ex) {
 511                     failed(ex, url, userName, password);
 512                 }
 513             }
 514         }.start();
 515     }
 516 
 517 
 518     // Call on EDT
 519     void addHost(final String hostName, final int port,
 520                  final String userName, final String password,
 521                  final boolean tile) {
 522         new Thread("JConsole.addHost") {
 523             public void run() {
 524                 try {
 525                     addProxyClient(ProxyClient.getProxyClient(hostName, port,
 526                                                               userName, password),
 527                                    tile);
 528                 } catch (final IOException ex) {
 529                     dbgStackTrace(ex);
 530                     SwingUtilities.invokeLater(new Runnable() {
 531                         public void run() {
 532                             showConnectDialog(null, hostName, port,
 533                                               userName, password, errorMessage(ex));
 534                         }
 535                     });
 536                 }
 537             }
 538         }.start();
 539     }
 540 
 541 
 542     // Call on worker thread
 543     void addProxyClient(final ProxyClient proxyClient, final boolean tile) {
 544         SwingUtilities.invokeLater(new Runnable() {
 545             public void run() {
 546                 VMPanel vmPanel = new VMPanel(proxyClient, updateInterval);
 547                 addFrame(vmPanel);
 548 
 549                 if (tile) {
 550                     SwingUtilities.invokeLater(new Runnable() {
 551                         public void run() {
 552                             tileWindows();
 553                         }
 554                     });
 555                 }
 556                 vmPanel.connect();
 557             }
 558         });
 559     }
 560 
 561 
 562     // Call on worker thread
 563     private void failed(final Exception ex,
 564                         final String url,
 565                         final String userName,
 566                         final String password) {
 567         SwingUtilities.invokeLater(new Runnable() {
 568             public void run() {
 569                 dbgStackTrace(ex);
 570                 showConnectDialog(url,
 571                                   null,
 572                                   -1,
 573                                   userName,
 574                                   password,
 575                                   errorMessage(ex));
 576             }
 577         });
 578     }
 579 
 580 
 581     private VMInternalFrame addFrame(VMPanel vmPanel) {
 582         final VMInternalFrame vmIF = new VMInternalFrame(vmPanel);
 583 
 584         for (VMInternalFrame f : windows) {
 585             try {
 586                 f.setMaximum(false);
 587             } catch (PropertyVetoException ex) {
 588                 // Ignore
 589             }
 590         }
 591         desktop.add(vmIF);
 592 
 593         vmIF.setLocation(frameLoc, frameLoc);
 594         frameLoc += 30;
 595         vmIF.setVisible(true);
 596         windows.add(vmIF);
 597         if (windows.size() == 1) {
 598             try {
 599                 vmIF.setMaximum(true);
 600             } catch (PropertyVetoException ex) {
 601                 // Ignore
 602             }
 603         }
 604         vmIF.addInternalFrameListener(this);
 605         windowMenu.add(vmIF);
 606 
 607         return vmIF;
 608     }
 609 
 610     private void showConnectDialog(String url,
 611                                    String hostName,
 612                                    int port,
 613                                    String userName,
 614                                    String password,
 615                                    String msg) {
 616         if (connectDialog == null) {
 617             connectDialog = new ConnectDialog(this);
 618         }
 619         connectDialog.setConnectionParameters(url,
 620                                               hostName,
 621                                               port,
 622                                               userName,
 623                                               password,
 624                                               msg);
 625 
 626         connectDialog.refresh();
 627         connectDialog.setVisible(true);
 628         try {
 629             // Bring to front of other dialogs
 630             connectDialog.setSelected(true);
 631         } catch (PropertyVetoException e) {
 632         }
 633     }
 634 
 635     private void showCreateMBeanDialog() {
 636         if (createDialog == null) {
 637             createDialog = new CreateMBeanDialog(this);
 638         }
 639         createDialog.setVisible(true);
 640         try {
 641             // Bring to front of other dialogs
 642             createDialog.setSelected(true);
 643         } catch (PropertyVetoException e) {
 644         }
 645     }
 646 
 647     private void removeVMInternalFrame(VMInternalFrame vmIF) {
 648         windowMenu.remove(vmIF);
 649         desktop.remove(vmIF);
 650         desktop.repaint();
 651         vmIF.getVMPanel().cleanUp();
 652         vmIF.dispose();
 653     }
 654 
 655     private boolean isProxyClientUsed(ProxyClient client) {
 656         for(VMInternalFrame frame : windows) {
 657             ProxyClient cli = frame.getVMPanel().getProxyClient(false);
 658             if(client == cli)
 659                 return true;
 660         }
 661         return false;
 662     }
 663 
 664     static boolean isValidRemoteString(String txt) {
 665         boolean valid = false;
 666         if (txt != null) {
 667             txt = txt.trim();
 668             if (txt.startsWith(ROOT_URL)) {
 669                 if (txt.length() > ROOT_URL.length()) {
 670                     valid = true;
 671                 }
 672             } else {
 673                 //---------------------------------------
 674                 // Supported host and port combinations:
 675                 //     hostname:port
 676                 //     IPv4Address:port
 677                 //     [IPv6Address]:port
 678                 //---------------------------------------
 679 
 680                 // Is literal IPv6 address?
 681                 //
 682                 if (txt.startsWith("[")) {
 683                     int index = txt.indexOf("]:");
 684                     if (index != -1) {
 685                         // Extract literal IPv6 address
 686                         //
 687                         String address = txt.substring(1, index);
 688                         if (IPAddressUtil.isIPv6LiteralAddress(address)) {
 689                             // Extract port
 690                             //
 691                             try {
 692                                 String portStr = txt.substring(index + 2);
 693                                 int port = Integer.parseInt(portStr);
 694                                 if (port >= 0 && port <= 0xFFFF) {
 695                                     valid = true;
 696                                 }
 697                             } catch (NumberFormatException ex) {
 698                                 valid = false;
 699                             }
 700                         }
 701                     }
 702                 } else {
 703                     String[] s = txt.split(":");
 704                     if (s.length == 2) {
 705                         try {
 706                             int port = Integer.parseInt(s[1]);
 707                             if (port >= 0 && port <= 0xFFFF) {
 708                                 valid = true;
 709                             }
 710                         } catch (NumberFormatException ex) {
 711                             valid = false;
 712                         }
 713                     }
 714                 }
 715             }
 716         }
 717         return valid;
 718     }
 719 
 720     private String errorMessage(Exception ex) {
 721        String msg = Messages.CONNECTION_FAILED;
 722        if (ex instanceof IOException || ex instanceof SecurityException) {
 723            Throwable cause = null;
 724            Throwable c = ex.getCause();
 725            while (c != null) {
 726                cause = c;
 727                c = c.getCause();
 728            }
 729            if (cause instanceof ConnectException) {
 730                return msg + ": " + cause.getMessage();
 731            } else if (cause instanceof UnknownHostException) {
 732                return Resources.format(Messages.UNKNOWN_HOST, cause.getMessage());
 733            } else if (cause instanceof NoRouteToHostException) {
 734                return msg + ": " + cause.getMessage();
 735            } else if (cause instanceof FailedLoginException) {
 736                return msg + ": " + cause.getMessage();
 737            } else if (cause instanceof SSLHandshakeException) {
 738                return msg + ": "+ cause.getMessage();
 739            }
 740         } else if (ex instanceof MalformedURLException) {
 741            return Resources.format(Messages.INVALID_URL, ex.getMessage());
 742         }
 743         return msg + ": " + ex.getMessage();
 744     }
 745 
 746 
 747     // InternalFrameListener interface
 748 
 749     public void internalFrameClosing(InternalFrameEvent e) {
 750         VMInternalFrame vmIF = (VMInternalFrame)e.getInternalFrame();
 751         removeVMInternalFrame(vmIF);
 752         windows.remove(vmIF);
 753         ProxyClient client = vmIF.getVMPanel().getProxyClient(false);
 754         if(!isProxyClientUsed(client))
 755             client.markAsDead();
 756         if (windows.size() == 0) {
 757             showConnectDialog("", "", 0, null, null, null);
 758         }
 759     }
 760 
 761     public void internalFrameOpened(InternalFrameEvent e) {}
 762     public void internalFrameClosed(InternalFrameEvent e) {}
 763     public void internalFrameIconified(InternalFrameEvent e) {}
 764     public void internalFrameDeiconified(InternalFrameEvent e) {}
 765     public void internalFrameActivated(InternalFrameEvent e) {}
 766     public void internalFrameDeactivated(InternalFrameEvent e) {}
 767 
 768 
 769     private static void usage() {
 770         System.err.println(Resources.format(Messages.ZZ_USAGE_TEXT, "jconsole"));
 771     }
 772 
 773     private static void mainInit(final List<String> urls,
 774                                  final List<String> hostNames,
 775                                  final List<Integer> ports,
 776                                  final List<LocalVirtualMachine> vmids,
 777                                  final ProxyClient proxyClient,
 778                                  final boolean noTile,
 779                                  final boolean hotspot) {
 780 
 781 
 782         // Always create Swing GUI on the Event Dispatching Thread
 783         SwingUtilities.invokeLater(new Runnable() {
 784                 public void run() {
 785                     JConsole jConsole = new JConsole(hotspot);
 786 
 787                     // Center the window on screen, taking into account screen
 788                     // size and insets.
 789                     Toolkit toolkit = Toolkit.getDefaultToolkit();
 790                     GraphicsConfiguration gc = jConsole.getGraphicsConfiguration();
 791                     Dimension scrSize = toolkit.getScreenSize();
 792                     Insets scrInsets  = toolkit.getScreenInsets(gc);
 793                     Rectangle scrBounds =
 794                         new Rectangle(scrInsets.left, scrInsets.top,
 795                                       scrSize.width  - scrInsets.left - scrInsets.right,
 796                                       scrSize.height - scrInsets.top  - scrInsets.bottom);
 797                     int w = Math.min(900, scrBounds.width);
 798                     int h = Math.min(750, scrBounds.height);
 799                     jConsole.setBounds(scrBounds.x + (scrBounds.width  - w) / 2,
 800                                        scrBounds.y + (scrBounds.height - h) / 2,
 801                                        w, h);
 802 
 803                     jConsole.setVisible(true);
 804                     jConsole.createMDI();
 805 
 806                     for (int i = 0; i < hostNames.size(); i++) {
 807                         jConsole.addHost(hostNames.get(i), ports.get(i),
 808                                          null, null,
 809                                          (i == hostNames.size() - 1) ?
 810                                          !noTile : false);
 811                     }
 812 
 813                     for (int i = 0; i < urls.size(); i++) {
 814                         jConsole.addUrl(urls.get(i),
 815                                         null,
 816                                         null,
 817                                         (i == urls.size() - 1) ?
 818                                         !noTile : false);
 819                     }
 820 
 821                     for (int i = 0; i < vmids.size(); i++) {
 822                         jConsole.addVmid(vmids.get(i),
 823                                         (i == vmids.size() - 1) ?
 824                                         !noTile : false);
 825                     }
 826 
 827                     if (vmids.size() == 0 &&
 828                         hostNames.size() == 0 &&
 829                         urls.size() == 0) {
 830                         jConsole.showConnectDialog(null,
 831                                                    null,
 832                                                    0,
 833                                                    null,
 834                                                    null,
 835                                                    null);
 836                     }
 837                 }
 838             });
 839     }
 840 
 841     public static void main(String[] args) {
 842         boolean noTile = false, hotspot = false;
 843         int argIndex = 0;
 844         ProxyClient proxyClient = null;
 845 
 846         if (System.getProperty("jconsole.showOutputViewer") != null) {
 847             OutputViewer.init();
 848         }
 849 
 850         while (args.length - argIndex > 0 && args[argIndex].startsWith("-")) {
 851             String arg = args[argIndex++];
 852             if (arg.equals("-h") ||
 853                 arg.equals("-help") ||
 854                 arg.equals("-?")) {
 855 
 856                 usage();
 857                 return;
 858             } else if (arg.startsWith("-interval=")) {
 859                 try {
 860                     updateInterval = Integer.parseInt(arg.substring(10)) *
 861                         1000;
 862                 } catch (NumberFormatException ex) {
 863                     usage();
 864                     return;
 865                 }
 866             } else if (arg.equals("-pluginpath")) {
 867                 if (argIndex < args.length && !args[argIndex].startsWith("-")) {
 868                     pluginPath = args[argIndex++];
 869                 } else {
 870                     // Invalid argument
 871                     usage();
 872                     return;
 873                 }
 874             } else if (arg.equals("-notile")) {
 875                 noTile = true;
 876             } else if (arg.equals("-version")) {
 877                 Version.print(System.err);
 878                 return;
 879             } else if (arg.equals("-debug")) {
 880                 debug = true;
 881             } else if (arg.equals("-fullversion")) {
 882                 Version.printFullVersion(System.err);
 883                 return;
 884             } else {
 885                 // Unknown switch
 886                 usage();
 887                 return;
 888             }
 889         }
 890 
 891         if (System.getProperty("jconsole.showUnsupported") != null) {
 892             hotspot = true;
 893         }
 894 
 895         List<String> urls = new ArrayList<String>();
 896         List<String> hostNames = new ArrayList<String>();
 897         List<Integer> ports = new ArrayList<Integer>();
 898         List<LocalVirtualMachine> vms = new ArrayList<LocalVirtualMachine>();
 899 
 900         for (int i = argIndex; i < args.length; i++) {
 901             String arg = args[i];
 902             if (isValidRemoteString(arg)) {
 903                 if (arg.startsWith(ROOT_URL)) {
 904                     urls.add(arg);
 905                 } else if (arg.matches(".*:[0-9]*")) {
 906                     int p = arg.lastIndexOf(':');
 907                     hostNames.add(arg.substring(0, p));
 908                     try {
 909                         ports.add(Integer.parseInt(arg.substring(p+1)));
 910                     } catch (NumberFormatException ex) {
 911                         usage();
 912                         return;
 913                     }
 914                 }
 915             } else {
 916                 if (!isLocalAttachAvailable()) {
 917                     System.err.println("Local process monitoring is not supported");
 918                     return;
 919                 }
 920                 try {
 921                     int vmid = Integer.parseInt(arg);
 922                     LocalVirtualMachine lvm =
 923                         LocalVirtualMachine.getLocalVirtualMachine(vmid);
 924                     if (lvm == null) {
 925                         System.err.println("Invalid process id:" + vmid);
 926                         return;
 927                     }
 928                     vms.add(lvm);
 929                 } catch (NumberFormatException ex) {
 930                     usage();
 931                     return;
 932                 }
 933             }
 934         }
 935 
 936         mainInit(urls, hostNames, ports, vms, proxyClient, noTile, hotspot);
 937     }
 938 
 939     public static boolean isDebug() {
 940         return debug;
 941     }
 942 
 943     private static void dbgStackTrace(Exception ex) {
 944         if (debug) {
 945             ex.printStackTrace();
 946         }
 947     }
 948 
 949     private static final boolean localAttachmentSupported;
 950     static {
 951         boolean supported;
 952         try {
 953             Class.forName("com.sun.tools.attach.VirtualMachine");
 954             Class.forName("sun.management.ConnectorAddressLink");
 955             supported = true;
 956         } catch (NoClassDefFoundError x) {
 957             supported = false;
 958         } catch (ClassNotFoundException x) {
 959             supported = false;
 960         }
 961         localAttachmentSupported = supported;
 962     }
 963 
 964     public static boolean isLocalAttachAvailable() {
 965         return localAttachmentSupported;
 966     }
 967 
 968 
 969     private static ServiceLoader<JConsolePlugin> pluginService = null;
 970 
 971     // Return a list of newly instantiated JConsolePlugin objects
 972     static synchronized List<JConsolePlugin> getPlugins() {
 973         if (pluginService == null) {
 974             // First time loading and initializing the plugins
 975             initPluginService(pluginPath);
 976         } else {
 977             // reload the plugin so that new instances will be created
 978             pluginService.reload();
 979         }
 980 
 981         List<JConsolePlugin> plugins = new ArrayList<JConsolePlugin>();
 982         for (JConsolePlugin p : pluginService) {
 983             plugins.add(p);
 984         }
 985         return plugins;
 986     }
 987 
 988     private static void initPluginService(String pluginPath) {
 989         if (pluginPath.length() > 0) {
 990             try {
 991                 ClassLoader pluginCL = new URLClassLoader(pathToURLs(pluginPath));
 992                 ServiceLoader<JConsolePlugin> plugins =
 993                     ServiceLoader.load(JConsolePlugin.class, pluginCL);
 994                 // validate all plugins
 995             for (JConsolePlugin p : plugins) {
 996                     if (isDebug()) {
 997                         System.out.println("Plugin " + p.getClass() + " loaded.");
 998                     }
 999                 }
1000                 pluginService = plugins;
1001             } catch (ServiceConfigurationError e) {
1002                 // Error occurs during initialization of plugin
1003                 System.out.println(Resources.format(Messages.FAIL_TO_LOAD_PLUGIN,
1004                                    e.getMessage()));
1005             } catch (MalformedURLException e) {
1006                 if (JConsole.isDebug()) {
1007                     e.printStackTrace();
1008                 }
1009                 System.out.println(Resources.format(Messages.INVALID_PLUGIN_PATH,
1010                                    e.getMessage()));
1011             }
1012         }
1013 
1014         if (pluginService == null) {
1015             initEmptyPlugin();
1016         }
1017     }
1018 
1019     private static void initEmptyPlugin() {
1020         ClassLoader pluginCL = new URLClassLoader(new URL[0]);
1021         pluginService = ServiceLoader.load(JConsolePlugin.class, pluginCL);
1022     }
1023 
1024    /**
1025      * Utility method for converting a search path string to an array
1026      * of directory and JAR file URLs.
1027      *
1028      * @param path the search path string
1029      * @return the resulting array of directory and JAR file URLs
1030      */
1031     private static URL[] pathToURLs(String path) throws MalformedURLException {
1032         String[] names = path.split(File.pathSeparator);
1033         URL[] urls = new URL[names.length];
1034         int count = 0;
1035         for (String f : names) {
1036             URL url = fileToURL(new File(f));
1037             urls[count++] = url;
1038         }
1039         return urls;
1040     }
1041 
1042     /**
1043      * Returns the directory or JAR file URL corresponding to the specified
1044      * local file name.
1045      *
1046      * @param file the File object
1047      * @return the resulting directory or JAR file URL, or null if unknown
1048      */
1049     private static URL fileToURL(File file) throws MalformedURLException {
1050         String name;
1051         try {
1052             name = file.getCanonicalPath();
1053         } catch (IOException e) {
1054             name = file.getAbsolutePath();
1055         }
1056         name = name.replace(File.separatorChar, '/');
1057         if (!name.startsWith("/")) {
1058             name = "/" + name;
1059         }
1060         // If the file does not exist, then assume that it's a directory
1061         if (!file.isFile()) {
1062             name = name + "/";
1063         }
1064         return new URL("file", "", name);
1065     }
1066 
1067 
1068     private static class FixedJRootPane extends JRootPane {
1069         public void updateUI() {
1070             updateLafValues();
1071             super.updateUI();
1072         }
1073 
1074         /**
1075          * The revalidate method seems to be the only one that gets
1076          * called whenever there is a change of L&F or change of theme
1077          * in Windows L&F and GTK L&F.
1078          */
1079         @Override
1080         public void revalidate() {
1081             // Workaround for Swing bug where the titledborder in both
1082             // GTK and Windows L&F's use calculated colors instead of
1083             // the highlight/shadow colors from the theme.
1084             //
1085             // Putting null removes any previous override and causes a
1086             // fallback to the current L&F's value.
1087             UIManager.put("TitledBorder.border", null);
1088             Border border = UIManager.getBorder("TitledBorder.border");
1089             if (border instanceof BorderUIResource.EtchedBorderUIResource) {
1090                 Color highlight = UIManager.getColor("ToolBar.highlight");
1091                 Color shadow    = UIManager.getColor("ToolBar.shadow");
1092                 border = new BorderUIResource.EtchedBorderUIResource(highlight,
1093                                                                      shadow);
1094                 UIManager.put("TitledBorder.border", border);
1095             }
1096 
1097             if (IS_GTK) {
1098                 // Workaround for Swing bug where the titledborder in
1099                 // GTK L&F use hardcoded color and font for the title
1100                 // instead of getting them from the theme.
1101                 UIManager.put("TitledBorder.titleColor",
1102                               UIManager.getColor("Label.foreground"));
1103                 UIManager.put("TitledBorder.font",
1104                               UIManager.getFont("Label.font"));
1105             }
1106             super.revalidate();
1107         }
1108     }
1109 }