src/jdk.jconsole/share/classes/sun/tools/jconsole/JConsole.java
changeset 47216 71c04702a3d5
parent 43726 7e7515c012c9
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jconsole/share/classes/sun/tools/jconsole/JConsole.java	Tue Sep 12 19:03:39 2017 +0200
@@ -0,0 +1,1101 @@
+/*
+ * Copyright (c) 2004, 2013, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package sun.tools.jconsole;
+
+import java.awt.*;
+import java.awt.event.*;
+import java.beans.*;
+import java.io.*;
+import java.net.*;
+import java.util.*;
+import java.util.List;
+
+import javax.swing.*;
+import javax.swing.border.*;
+import javax.swing.event.*;
+import javax.swing.plaf.*;
+import javax.security.auth.login.FailedLoginException;
+import javax.net.ssl.SSLHandshakeException;
+
+import com.sun.tools.jconsole.JConsolePlugin;
+
+import sun.net.util.IPAddressUtil;
+
+import static sun.tools.jconsole.Utilities.*;
+
+@SuppressWarnings("serial")
+public class JConsole extends JFrame
+    implements ActionListener, InternalFrameListener {
+
+    static /*final*/ boolean IS_GTK;
+    static /*final*/ boolean IS_WIN;
+
+    static {
+        // Apply the system L&F if it is GTK or Windows, and
+        // the L&F is not specified using a system property.
+        if (System.getProperty("swing.defaultlaf") == null) {
+            String systemLaF = UIManager.getSystemLookAndFeelClassName();
+            if (systemLaF.equals("com.sun.java.swing.plaf.gtk.GTKLookAndFeel") ||
+                systemLaF.equals("com.sun.java.swing.plaf.windows.WindowsLookAndFeel")) {
+
+                try {
+                    UIManager.setLookAndFeel(systemLaF);
+                } catch (Exception e) {
+                    System.err.println(Resources.format(Messages.JCONSOLE_COLON_, e.getMessage()));
+                }
+            }
+        }
+
+        updateLafValues();
+    }
+
+
+    static void updateLafValues() {
+        String lafName = UIManager.getLookAndFeel().getClass().getName();
+        IS_GTK = lafName.equals("com.sun.java.swing.plaf.gtk.GTKLookAndFeel");
+        IS_WIN = lafName.equals("com.sun.java.swing.plaf.windows.WindowsLookAndFeel");
+
+        //BorderedComponent.updateLafValues();
+    }
+
+
+    private final static String title =
+        Messages.JAVA_MONITORING___MANAGEMENT_CONSOLE;
+    public final static String ROOT_URL =
+        "service:jmx:";
+
+    private static int updateInterval = 4000;
+    private static String pluginPath = "";
+
+    private JMenuBar menuBar;
+    private JMenuItem hotspotMI, connectMI, exitMI;
+    private WindowMenu windowMenu;
+    private JMenuItem tileMI, cascadeMI, minimizeAllMI, restoreAllMI;
+    private JMenuItem userGuideMI, aboutMI;
+
+    private JButton connectButton;
+    private JDesktopPane desktop;
+    private ConnectDialog connectDialog;
+    private CreateMBeanDialog createDialog;
+
+    private ArrayList<VMInternalFrame> windows =
+        new ArrayList<VMInternalFrame>();
+
+    private int frameLoc = 5;
+    static boolean debug;
+
+    public JConsole(boolean hotspot) {
+        super(title);
+
+        setRootPane(new FixedJRootPane());
+        setAccessibleDescription(this,
+                                 Messages.JCONSOLE_ACCESSIBLE_DESCRIPTION);
+        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
+
+        menuBar = new JMenuBar();
+        setJMenuBar(menuBar);
+
+        // TODO: Use Actions !
+
+        JMenu connectionMenu = new JMenu(Messages.CONNECTION);
+        connectionMenu.setMnemonic(Resources.getMnemonicInt(Messages.CONNECTION));
+        menuBar.add(connectionMenu);
+        if(hotspot) {
+            hotspotMI = new JMenuItem(Messages.HOTSPOT_MBEANS_ELLIPSIS);
+            hotspotMI.setMnemonic(Resources.getMnemonicInt(Messages.HOTSPOT_MBEANS_ELLIPSIS));
+            hotspotMI.setAccelerator(KeyStroke.
+                                     getKeyStroke(KeyEvent.VK_H,
+                                                  InputEvent.CTRL_DOWN_MASK));
+            hotspotMI.addActionListener(this);
+            connectionMenu.add(hotspotMI);
+
+            connectionMenu.addSeparator();
+        }
+
+        connectMI = new JMenuItem(Messages.NEW_CONNECTION_ELLIPSIS);
+        connectMI.setMnemonic(Resources.getMnemonicInt(Messages.NEW_CONNECTION_ELLIPSIS));
+        connectMI.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_N,
+                                                        InputEvent.CTRL_DOWN_MASK));
+        connectMI.addActionListener(this);
+        connectionMenu.add(connectMI);
+
+        connectionMenu.addSeparator();
+
+        exitMI = new JMenuItem(Messages.EXIT);
+        exitMI.setMnemonic(Resources.getMnemonicInt(Messages.EXIT));
+        exitMI.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_F4,
+                                                     InputEvent.ALT_DOWN_MASK));
+        exitMI.addActionListener(this);
+        connectionMenu.add(exitMI);
+
+
+        JMenu helpMenu = new JMenu(Messages.HELP_MENU_TITLE);
+        helpMenu.setMnemonic(Resources.getMnemonicInt(Messages.HELP_MENU_TITLE));
+        menuBar.add(helpMenu);
+
+        if (AboutDialog.isBrowseSupported()) {
+            userGuideMI = new JMenuItem(Messages.HELP_MENU_USER_GUIDE_TITLE);
+            userGuideMI.setMnemonic(Resources.getMnemonicInt(Messages.HELP_MENU_USER_GUIDE_TITLE));
+            userGuideMI.addActionListener(this);
+            helpMenu.add(userGuideMI);
+            helpMenu.addSeparator();
+        }
+        aboutMI = new JMenuItem(Messages.HELP_MENU_ABOUT_TITLE);
+        aboutMI.setMnemonic(Resources.getMnemonicInt(Messages.HELP_MENU_ABOUT_TITLE));
+        aboutMI.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_F1, 0));
+        aboutMI.addActionListener(this);
+        helpMenu.add(aboutMI);
+    }
+
+    public JDesktopPane getDesktopPane() {
+        return desktop;
+    }
+
+    public List<VMInternalFrame> getInternalFrames() {
+        return windows;
+    }
+
+    private void createMDI() {
+        // Restore title - we now show connection name on internal frames
+        setTitle(title);
+
+        Container cp = getContentPane();
+        Component oldCenter =
+            ((BorderLayout)cp.getLayout()).
+            getLayoutComponent(BorderLayout.CENTER);
+
+        windowMenu = new WindowMenu(Messages.WINDOW);
+        windowMenu.setMnemonic(Resources.getMnemonicInt(Messages.WINDOW));
+        // Add Window menu before Help menu
+        menuBar.add(windowMenu, menuBar.getComponentCount() - 1);
+
+        desktop = new JDesktopPane();
+        desktop.setBackground(new Color(235, 245, 255));
+
+        cp.add(desktop, BorderLayout.CENTER);
+
+        if (oldCenter instanceof VMPanel) {
+            addFrame((VMPanel)oldCenter);
+        }
+    }
+
+    private class WindowMenu extends JMenu {
+        VMInternalFrame[] windowMenuWindows = new VMInternalFrame[0];
+        int separatorPosition;
+
+        // The width value of viewR is used to truncate long menu items.
+        // The rest are placeholders and are ignored for this purpose.
+        Rectangle viewR = new Rectangle(0, 0, 400, 20);
+        Rectangle textR = new Rectangle(0, 0, 0, 0);
+        Rectangle iconR = new Rectangle(0, 0, 0, 0);
+
+        WindowMenu(String text) {
+            super(text);
+
+            cascadeMI = new JMenuItem(Messages.CASCADE);
+            cascadeMI.setMnemonic(Resources.getMnemonicInt(Messages.CASCADE));
+            cascadeMI.addActionListener(JConsole.this);
+            add(cascadeMI);
+
+            tileMI = new JMenuItem(Messages.TILE);
+            tileMI.setMnemonic(Resources.getMnemonicInt(Messages.TILE));
+            tileMI.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_T,
+                                                         InputEvent.CTRL_DOWN_MASK));
+            tileMI.addActionListener(JConsole.this);
+            add(tileMI);
+
+            minimizeAllMI = new JMenuItem(Messages.MINIMIZE_ALL);
+            minimizeAllMI.setMnemonic(Resources.getMnemonicInt(Messages.MINIMIZE_ALL));
+            minimizeAllMI.addActionListener(JConsole.this);
+            add(minimizeAllMI);
+
+            restoreAllMI = new JMenuItem(Messages.RESTORE_ALL);
+            restoreAllMI.setMnemonic(Resources.getMnemonicInt(Messages.RESTORE_ALL));
+            restoreAllMI.addActionListener(JConsole.this);
+            add(restoreAllMI);
+
+            separatorPosition = getMenuComponentCount();
+        }
+
+        private void add(VMInternalFrame vmIF) {
+            if (separatorPosition == getMenuComponentCount()) {
+                addSeparator();
+            }
+
+            int index = -1;
+            int position = separatorPosition + 1;
+            int n = windowMenuWindows.length;
+
+            for (int i = 0; i < n; i++) {
+                if (windowMenuWindows[i] != null) {
+                    // Slot is in use, try next
+                    position++;
+                } else {
+                    // Found a free slot
+                    index = i;
+                    break;
+                }
+            }
+
+            if (index == -1) {
+                // Create a slot at the end
+                VMInternalFrame[] newArray = new VMInternalFrame[n + 1];
+                System.arraycopy(windowMenuWindows, 0, newArray, 0, n);
+                windowMenuWindows = newArray;
+                index = n;
+            }
+
+            windowMenuWindows[index] = vmIF;
+
+            String indexString = "" + (index+1);
+            String vmName = vmIF.getVMPanel().getDisplayName();
+            // Maybe truncate menu item string and end with "..."
+            String text =
+                SwingUtilities.layoutCompoundLabel(this,
+                                        getGraphics().getFontMetrics(getFont()),
+                                        indexString +  " " + vmName,
+                                        null, 0, 0, 0, 0,
+                                        viewR, iconR, textR, 0);
+            JMenuItem mi = new JMenuItem(text);
+            if (text.endsWith("...")) {
+                mi.setToolTipText(vmName);
+            }
+
+            // Set mnemonic using last digit of number
+            int nDigits = indexString.length();
+            mi.setMnemonic(indexString.charAt(nDigits-1));
+            mi.setDisplayedMnemonicIndex(nDigits-1);
+
+            mi.putClientProperty("JConsole.vmIF", vmIF);
+            mi.addActionListener(JConsole.this);
+            vmIF.putClientProperty("JConsole.menuItem", mi);
+            add(mi, position);
+        }
+
+        private void remove(VMInternalFrame vmIF) {
+            for (int i = 0; i < windowMenuWindows.length; i++) {
+                if (windowMenuWindows[i] == vmIF) {
+                    windowMenuWindows[i] = null;
+                }
+            }
+            JMenuItem mi = (JMenuItem)vmIF.getClientProperty("JConsole.menuItem");
+            remove(mi);
+            mi.putClientProperty("JConsole.vmIF", null);
+            vmIF.putClientProperty("JConsole.menuItem", null);
+
+            if (separatorPosition == getMenuComponentCount() - 1) {
+                remove(getMenuComponent(getMenuComponentCount() - 1));
+            }
+        }
+    }
+
+    public void actionPerformed(ActionEvent ev) {
+        Object src = ev.getSource();
+        if (src == hotspotMI) {
+            showCreateMBeanDialog();
+        }
+
+        if (src == connectButton || src == connectMI) {
+            VMPanel vmPanel = null;
+            JInternalFrame vmIF = desktop.getSelectedFrame();
+            if (vmIF instanceof VMInternalFrame) {
+                vmPanel = ((VMInternalFrame)vmIF).getVMPanel();
+            }
+                String hostName = "";
+                String url = "";
+                if (vmPanel != null) {
+                    hostName = vmPanel.getHostName();
+                    if(vmPanel.getUrl() != null)
+                        url = vmPanel.getUrl();
+                }
+                showConnectDialog(url, hostName, 0, null, null, null);
+        } else if (src == tileMI) {
+            tileWindows();
+        } else if (src == cascadeMI) {
+            cascadeWindows();
+        } else if (src == minimizeAllMI) {
+            for (VMInternalFrame vmIF : windows) {
+                try {
+                    vmIF.setIcon(true);
+                } catch (PropertyVetoException ex) {
+                    // Ignore
+                }
+            }
+        } else if (src == restoreAllMI) {
+            for (VMInternalFrame vmIF : windows) {
+                try {
+                    vmIF.setIcon(false);
+                } catch (PropertyVetoException ex) {
+                    // Ignore
+                }
+            }
+        } else if (src == exitMI) {
+            System.exit(0);
+        } else if (src == userGuideMI) {
+            AboutDialog.browseUserGuide(this);
+        } else if (src == aboutMI) {
+            AboutDialog.showAboutDialog(this);
+        } else if (src instanceof JMenuItem) {
+            JMenuItem mi = (JMenuItem)src;
+            VMInternalFrame vmIF = (VMInternalFrame)mi.
+                getClientProperty("JConsole.vmIF");
+            if (vmIF != null) {
+                try {
+                    vmIF.setIcon(false);
+                    vmIF.setSelected(true);
+                } catch (PropertyVetoException ex) {
+                    // Ignore
+                }
+                vmIF.moveToFront();
+            }
+        }
+    }
+
+
+    public void tileWindows() {
+        int w = -1;
+        int h = -1;
+        int n = 0;
+        for (VMInternalFrame vmIF : windows) {
+            if (!vmIF.isIcon()) {
+                n++;
+                if (w == -1) {
+                    try {
+                        vmIF.setMaximum(true);
+                        w = vmIF.getWidth();
+                        h = vmIF.getHeight();
+                    } catch (PropertyVetoException ex) {
+                        // Ignore
+                    }
+                }
+            }
+        }
+        if (n > 0 && w > 0 && h > 0) {
+            int rows = (int)Math.ceil(Math.sqrt(n));
+            int cols = n / rows;
+            if (rows * cols < n) cols++;
+            int x = 0;
+            int y = 0;
+            w /= cols;
+            h /= rows;
+            int col = 0;
+            for (VMInternalFrame vmIF : windows) {
+                if (!vmIF.isIcon()) {
+                    try {
+                        vmIF.setMaximum(n==1);
+                    } catch (PropertyVetoException ex) {
+                        // Ignore
+                    }
+                    if (n > 1) {
+                        vmIF.setBounds(x, y, w, h);
+                    }
+                    if (col < cols-1) {
+                        col++;
+                        x += w;
+                    } else {
+                        col = 0;
+                        x = 0;
+                        y += h;
+                    }
+                }
+            }
+        }
+    }
+
+    public void cascadeWindows() {
+        int n = 0;
+        int w = -1;
+        int h = -1;
+        for (VMInternalFrame vmIF : windows) {
+            if (!vmIF.isIcon()) {
+                try {
+                    vmIF.setMaximum(false);
+                } catch (PropertyVetoException ex) {
+                    // Ignore
+                }
+                n++;
+                vmIF.pack();
+                if (w == -1) {
+                    try {
+                        w = vmIF.getWidth();
+                        h = vmIF.getHeight();
+                        vmIF.setMaximum(true);
+                        w = vmIF.getWidth() - w;
+                        h = vmIF.getHeight() - h;
+                        vmIF.pack();
+                    } catch (PropertyVetoException ex) {
+                        // Ignore
+                    }
+                }
+            }
+        }
+        int x = 0;
+        int y = 0;
+        int dX = (n > 1) ? (w / (n - 1)) : 0;
+        int dY = (n > 1) ? (h / (n - 1)) : 0;
+        for (VMInternalFrame vmIF : windows) {
+            if (!vmIF.isIcon()) {
+                vmIF.setLocation(x, y);
+                vmIF.moveToFront();
+                x += dX;
+                y += dY;
+            }
+        }
+    }
+
+    // Call on EDT
+    void addHost(String hostName, int port,
+                 String userName, String password) {
+        addHost(hostName, port, userName, password, false);
+    }
+
+    // Call on EDT
+    void addVmid(LocalVirtualMachine lvm) {
+        addVmid(lvm, false);
+    }
+
+    // Call on EDT
+    void addVmid(final LocalVirtualMachine lvm, final boolean tile) {
+        new Thread("JConsole.addVmid") {
+            public void run() {
+                try {
+                    addProxyClient(ProxyClient.getProxyClient(lvm), tile);
+                } catch (final SecurityException ex) {
+                    failed(ex, null, null, null);
+                } catch (final IOException ex) {
+                    failed(ex, null, null, null);
+                }
+            }
+        }.start();
+    }
+
+    // Call on EDT
+    void addUrl(final String url,
+                final String userName,
+                final String password,
+                final boolean tile) {
+        new Thread("JConsole.addUrl") {
+            public void run() {
+                try {
+                    addProxyClient(ProxyClient.getProxyClient(url, userName, password),
+                                   tile);
+                } catch (final MalformedURLException ex) {
+                    failed(ex, url, userName, password);
+                } catch (final SecurityException ex) {
+                    failed(ex, url, userName, password);
+                } catch (final IOException ex) {
+                    failed(ex, url, userName, password);
+                }
+            }
+        }.start();
+    }
+
+
+    // Call on EDT
+    void addHost(final String hostName, final int port,
+                 final String userName, final String password,
+                 final boolean tile) {
+        new Thread("JConsole.addHost") {
+            public void run() {
+                try {
+                    addProxyClient(ProxyClient.getProxyClient(hostName, port,
+                                                              userName, password),
+                                   tile);
+                } catch (final IOException ex) {
+                    dbgStackTrace(ex);
+                    SwingUtilities.invokeLater(new Runnable() {
+                        public void run() {
+                            showConnectDialog(null, hostName, port,
+                                              userName, password, errorMessage(ex));
+                        }
+                    });
+                }
+            }
+        }.start();
+    }
+
+
+    // Call on worker thread
+    void addProxyClient(final ProxyClient proxyClient, final boolean tile) {
+        SwingUtilities.invokeLater(new Runnable() {
+            public void run() {
+                VMPanel vmPanel = new VMPanel(proxyClient, updateInterval);
+                addFrame(vmPanel);
+
+                if (tile) {
+                    SwingUtilities.invokeLater(new Runnable() {
+                        public void run() {
+                            tileWindows();
+                        }
+                    });
+                }
+                vmPanel.connect();
+            }
+        });
+    }
+
+
+    // Call on worker thread
+    private void failed(final Exception ex,
+                        final String url,
+                        final String userName,
+                        final String password) {
+        SwingUtilities.invokeLater(new Runnable() {
+            public void run() {
+                dbgStackTrace(ex);
+                showConnectDialog(url,
+                                  null,
+                                  -1,
+                                  userName,
+                                  password,
+                                  errorMessage(ex));
+            }
+        });
+    }
+
+
+    private VMInternalFrame addFrame(VMPanel vmPanel) {
+        final VMInternalFrame vmIF = new VMInternalFrame(vmPanel);
+
+        for (VMInternalFrame f : windows) {
+            try {
+                f.setMaximum(false);
+            } catch (PropertyVetoException ex) {
+                // Ignore
+            }
+        }
+        desktop.add(vmIF);
+
+        vmIF.setLocation(frameLoc, frameLoc);
+        frameLoc += 30;
+        vmIF.setVisible(true);
+        windows.add(vmIF);
+        if (windows.size() == 1) {
+            try {
+                vmIF.setMaximum(true);
+            } catch (PropertyVetoException ex) {
+                // Ignore
+            }
+        }
+        vmIF.addInternalFrameListener(this);
+        windowMenu.add(vmIF);
+
+        return vmIF;
+    }
+
+    private void showConnectDialog(String url,
+                                   String hostName,
+                                   int port,
+                                   String userName,
+                                   String password,
+                                   String msg) {
+        if (connectDialog == null) {
+            connectDialog = new ConnectDialog(this);
+        }
+        connectDialog.setConnectionParameters(url,
+                                              hostName,
+                                              port,
+                                              userName,
+                                              password,
+                                              msg);
+
+        connectDialog.refresh();
+        connectDialog.setVisible(true);
+        try {
+            // Bring to front of other dialogs
+            connectDialog.setSelected(true);
+        } catch (PropertyVetoException e) {
+        }
+    }
+
+    private void showCreateMBeanDialog() {
+        if (createDialog == null) {
+            createDialog = new CreateMBeanDialog(this);
+        }
+        createDialog.setVisible(true);
+        try {
+            // Bring to front of other dialogs
+            createDialog.setSelected(true);
+        } catch (PropertyVetoException e) {
+        }
+    }
+
+    private void removeVMInternalFrame(VMInternalFrame vmIF) {
+        windowMenu.remove(vmIF);
+        desktop.remove(vmIF);
+        desktop.repaint();
+        vmIF.getVMPanel().cleanUp();
+        vmIF.dispose();
+    }
+
+    private boolean isProxyClientUsed(ProxyClient client) {
+        for(VMInternalFrame frame : windows) {
+            ProxyClient cli = frame.getVMPanel().getProxyClient(false);
+            if(client == cli)
+                return true;
+        }
+        return false;
+    }
+
+    static boolean isValidRemoteString(String txt) {
+        boolean valid = false;
+        if (txt != null) {
+            txt = txt.trim();
+            if (txt.startsWith(ROOT_URL)) {
+                if (txt.length() > ROOT_URL.length()) {
+                    valid = true;
+                }
+            } else {
+                //---------------------------------------
+                // Supported host and port combinations:
+                //     hostname:port
+                //     IPv4Address:port
+                //     [IPv6Address]:port
+                //---------------------------------------
+
+                // Is literal IPv6 address?
+                //
+                if (txt.startsWith("[")) {
+                    int index = txt.indexOf("]:");
+                    if (index != -1) {
+                        // Extract literal IPv6 address
+                        //
+                        String address = txt.substring(1, index);
+                        if (IPAddressUtil.isIPv6LiteralAddress(address)) {
+                            // Extract port
+                            //
+                            try {
+                                String portStr = txt.substring(index + 2);
+                                int port = Integer.parseInt(portStr);
+                                if (port >= 0 && port <= 0xFFFF) {
+                                    valid = true;
+                                }
+                            } catch (NumberFormatException ex) {
+                                valid = false;
+                            }
+                        }
+                    }
+                } else {
+                    String[] s = txt.split(":");
+                    if (s.length == 2) {
+                        try {
+                            int port = Integer.parseInt(s[1]);
+                            if (port >= 0 && port <= 0xFFFF) {
+                                valid = true;
+                            }
+                        } catch (NumberFormatException ex) {
+                            valid = false;
+                        }
+                    }
+                }
+            }
+        }
+        return valid;
+    }
+
+    private String errorMessage(Exception ex) {
+       String msg = Messages.CONNECTION_FAILED;
+       if (ex instanceof IOException || ex instanceof SecurityException) {
+           Throwable cause = null;
+           Throwable c = ex.getCause();
+           while (c != null) {
+               cause = c;
+               c = c.getCause();
+           }
+           if (cause instanceof ConnectException) {
+               return msg + ": " + cause.getMessage();
+           } else if (cause instanceof UnknownHostException) {
+               return Resources.format(Messages.UNKNOWN_HOST, cause.getMessage());
+           } else if (cause instanceof NoRouteToHostException) {
+               return msg + ": " + cause.getMessage();
+           } else if (cause instanceof FailedLoginException) {
+               return msg + ": " + cause.getMessage();
+           } else if (cause instanceof SSLHandshakeException) {
+               return msg + ": "+ cause.getMessage();
+           }
+        } else if (ex instanceof MalformedURLException) {
+           return Resources.format(Messages.INVALID_URL, ex.getMessage());
+        }
+        return msg + ": " + ex.getMessage();
+    }
+
+
+    // InternalFrameListener interface
+
+    public void internalFrameClosing(InternalFrameEvent e) {
+        VMInternalFrame vmIF = (VMInternalFrame)e.getInternalFrame();
+        removeVMInternalFrame(vmIF);
+        windows.remove(vmIF);
+        ProxyClient client = vmIF.getVMPanel().getProxyClient(false);
+        if(!isProxyClientUsed(client))
+            client.markAsDead();
+        if (windows.size() == 0) {
+            showConnectDialog("", "", 0, null, null, null);
+        }
+    }
+
+    public void internalFrameOpened(InternalFrameEvent e) {}
+    public void internalFrameClosed(InternalFrameEvent e) {}
+    public void internalFrameIconified(InternalFrameEvent e) {}
+    public void internalFrameDeiconified(InternalFrameEvent e) {}
+    public void internalFrameActivated(InternalFrameEvent e) {}
+    public void internalFrameDeactivated(InternalFrameEvent e) {}
+
+
+    private static void usage() {
+        System.err.println(Resources.format(Messages.ZZ_USAGE_TEXT, "jconsole"));
+    }
+
+    private static void mainInit(final List<String> urls,
+                                 final List<String> hostNames,
+                                 final List<Integer> ports,
+                                 final List<LocalVirtualMachine> vmids,
+                                 final ProxyClient proxyClient,
+                                 final boolean noTile,
+                                 final boolean hotspot) {
+
+
+        // Always create Swing GUI on the Event Dispatching Thread
+        SwingUtilities.invokeLater(new Runnable() {
+                public void run() {
+                    JConsole jConsole = new JConsole(hotspot);
+
+                    // Center the window on screen, taking into account screen
+                    // size and insets.
+                    Toolkit toolkit = Toolkit.getDefaultToolkit();
+                    GraphicsConfiguration gc = jConsole.getGraphicsConfiguration();
+                    Dimension scrSize = toolkit.getScreenSize();
+                    Insets scrInsets  = toolkit.getScreenInsets(gc);
+                    Rectangle scrBounds =
+                        new Rectangle(scrInsets.left, scrInsets.top,
+                                      scrSize.width  - scrInsets.left - scrInsets.right,
+                                      scrSize.height - scrInsets.top  - scrInsets.bottom);
+                    int w = Math.min(900, scrBounds.width);
+                    int h = Math.min(750, scrBounds.height);
+                    jConsole.setBounds(scrBounds.x + (scrBounds.width  - w) / 2,
+                                       scrBounds.y + (scrBounds.height - h) / 2,
+                                       w, h);
+
+                    jConsole.setVisible(true);
+                    jConsole.createMDI();
+
+                    for (int i = 0; i < hostNames.size(); i++) {
+                        jConsole.addHost(hostNames.get(i), ports.get(i),
+                                         null, null,
+                                         (i == hostNames.size() - 1) ?
+                                         !noTile : false);
+                    }
+
+                    for (int i = 0; i < urls.size(); i++) {
+                        jConsole.addUrl(urls.get(i),
+                                        null,
+                                        null,
+                                        (i == urls.size() - 1) ?
+                                        !noTile : false);
+                    }
+
+                    for (int i = 0; i < vmids.size(); i++) {
+                        jConsole.addVmid(vmids.get(i),
+                                        (i == vmids.size() - 1) ?
+                                        !noTile : false);
+                    }
+
+                    if (vmids.size() == 0 &&
+                        hostNames.size() == 0 &&
+                        urls.size() == 0) {
+                        jConsole.showConnectDialog(null,
+                                                   null,
+                                                   0,
+                                                   null,
+                                                   null,
+                                                   null);
+                    }
+                }
+            });
+    }
+
+    public static void main(String[] args) {
+        boolean noTile = false, hotspot = false;
+        int argIndex = 0;
+        ProxyClient proxyClient = null;
+
+        if (System.getProperty("jconsole.showOutputViewer") != null) {
+            OutputViewer.init();
+        }
+
+        while (args.length - argIndex > 0 && args[argIndex].startsWith("-")) {
+            String arg = args[argIndex++];
+            if (arg.equals("-h") ||
+                arg.equals("-help") ||
+                arg.equals("-?")) {
+
+                usage();
+                return;
+            } else if (arg.startsWith("-interval=")) {
+                try {
+                    updateInterval = Integer.parseInt(arg.substring(10)) *
+                        1000;
+                    if (updateInterval <= 0) {
+                        usage();
+                        return;
+                    }
+                } catch (NumberFormatException ex) {
+                    usage();
+                    return;
+                }
+            } else if (arg.equals("-pluginpath")) {
+                if (argIndex < args.length && !args[argIndex].startsWith("-")) {
+                    pluginPath = args[argIndex++];
+                } else {
+                    // Invalid argument
+                    usage();
+                    return;
+                }
+            } else if (arg.equals("-notile")) {
+                noTile = true;
+            } else if (arg.equals("-version")) {
+                Version.print(System.err);
+                return;
+            } else if (arg.equals("-debug")) {
+                debug = true;
+            } else if (arg.equals("-fullversion")) {
+                Version.printFullVersion(System.err);
+                return;
+            } else {
+                // Unknown switch
+                usage();
+                return;
+            }
+        }
+
+        if (System.getProperty("jconsole.showUnsupported") != null) {
+            hotspot = true;
+        }
+
+        List<String> urls = new ArrayList<String>();
+        List<String> hostNames = new ArrayList<String>();
+        List<Integer> ports = new ArrayList<Integer>();
+        List<LocalVirtualMachine> vms = new ArrayList<LocalVirtualMachine>();
+
+        for (int i = argIndex; i < args.length; i++) {
+            String arg = args[i];
+            if (isValidRemoteString(arg)) {
+                if (arg.startsWith(ROOT_URL)) {
+                    urls.add(arg);
+                } else if (arg.matches(".*:[0-9]*")) {
+                    int p = arg.lastIndexOf(':');
+                    hostNames.add(arg.substring(0, p));
+                    try {
+                        ports.add(Integer.parseInt(arg.substring(p+1)));
+                    } catch (NumberFormatException ex) {
+                        usage();
+                        return;
+                    }
+                }
+            } else {
+                if (!isLocalAttachAvailable()) {
+                    System.err.println("Local process monitoring is not supported");
+                    return;
+                }
+                try {
+                    int vmid = Integer.parseInt(arg);
+                    LocalVirtualMachine lvm =
+                        LocalVirtualMachine.getLocalVirtualMachine(vmid);
+                    if (lvm == null) {
+                        System.err.println("Invalid process id:" + vmid);
+                        return;
+                    }
+                    vms.add(lvm);
+                } catch (NumberFormatException ex) {
+                    usage();
+                    return;
+                }
+            }
+        }
+
+        mainInit(urls, hostNames, ports, vms, proxyClient, noTile, hotspot);
+    }
+
+    public static boolean isDebug() {
+        return debug;
+    }
+
+    private static void dbgStackTrace(Exception ex) {
+        if (debug) {
+            ex.printStackTrace();
+        }
+    }
+
+    /**
+     * local attach is supported in this implementation as jdk.jconsole
+     * requires jdk.attach and jdk.management.agent
+     */
+    public static boolean isLocalAttachAvailable() {
+        return true;
+    }
+
+
+    private static ServiceLoader<JConsolePlugin> pluginService = null;
+
+    // Return a list of newly instantiated JConsolePlugin objects
+    static synchronized List<JConsolePlugin> getPlugins() {
+        if (pluginService == null) {
+            // First time loading and initializing the plugins
+            initPluginService(pluginPath);
+        } else {
+            // reload the plugin so that new instances will be created
+            pluginService.reload();
+        }
+
+        List<JConsolePlugin> plugins = new ArrayList<JConsolePlugin>();
+        for (JConsolePlugin p : pluginService) {
+            plugins.add(p);
+        }
+        return plugins;
+    }
+
+    private static void initPluginService(String pluginPath) {
+        if (pluginPath.length() > 0) {
+            try {
+                ClassLoader pluginCL = new URLClassLoader(pathToURLs(pluginPath));
+                ServiceLoader<JConsolePlugin> plugins =
+                    ServiceLoader.load(JConsolePlugin.class, pluginCL);
+                // validate all plugins
+            for (JConsolePlugin p : plugins) {
+                    if (isDebug()) {
+                        System.out.println("Plugin " + p.getClass() + " loaded.");
+                    }
+                }
+                pluginService = plugins;
+            } catch (ServiceConfigurationError e) {
+                // Error occurs during initialization of plugin
+                System.out.println(Resources.format(Messages.FAIL_TO_LOAD_PLUGIN,
+                                   e.getMessage()));
+            } catch (MalformedURLException e) {
+                if (JConsole.isDebug()) {
+                    e.printStackTrace();
+                }
+                System.out.println(Resources.format(Messages.INVALID_PLUGIN_PATH,
+                                   e.getMessage()));
+            }
+        }
+
+        if (pluginService == null) {
+            initEmptyPlugin();
+        }
+    }
+
+    private static void initEmptyPlugin() {
+        ClassLoader pluginCL = new URLClassLoader(new URL[0]);
+        pluginService = ServiceLoader.load(JConsolePlugin.class, pluginCL);
+    }
+
+   /**
+     * Utility method for converting a search path string to an array
+     * of directory and JAR file URLs.
+     *
+     * @param path the search path string
+     * @return the resulting array of directory and JAR file URLs
+     */
+    private static URL[] pathToURLs(String path) throws MalformedURLException {
+        String[] names = path.split(File.pathSeparator);
+        URL[] urls = new URL[names.length];
+        int count = 0;
+        for (String f : names) {
+            URL url = fileToURL(new File(f));
+            urls[count++] = url;
+        }
+        return urls;
+    }
+
+    /**
+     * Returns the directory or JAR file URL corresponding to the specified
+     * local file name.
+     *
+     * @param file the File object
+     * @return the resulting directory or JAR file URL, or null if unknown
+     */
+    private static URL fileToURL(File file) throws MalformedURLException {
+        String name;
+        try {
+            name = file.getCanonicalPath();
+        } catch (IOException e) {
+            name = file.getAbsolutePath();
+        }
+        name = name.replace(File.separatorChar, '/');
+        if (!name.startsWith("/")) {
+            name = "/" + name;
+        }
+        // If the file does not exist, then assume that it's a directory
+        if (!file.isFile()) {
+            name = name + "/";
+        }
+        return new URL("file", "", name);
+    }
+
+
+    private static class FixedJRootPane extends JRootPane {
+        public void updateUI() {
+            updateLafValues();
+            super.updateUI();
+        }
+
+        /**
+         * The revalidate method seems to be the only one that gets
+         * called whenever there is a change of L&F or change of theme
+         * in Windows L&F and GTK L&F.
+         */
+        @Override
+        public void revalidate() {
+            // Workaround for Swing bug where the titledborder in both
+            // GTK and Windows L&F's use calculated colors instead of
+            // the highlight/shadow colors from the theme.
+            //
+            // Putting null removes any previous override and causes a
+            // fallback to the current L&F's value.
+            UIManager.put("TitledBorder.border", null);
+            Border border = UIManager.getBorder("TitledBorder.border");
+            if (border instanceof BorderUIResource.EtchedBorderUIResource) {
+                Color highlight = UIManager.getColor("ToolBar.highlight");
+                Color shadow    = UIManager.getColor("ToolBar.shadow");
+                border = new BorderUIResource.EtchedBorderUIResource(highlight,
+                                                                     shadow);
+                UIManager.put("TitledBorder.border", border);
+            }
+
+            if (IS_GTK) {
+                // Workaround for Swing bug where the titledborder in
+                // GTK L&F use hardcoded color and font for the title
+                // instead of getting them from the theme.
+                UIManager.put("TitledBorder.titleColor",
+                              UIManager.getColor("Label.foreground"));
+                UIManager.put("TitledBorder.font",
+                              UIManager.getFont("Label.font"));
+            }
+            super.revalidate();
+        }
+    }
+}