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