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
26package sun.tools.jconsole;
27
28import java.awt.*;
29import java.awt.event.*;
30import java.beans.*;
31import java.io.*;
32import java.net.*;
33import java.util.*;
34import java.util.List;
35
36import javax.swing.*;
37import javax.swing.border.*;
38import javax.swing.event.*;
39import javax.swing.plaf.*;
40import javax.security.auth.login.FailedLoginException;
41import javax.net.ssl.SSLHandshakeException;
42
43import com.sun.tools.jconsole.JConsolePlugin;
44
45import sun.net.util.IPAddressUtil;
46
47import static sun.tools.jconsole.Utilities.*;
48
49@SuppressWarnings("serial")
50public 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    /**
953     * local attach is supported in this implementation as jdk.jconsole
954     * requires jdk.attach and jdk.management.agent
955     */
956    public static boolean isLocalAttachAvailable() {
957        return true;
958    }
959
960
961    private static ServiceLoader<JConsolePlugin> pluginService = null;
962
963    // Return a list of newly instantiated JConsolePlugin objects
964    static synchronized List<JConsolePlugin> getPlugins() {
965        if (pluginService == null) {
966            // First time loading and initializing the plugins
967            initPluginService(pluginPath);
968        } else {
969            // reload the plugin so that new instances will be created
970            pluginService.reload();
971        }
972
973        List<JConsolePlugin> plugins = new ArrayList<JConsolePlugin>();
974        for (JConsolePlugin p : pluginService) {
975            plugins.add(p);
976        }
977        return plugins;
978    }
979
980    private static void initPluginService(String pluginPath) {
981        if (pluginPath.length() > 0) {
982            try {
983                ClassLoader pluginCL = new URLClassLoader(pathToURLs(pluginPath));
984                ServiceLoader<JConsolePlugin> plugins =
985                    ServiceLoader.load(JConsolePlugin.class, pluginCL);
986                // validate all plugins
987            for (JConsolePlugin p : plugins) {
988                    if (isDebug()) {
989                        System.out.println("Plugin " + p.getClass() + " loaded.");
990                    }
991                }
992                pluginService = plugins;
993            } catch (ServiceConfigurationError e) {
994                // Error occurs during initialization of plugin
995                System.out.println(Resources.format(Messages.FAIL_TO_LOAD_PLUGIN,
996                                   e.getMessage()));
997            } catch (MalformedURLException e) {
998                if (JConsole.isDebug()) {
999                    e.printStackTrace();
1000                }
1001                System.out.println(Resources.format(Messages.INVALID_PLUGIN_PATH,
1002                                   e.getMessage()));
1003            }
1004        }
1005
1006        if (pluginService == null) {
1007            initEmptyPlugin();
1008        }
1009    }
1010
1011    private static void initEmptyPlugin() {
1012        ClassLoader pluginCL = new URLClassLoader(new URL[0]);
1013        pluginService = ServiceLoader.load(JConsolePlugin.class, pluginCL);
1014    }
1015
1016   /**
1017     * Utility method for converting a search path string to an array
1018     * of directory and JAR file URLs.
1019     *
1020     * @param path the search path string
1021     * @return the resulting array of directory and JAR file URLs
1022     */
1023    private static URL[] pathToURLs(String path) throws MalformedURLException {
1024        String[] names = path.split(File.pathSeparator);
1025        URL[] urls = new URL[names.length];
1026        int count = 0;
1027        for (String f : names) {
1028            URL url = fileToURL(new File(f));
1029            urls[count++] = url;
1030        }
1031        return urls;
1032    }
1033
1034    /**
1035     * Returns the directory or JAR file URL corresponding to the specified
1036     * local file name.
1037     *
1038     * @param file the File object
1039     * @return the resulting directory or JAR file URL, or null if unknown
1040     */
1041    private static URL fileToURL(File file) throws MalformedURLException {
1042        String name;
1043        try {
1044            name = file.getCanonicalPath();
1045        } catch (IOException e) {
1046            name = file.getAbsolutePath();
1047        }
1048        name = name.replace(File.separatorChar, '/');
1049        if (!name.startsWith("/")) {
1050            name = "/" + name;
1051        }
1052        // If the file does not exist, then assume that it's a directory
1053        if (!file.isFile()) {
1054            name = name + "/";
1055        }
1056        return new URL("file", "", name);
1057    }
1058
1059
1060    private static class FixedJRootPane extends JRootPane {
1061        public void updateUI() {
1062            updateLafValues();
1063            super.updateUI();
1064        }
1065
1066        /**
1067         * The revalidate method seems to be the only one that gets
1068         * called whenever there is a change of L&F or change of theme
1069         * in Windows L&F and GTK L&F.
1070         */
1071        @Override
1072        public void revalidate() {
1073            // Workaround for Swing bug where the titledborder in both
1074            // GTK and Windows L&F's use calculated colors instead of
1075            // the highlight/shadow colors from the theme.
1076            //
1077            // Putting null removes any previous override and causes a
1078            // fallback to the current L&F's value.
1079            UIManager.put("TitledBorder.border", null);
1080            Border border = UIManager.getBorder("TitledBorder.border");
1081            if (border instanceof BorderUIResource.EtchedBorderUIResource) {
1082                Color highlight = UIManager.getColor("ToolBar.highlight");
1083                Color shadow    = UIManager.getColor("ToolBar.shadow");
1084                border = new BorderUIResource.EtchedBorderUIResource(highlight,
1085                                                                     shadow);
1086                UIManager.put("TitledBorder.border", border);
1087            }
1088
1089            if (IS_GTK) {
1090                // Workaround for Swing bug where the titledborder in
1091                // GTK L&F use hardcoded color and font for the title
1092                // instead of getting them from the theme.
1093                UIManager.put("TitledBorder.titleColor",
1094                              UIManager.getColor("Label.foreground"));
1095                UIManager.put("TitledBorder.font",
1096                              UIManager.getFont("Label.font"));
1097            }
1098            super.revalidate();
1099        }
1100    }
1101}
1102