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