1 /* 2 * Copyright (c) 2004, 2008, 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 27 /* 28 * This file has been modified by jvmtop project authors 29 */ 30 package com.jvmtop.openjdk.tools; 31 32 import static java.lang.management.ManagementFactory.*; 33 34 import java.beans.PropertyChangeEvent; 35 import java.beans.PropertyChangeListener; 36 import java.io.IOException; 37 import java.lang.management.ClassLoadingMXBean; 38 import java.lang.management.CompilationMXBean; 39 import java.lang.management.GarbageCollectorMXBean; 40 import java.lang.management.ManagementFactory; 41 import java.lang.management.MemoryMXBean; 42 import java.lang.management.OperatingSystemMXBean; 43 import java.lang.management.RuntimeMXBean; 44 import java.lang.management.ThreadMXBean; 45 import java.lang.ref.WeakReference; 46 import java.lang.reflect.InvocationHandler; 47 import java.lang.reflect.InvocationTargetException; 48 import java.lang.reflect.Method; 49 import java.lang.reflect.Proxy; 50 import java.rmi.NotBoundException; 51 import java.rmi.Remote; 52 import java.rmi.registry.LocateRegistry; 53 import java.rmi.registry.Registry; 54 import java.rmi.server.RMIClientSocketFactory; 55 import java.rmi.server.RemoteObject; 56 import java.rmi.server.RemoteObjectInvocationHandler; 57 import java.rmi.server.RemoteRef; 58 import java.util.ArrayList; 59 import java.util.Arrays; 60 import java.util.Collection; 61 import java.util.Collections; 62 import java.util.HashMap; 63 import java.util.Iterator; 64 import java.util.List; 65 import java.util.Map; 66 import java.util.Set; 67 import java.util.TreeSet; 68 69 import javax.management.Attribute; 70 import javax.management.AttributeList; 71 import javax.management.AttributeNotFoundException; 72 import javax.management.InstanceNotFoundException; 73 import javax.management.IntrospectionException; 74 import javax.management.InvalidAttributeValueException; 75 import javax.management.MBeanException; 76 import javax.management.MBeanInfo; 77 import javax.management.MBeanOperationInfo; 78 import javax.management.MBeanServerConnection; 79 import javax.management.MalformedObjectNameException; 80 import javax.management.ObjectName; 81 import javax.management.ReflectionException; 82 import javax.management.remote.JMXConnector; 83 import javax.management.remote.JMXConnectorFactory; 84 import javax.management.remote.JMXServiceURL; 85 import javax.management.remote.rmi.RMIConnector; 86 import javax.management.remote.rmi.RMIServer; 87 import javax.rmi.ssl.SslRMIClientSocketFactory; 88 89 import sun.rmi.server.UnicastRef2; 90 import sun.rmi.transport.LiveRef; 91 92 public class ProxyClient 93 { 94 95 private ConnectionState connectionState = ConnectionState.DISCONNECTED; 96 97 98 private static Map<String, ProxyClient> cache = 99 Collections.synchronizedMap(new HashMap<String, ProxyClient>()); 100 101 private volatile boolean isDead = true; 102 private String hostName = null; 103 private int port = 0; 104 private String userName = null; 105 private String password = null; 106 private boolean hasPlatformMXBeans = false; 107 private boolean hasHotSpotDiagnosticMXBean= false; 108 private boolean hasCompilationMXBean = false; 109 private boolean supportsLockUsage = false; 110 111 // REVISIT: VMPanel and other places relying using getUrl(). 112 113 // set only if it's created for local monitoring 114 private LocalVirtualMachine lvm; 115 116 // set only if it's created from a given URL via the Advanced tab 117 private String advancedUrl = null; 118 119 private JMXServiceURL jmxUrl = null; 120 private MBeanServerConnection mbsc = null; 121 private SnapshotMBeanServerConnection server = null; 122 private JMXConnector jmxc = null; 123 private RMIServer stub = null; 124 private static final SslRMIClientSocketFactory sslRMIClientSocketFactory = 125 new SslRMIClientSocketFactory(); 126 private String registryHostName = null; 127 private int registryPort = 0; 128 private boolean vmConnector = false; 129 private boolean sslRegistry = false; 130 private boolean sslStub = false; 131 final private String connectionName; 132 final private String displayName; 133 134 private ClassLoadingMXBean classLoadingMBean = null; 135 private CompilationMXBean compilationMBean = null; 136 private MemoryMXBean memoryMBean = null; 137 private OperatingSystemMXBean operatingSystemMBean = null; 138 private RuntimeMXBean runtimeMBean = null; 139 private ThreadMXBean threadMBean = null; 140 141 private java.lang.management.OperatingSystemMXBean sunOperatingSystemMXBean = null; 142 143 // private HotSpotDiagnosticMXBean hotspotDiagnosticMXBean = null; 144 145 private List<GarbageCollectorMXBean> garbageCollectorMBeans = null; 146 147 final static private String HOTSPOT_DIAGNOSTIC_MXBEAN_NAME = 148 "com.sun.management:type=HotSpotDiagnostic"; 149 ProxyClient(String hostName, int port, String userName, String password)150 private ProxyClient(String hostName, int port, 151 String userName, String password) throws IOException { 152 this.connectionName = getConnectionName(hostName, port, userName); 153 this.displayName = connectionName; 154 if (hostName.equals("localhost") && port == 0) { 155 // Monitor self 156 this.hostName = hostName; 157 this.port = port; 158 } else { 159 // Create an RMI connector client and connect it to 160 // the RMI connector server 161 final String urlPath = "/jndi/rmi://" + hostName + ":" + port + 162 "/jmxrmi"; 163 JMXServiceURL url = new JMXServiceURL("rmi", "", 0, urlPath); 164 setParameters(url, userName, password); 165 vmConnector = true; 166 registryHostName = hostName; 167 registryPort = port; 168 checkSslConfig(); 169 } 170 } 171 ProxyClient(String url, String userName, String password)172 private ProxyClient(String url, 173 String userName, String password) throws IOException { 174 this.advancedUrl = url; 175 this.connectionName = getConnectionName(url, userName); 176 this.displayName = connectionName; 177 setParameters(new JMXServiceURL(url), userName, password); 178 } 179 ProxyClient(LocalVirtualMachine lvm)180 private ProxyClient(LocalVirtualMachine lvm) throws IOException { 181 this.lvm = lvm; 182 this.connectionName = getConnectionName(lvm); 183 this.displayName = "pid: " + lvm.vmid() + " " + lvm.displayName(); 184 } 185 setParameters(JMXServiceURL url, String userName, String password)186 private void setParameters(JMXServiceURL url, String userName, String password) { 187 this.jmxUrl = url; 188 this.hostName = jmxUrl.getHost(); 189 this.port = jmxUrl.getPort(); 190 this.userName = userName; 191 this.password = password; 192 } 193 checkStub(Remote stub, Class<? extends Remote> stubClass)194 private static void checkStub(Remote stub, 195 Class<? extends Remote> stubClass) { 196 // Check remote stub is from the expected class. 197 // 198 if (stub.getClass() != stubClass) { 199 if (!Proxy.isProxyClass(stub.getClass())) { 200 throw new SecurityException( 201 "Expecting a " + stubClass.getName() + " stub!"); 202 } else { 203 InvocationHandler handler = Proxy.getInvocationHandler(stub); 204 if (handler.getClass() != RemoteObjectInvocationHandler.class) { 205 throw new SecurityException( 206 "Expecting a dynamic proxy instance with a " + 207 RemoteObjectInvocationHandler.class.getName() + 208 " invocation handler!"); 209 } else { 210 stub = (Remote) handler; 211 } 212 } 213 } 214 // Check RemoteRef in stub is from the expected class 215 // "sun.rmi.server.UnicastRef2". 216 // 217 RemoteRef ref = ((RemoteObject)stub).getRef(); 218 if (ref.getClass() != UnicastRef2.class) { 219 throw new SecurityException( 220 "Expecting a " + UnicastRef2.class.getName() + 221 " remote reference in stub!"); 222 } 223 // Check RMIClientSocketFactory in stub is from the expected class 224 // "javax.rmi.ssl.SslRMIClientSocketFactory". 225 // 226 LiveRef liveRef = ((UnicastRef2)ref).getLiveRef(); 227 RMIClientSocketFactory csf = liveRef.getClientSocketFactory(); 228 if (csf == null || csf.getClass() != SslRMIClientSocketFactory.class) { 229 throw new SecurityException( 230 "Expecting a " + SslRMIClientSocketFactory.class.getName() + 231 " RMI client socket factory in stub!"); 232 } 233 } 234 235 private static final String rmiServerImplStubClassName = 236 "javax.management.remote.rmi.RMIServerImpl_Stub"; 237 private static final Class<? extends Remote> rmiServerImplStubClass; 238 239 static { 240 // FIXME: RMIServerImpl_Stub is generated at build time 241 // after jconsole is built. We need to investigate if 242 // the Makefile can be fixed to build jconsole in the 243 // right order. As a workaround for now, we dynamically 244 // load RMIServerImpl_Stub class instead of statically 245 // referencing it. 246 Class<? extends Remote> serverStubClass = null; 247 try { 248 serverStubClass = Class.forName(rmiServerImplStubClassName).asSubclass(Remote.class); 249 } catch (ClassNotFoundException e) { 250 // should never reach here 251 throw (InternalError) new InternalError(e.getMessage()).initCause(e); 252 } 253 rmiServerImplStubClass = serverStubClass; 254 } 255 checkSslConfig()256 private void checkSslConfig() throws IOException { 257 // Get the reference to the RMI Registry and lookup RMIServer stub 258 // 259 Registry registry; 260 try { 261 registry = 262 LocateRegistry.getRegistry(registryHostName, registryPort, 263 sslRMIClientSocketFactory); 264 try { 265 stub = (RMIServer) registry.lookup("jmxrmi"); 266 } catch (NotBoundException nbe) { 267 throw (IOException) 268 new IOException(nbe.getMessage()).initCause(nbe); 269 } 270 sslRegistry = true; 271 } catch (IOException e) { 272 registry = 273 LocateRegistry.getRegistry(registryHostName, registryPort); 274 try { 275 stub = (RMIServer) registry.lookup("jmxrmi"); 276 } catch (NotBoundException nbe) { 277 throw (IOException) 278 new IOException(nbe.getMessage()).initCause(nbe); 279 } 280 sslRegistry = false; 281 } 282 // Perform the checks for secure stub 283 // 284 try { 285 checkStub(stub, rmiServerImplStubClass); 286 sslStub = true; 287 } catch (SecurityException e) { 288 sslStub = false; 289 } 290 } 291 292 /** 293 * Returns true if the underlying RMI registry is SSL-protected. 294 * 295 * @exception UnsupportedOperationException If this {@code ProxyClient} 296 * does not denote a JMX connector for a JMX VM agent. 297 */ isSslRmiRegistry()298 public boolean isSslRmiRegistry() { 299 // Check for VM connector 300 // 301 if (!isVmConnector()) { 302 throw new UnsupportedOperationException( 303 "ProxyClient.isSslRmiRegistry() is only supported if this " + 304 "ProxyClient is a JMX connector for a JMX VM agent"); 305 } 306 return sslRegistry; 307 } 308 309 /** 310 * Returns true if the retrieved RMI stub is SSL-protected. 311 * 312 * @exception UnsupportedOperationException If this {@code ProxyClient} 313 * does not denote a JMX connector for a JMX VM agent. 314 */ isSslRmiStub()315 public boolean isSslRmiStub() { 316 // Check for VM connector 317 // 318 if (!isVmConnector()) { 319 throw new UnsupportedOperationException( 320 "ProxyClient.isSslRmiStub() is only supported if this " + 321 "ProxyClient is a JMX connector for a JMX VM agent"); 322 } 323 return sslStub; 324 } 325 326 /** 327 * Returns true if this {@code ProxyClient} denotes 328 * a JMX connector for a JMX VM agent. 329 */ isVmConnector()330 public boolean isVmConnector() { 331 return vmConnector; 332 } 333 setConnectionState(ConnectionState state)334 private void setConnectionState(ConnectionState state) { 335 ConnectionState oldState = this.connectionState; 336 this.connectionState = state; 337 } 338 getConnectionState()339 public ConnectionState getConnectionState() { 340 return this.connectionState; 341 } 342 flush()343 public void flush() 344 { 345 if (server != null) { 346 server.flush(); 347 } 348 } 349 connect()350 public void connect() throws Exception 351 { 352 setConnectionState(ConnectionState.CONNECTING); 353 try { 354 tryConnect(); 355 setConnectionState(ConnectionState.CONNECTED); 356 } catch (Exception e) { 357 setConnectionState(ConnectionState.DISCONNECTED); 358 throw e; 359 } 360 } 361 tryConnect()362 private void tryConnect() throws IOException { 363 if (jmxUrl == null && "localhost".equals(hostName) && port == 0) { 364 // Monitor self 365 this.jmxc = null; 366 this.mbsc = ManagementFactory.getPlatformMBeanServer(); 367 this.server = Snapshot.newSnapshot(mbsc); 368 } else { 369 // Monitor another process 370 if (lvm != null) { 371 if (!lvm.isManageable()) { 372 lvm.startManagementAgent(); 373 if (!lvm.isManageable()) { 374 // FIXME: what to throw 375 throw new IOException(lvm + "not manageable"); 376 } 377 } 378 if (this.jmxUrl == null) { 379 this.jmxUrl = new JMXServiceURL(lvm.connectorAddress()); 380 } 381 } 382 // Need to pass in credentials ? 383 if (userName == null && password == null) { 384 if (isVmConnector()) { 385 // Check for SSL config on reconnection only 386 if (stub == null) { 387 checkSslConfig(); 388 } 389 this.jmxc = new RMIConnector(stub, null); 390 jmxc.connect(); 391 } else { 392 this.jmxc = JMXConnectorFactory.connect(jmxUrl); 393 } 394 } else { 395 Map<String, String[]> env = new HashMap<String, String[]>(); 396 env.put(JMXConnector.CREDENTIALS, 397 new String[] {userName, password}); 398 if (isVmConnector()) { 399 // Check for SSL config on reconnection only 400 if (stub == null) { 401 checkSslConfig(); 402 } 403 this.jmxc = new RMIConnector(stub, null); 404 jmxc.connect(env); 405 } else { 406 this.jmxc = JMXConnectorFactory.connect(jmxUrl, env); 407 } 408 } 409 this.mbsc = jmxc.getMBeanServerConnection(); 410 this.server = Snapshot.newSnapshot(mbsc); 411 } 412 this.isDead = false; 413 414 try { 415 ObjectName on = new ObjectName(THREAD_MXBEAN_NAME); 416 this.hasPlatformMXBeans = server.isRegistered(on); 417 this.hasHotSpotDiagnosticMXBean = 418 server.isRegistered(new ObjectName(HOTSPOT_DIAGNOSTIC_MXBEAN_NAME)); 419 // check if it has 6.0 new APIs 420 if (this.hasPlatformMXBeans) { 421 MBeanOperationInfo[] mopis = server.getMBeanInfo(on).getOperations(); 422 // look for findDeadlockedThreads operations; 423 for (MBeanOperationInfo op : mopis) { 424 if (op.getName().equals("findDeadlockedThreads")) { 425 this.supportsLockUsage = true; 426 break; 427 } 428 } 429 430 on = new ObjectName(COMPILATION_MXBEAN_NAME); 431 this.hasCompilationMXBean = server.isRegistered(on); 432 } 433 } catch (MalformedObjectNameException e) { 434 // should not reach here 435 throw new InternalError(e.getMessage()); 436 } catch (IntrospectionException e) { 437 InternalError ie = new InternalError(e.getMessage()); 438 ie.initCause(e); 439 throw ie; 440 } catch (InstanceNotFoundException e) { 441 InternalError ie = new InternalError(e.getMessage()); 442 ie.initCause(e); 443 throw ie; 444 } catch (ReflectionException e) { 445 InternalError ie = new InternalError(e.getMessage()); 446 ie.initCause(e); 447 throw ie; 448 } 449 450 if (hasPlatformMXBeans) { 451 // WORKAROUND for bug 5056632 452 // Check if the access role is correct by getting a RuntimeMXBean 453 getRuntimeMXBean(); 454 } 455 } 456 457 /** 458 * Gets a proxy client for a given local virtual machine. 459 */ getProxyClient(LocalVirtualMachine lvm)460 public static ProxyClient getProxyClient(LocalVirtualMachine lvm) 461 throws IOException { 462 final String key = getCacheKey(lvm); 463 ProxyClient proxyClient = cache.get(key); 464 if (proxyClient == null) { 465 proxyClient = new ProxyClient(lvm); 466 cache.put(key, proxyClient); 467 } 468 return proxyClient; 469 } 470 getConnectionName(LocalVirtualMachine lvm)471 public static String getConnectionName(LocalVirtualMachine lvm) { 472 return Integer.toString(lvm.vmid()); 473 } 474 getCacheKey(LocalVirtualMachine lvm)475 private static String getCacheKey(LocalVirtualMachine lvm) { 476 return Integer.toString(lvm.vmid()); 477 } 478 479 /** 480 * Gets a proxy client for a given JMXServiceURL. 481 */ getProxyClient(String url, String userName, String password)482 public static ProxyClient getProxyClient(String url, 483 String userName, String password) 484 throws IOException { 485 final String key = getCacheKey(url, userName, password); 486 ProxyClient proxyClient = cache.get(key); 487 if (proxyClient == null) { 488 proxyClient = new ProxyClient(url, userName, password); 489 cache.put(key, proxyClient); 490 } 491 return proxyClient; 492 } 493 getConnectionName(String url, String userName)494 public static String getConnectionName(String url, 495 String userName) { 496 if (userName != null && userName.length() > 0) { 497 return userName + "@" + url; 498 } else { 499 return url; 500 } 501 } 502 getCacheKey(String url, String userName, String password)503 private static String getCacheKey(String url, 504 String userName, String password) { 505 return (url == null ? "" : url) + ":" + 506 (userName == null ? "" : userName) + ":" + 507 (password == null ? "" : password); 508 } 509 510 /** 511 * Gets a proxy client for a given "hostname:port". 512 */ getProxyClient(String hostName, int port, String userName, String password)513 public static ProxyClient getProxyClient(String hostName, int port, 514 String userName, String password) 515 throws IOException { 516 final String key = getCacheKey(hostName, port, userName, password); 517 ProxyClient proxyClient = cache.get(key); 518 if (proxyClient == null) { 519 proxyClient = new ProxyClient(hostName, port, userName, password); 520 cache.put(key, proxyClient); 521 } 522 return proxyClient; 523 } 524 getConnectionName(String hostName, int port, String userName)525 public static String getConnectionName(String hostName, int port, 526 String userName) { 527 String name = hostName + ":" + port; 528 if (userName != null && userName.length() > 0) { 529 return userName + "@" + name; 530 } else { 531 return name; 532 } 533 } 534 getCacheKey(String hostName, int port, String userName, String password)535 private static String getCacheKey(String hostName, int port, 536 String userName, String password) { 537 return (hostName == null ? "" : hostName) + ":" + 538 port + ":" + 539 (userName == null ? "" : userName) + ":" + 540 (password == null ? "" : password); 541 } 542 connectionName()543 public String connectionName() { 544 return connectionName; 545 } 546 getDisplayName()547 public String getDisplayName() { 548 return displayName; 549 } 550 551 @Override toString()552 public String toString() { 553 return displayName; 554 } 555 getMBeanServerConnection()556 public MBeanServerConnection getMBeanServerConnection() { 557 return mbsc; 558 } 559 getSnapshotMBeanServerConnection()560 public SnapshotMBeanServerConnection getSnapshotMBeanServerConnection() { 561 return server; 562 } 563 getUrl()564 public String getUrl() { 565 return advancedUrl; 566 } 567 getHostName()568 public String getHostName() { 569 return hostName; 570 } 571 getPort()572 public int getPort() { 573 return port; 574 } 575 getVmid()576 public int getVmid() { 577 return (lvm != null) ? lvm.vmid() : 0; 578 } 579 getUserName()580 public String getUserName() { 581 return userName; 582 } 583 getPassword()584 public String getPassword() { 585 return password; 586 } 587 disconnect()588 public void disconnect() { 589 // Reset remote stub 590 stub = null; 591 // Close MBeanServer connection 592 if (jmxc != null) { 593 try { 594 jmxc.close(); 595 } catch (IOException e) { 596 // Ignore ??? 597 } 598 } 599 // Reset platform MBean references 600 classLoadingMBean = null; 601 compilationMBean = null; 602 memoryMBean = null; 603 operatingSystemMBean = null; 604 runtimeMBean = null; 605 threadMBean = null; 606 sunOperatingSystemMXBean = null; 607 garbageCollectorMBeans = null; 608 // Set connection state to DISCONNECTED 609 if (!isDead) { 610 isDead = true; 611 setConnectionState(ConnectionState.DISCONNECTED); 612 } 613 } 614 615 /** 616 * Returns the list of domains in which any MBean is 617 * currently registered. 618 */ getDomains()619 public String[] getDomains() throws IOException { 620 return server.getDomains(); 621 } 622 623 /** 624 * Returns a map of MBeans with ObjectName as the key and MBeanInfo value 625 * of a given domain. If domain is <tt>null</tt>, all MBeans 626 * are returned. If no MBean found, an empty map is returned. 627 * 628 */ getMBeans(String domain)629 public Map<ObjectName, MBeanInfo> getMBeans(String domain) 630 throws IOException { 631 632 ObjectName name = null; 633 if (domain != null) { 634 try { 635 name = new ObjectName(domain + ":*"); 636 } catch (MalformedObjectNameException e) { 637 // should not reach here 638 assert(false); 639 } 640 } 641 Set mbeans = server.queryNames(name, null); 642 Map<ObjectName,MBeanInfo> result = 643 new HashMap<ObjectName,MBeanInfo>(mbeans.size()); 644 Iterator iterator = mbeans.iterator(); 645 while (iterator.hasNext()) { 646 Object object = iterator.next(); 647 if (object instanceof ObjectName) { 648 ObjectName o = (ObjectName)object; 649 try { 650 MBeanInfo info = server.getMBeanInfo(o); 651 result.put(o, info); 652 } catch (IntrospectionException e) { 653 // TODO: should log the error 654 } catch (InstanceNotFoundException e) { 655 // TODO: should log the error 656 } catch (ReflectionException e) { 657 // TODO: should log the error 658 } 659 } 660 } 661 return result; 662 } 663 664 /** 665 * Returns a list of attributes of a named MBean. 666 * 667 */ getAttributes(ObjectName name, String[] attributes)668 public AttributeList getAttributes(ObjectName name, String[] attributes) 669 throws IOException { 670 AttributeList list = null; 671 try { 672 list = server.getAttributes(name, attributes); 673 } catch (InstanceNotFoundException e) { 674 // TODO: A MBean may have been unregistered. 675 // need to set up listener to listen for MBeanServerNotification. 676 } catch (ReflectionException e) { 677 // TODO: should log the error 678 } 679 return list; 680 } 681 682 /** 683 * Set the value of a specific attribute of a named MBean. 684 */ setAttribute(ObjectName name, Attribute attribute)685 public void setAttribute(ObjectName name, Attribute attribute) 686 throws InvalidAttributeValueException, 687 MBeanException, 688 IOException { 689 try { 690 server.setAttribute(name, attribute); 691 } catch (InstanceNotFoundException e) { 692 // TODO: A MBean may have been unregistered. 693 } catch (AttributeNotFoundException e) { 694 assert(false); 695 } catch (ReflectionException e) { 696 // TODO: should log the error 697 } 698 } 699 700 /** 701 * Invokes an operation of a named MBean. 702 * 703 * @throws MBeanException Wraps an exception thrown by 704 * the MBean's invoked method. 705 */ invoke(ObjectName name, String operationName, Object[] params, String[] signature)706 public Object invoke(ObjectName name, String operationName, 707 Object[] params, String[] signature) 708 throws IOException, MBeanException { 709 Object result = null; 710 try { 711 result = server.invoke(name, operationName, params, signature); 712 } catch (InstanceNotFoundException e) { 713 // TODO: A MBean may have been unregistered. 714 } catch (ReflectionException e) { 715 // TODO: should log the error 716 } 717 return result; 718 } 719 getClassLoadingMXBean()720 public synchronized ClassLoadingMXBean getClassLoadingMXBean() throws IOException { 721 if (hasPlatformMXBeans && classLoadingMBean == null) { 722 classLoadingMBean = 723 newPlatformMXBeanProxy(server, CLASS_LOADING_MXBEAN_NAME, 724 ClassLoadingMXBean.class); 725 } 726 return classLoadingMBean; 727 } 728 getCompilationMXBean()729 public synchronized CompilationMXBean getCompilationMXBean() throws IOException { 730 if (hasCompilationMXBean && compilationMBean == null) { 731 compilationMBean = 732 newPlatformMXBeanProxy(server, COMPILATION_MXBEAN_NAME, 733 CompilationMXBean.class); 734 } 735 return compilationMBean; 736 } 737 738 getGarbageCollectorMXBeans()739 public synchronized Collection<GarbageCollectorMXBean> getGarbageCollectorMXBeans() 740 throws IOException { 741 742 // TODO: How to deal with changes to the list?? 743 if (garbageCollectorMBeans == null) { 744 ObjectName gcName = null; 745 try { 746 gcName = new ObjectName(GARBAGE_COLLECTOR_MXBEAN_DOMAIN_TYPE + ",*"); 747 } catch (MalformedObjectNameException e) { 748 // should not reach here 749 assert(false); 750 } 751 Set mbeans = server.queryNames(gcName, null); 752 if (mbeans != null) { 753 garbageCollectorMBeans = new ArrayList<GarbageCollectorMXBean>(); 754 Iterator iterator = mbeans.iterator(); 755 while (iterator.hasNext()) { 756 ObjectName on = (ObjectName) iterator.next(); 757 String name = GARBAGE_COLLECTOR_MXBEAN_DOMAIN_TYPE + 758 ",name=" + on.getKeyProperty("name"); 759 760 GarbageCollectorMXBean mBean = 761 newPlatformMXBeanProxy(server, name, 762 GarbageCollectorMXBean.class); 763 garbageCollectorMBeans.add(mBean); 764 } 765 } 766 } 767 return garbageCollectorMBeans; 768 } 769 getMemoryMXBean()770 public synchronized MemoryMXBean getMemoryMXBean() throws IOException { 771 if (hasPlatformMXBeans && memoryMBean == null) { 772 memoryMBean = 773 newPlatformMXBeanProxy(server, MEMORY_MXBEAN_NAME, 774 MemoryMXBean.class); 775 } 776 return memoryMBean; 777 } 778 getRuntimeMXBean()779 public synchronized RuntimeMXBean getRuntimeMXBean() throws IOException { 780 if (hasPlatformMXBeans && runtimeMBean == null) { 781 runtimeMBean = 782 newPlatformMXBeanProxy(server, RUNTIME_MXBEAN_NAME, 783 RuntimeMXBean.class); 784 } 785 return runtimeMBean; 786 } 787 788 getThreadMXBean()789 public synchronized ThreadMXBean getThreadMXBean() throws IOException { 790 if (hasPlatformMXBeans && threadMBean == null) { 791 threadMBean = 792 newPlatformMXBeanProxy(server, THREAD_MXBEAN_NAME, 793 ThreadMXBean.class); 794 } 795 return threadMBean; 796 } 797 getOperatingSystemMXBean()798 public synchronized OperatingSystemMXBean getOperatingSystemMXBean() throws IOException { 799 if (hasPlatformMXBeans && operatingSystemMBean == null) { 800 operatingSystemMBean = 801 newPlatformMXBeanProxy(server, OPERATING_SYSTEM_MXBEAN_NAME, 802 OperatingSystemMXBean.class); 803 } 804 return operatingSystemMBean; 805 } 806 807 public synchronized java.lang.management.OperatingSystemMXBean getSunOperatingSystemMXBean()808 getSunOperatingSystemMXBean() throws IOException { 809 810 try { 811 ObjectName on = new ObjectName(OPERATING_SYSTEM_MXBEAN_NAME); 812 if (sunOperatingSystemMXBean == null) { 813 if (server.isInstanceOf(on, 814 "java.lang.management.OperatingSystemMXBean")) 815 { 816 sunOperatingSystemMXBean = 817 newPlatformMXBeanProxy(server, 818 OPERATING_SYSTEM_MXBEAN_NAME, 819 // com.sun.management.OperatingSystemMXBean.class); 820 java.lang.management.OperatingSystemMXBean.class); 821 } 822 } 823 } catch (InstanceNotFoundException e) { 824 return null; 825 } catch (MalformedObjectNameException e) { 826 return null; // should never reach here 827 } 828 return sunOperatingSystemMXBean; 829 } 830 831 /* 832 public synchronized HotSpotDiagnosticMXBean getHotSpotDiagnosticMXBean() throws IOException { 833 if (hasHotSpotDiagnosticMXBean && hotspotDiagnosticMXBean == null) { 834 hotspotDiagnosticMXBean = 835 newPlatformMXBeanProxy(server, HOTSPOT_DIAGNOSTIC_MXBEAN_NAME, 836 HotSpotDiagnosticMXBean.class); 837 } 838 return hotspotDiagnosticMXBean; 839 } 840 */ 841 getMXBean(ObjectName objName, Class<T> interfaceClass)842 public <T> T getMXBean(ObjectName objName, Class<T> interfaceClass) 843 throws IOException { 844 return newPlatformMXBeanProxy(server, 845 objName.toString(), 846 interfaceClass); 847 848 } 849 850 // Return thread IDs of deadlocked threads or null if any. 851 // It finds deadlocks involving only monitors if it's a Tiger VM. 852 // Otherwise, it finds deadlocks involving both monitors and 853 // the concurrent locks. findDeadlockedThreads()854 public long[] findDeadlockedThreads() throws IOException { 855 ThreadMXBean tm = getThreadMXBean(); 856 if (supportsLockUsage && tm.isSynchronizerUsageSupported()) { 857 return tm.findDeadlockedThreads(); 858 } else { 859 return tm.findMonitorDeadlockedThreads(); 860 } 861 } 862 markAsDead()863 public synchronized void markAsDead() { 864 disconnect(); 865 } 866 isDead()867 public boolean isDead() { 868 return isDead; 869 } 870 isConnected()871 boolean isConnected() { 872 return !isDead(); 873 } 874 hasPlatformMXBeans()875 boolean hasPlatformMXBeans() { 876 return this.hasPlatformMXBeans; 877 } 878 hasHotSpotDiagnosticMXBean()879 boolean hasHotSpotDiagnosticMXBean() { 880 return this.hasHotSpotDiagnosticMXBean; 881 } 882 isLockUsageSupported()883 boolean isLockUsageSupported() { 884 return supportsLockUsage; 885 } 886 isRegistered(ObjectName name)887 public boolean isRegistered(ObjectName name) throws IOException { 888 return server.isRegistered(name); 889 } 890 addPropertyChangeListener(PropertyChangeListener listener)891 public void addPropertyChangeListener(PropertyChangeListener listener) 892 { 893 894 } 895 addWeakPropertyChangeListener(PropertyChangeListener listener)896 public void addWeakPropertyChangeListener(PropertyChangeListener listener) 897 { 898 if (!(listener instanceof WeakPCL)) 899 { 900 listener = new WeakPCL(listener); 901 } 902 903 } 904 removePropertyChangeListener(PropertyChangeListener listener)905 public void removePropertyChangeListener(PropertyChangeListener listener) 906 { 907 908 } 909 910 /** 911 * The PropertyChangeListener is handled via a WeakReference 912 * so as not to pin down the listener. 913 */ 914 private class WeakPCL extends WeakReference<PropertyChangeListener> 915 implements PropertyChangeListener { WeakPCL(PropertyChangeListener referent)916 WeakPCL(PropertyChangeListener referent) { 917 super(referent); 918 } 919 propertyChange(PropertyChangeEvent pce)920 public void propertyChange(PropertyChangeEvent pce) { 921 PropertyChangeListener pcl = get(); 922 923 if (pcl == null) { 924 // The referent listener was GC'ed, we're no longer 925 // interested in PropertyChanges, remove the listener. 926 dispose(); 927 } else { 928 pcl.propertyChange(pce); 929 } 930 } 931 dispose()932 private void dispose() { 933 removePropertyChangeListener(this); 934 } 935 } 936 937 // 938 // Snapshot MBeanServerConnection: 939 // 940 // This is an object that wraps an existing MBeanServerConnection and adds 941 // caching to it, as follows: 942 // 943 // - The first time an attribute is called in a given MBean, the result is 944 // cached. Every subsequent time getAttribute is called for that attribute 945 // the cached result is returned. 946 // 947 // - Before every call to VMPanel.update() or when the Refresh button in the 948 // Attributes table is pressed down the attributes cache is flushed. Then 949 // any subsequent call to getAttribute will retrieve all the values for 950 // the attributes that are known to the cache. 951 // 952 // - The attributes cache uses a learning approach and only the attributes 953 // that are in the cache will be retrieved between two subsequent updates. 954 // 955 956 public interface SnapshotMBeanServerConnection 957 extends MBeanServerConnection { 958 /** 959 * Flush all cached values of attributes. 960 */ flush()961 public void flush(); 962 } 963 964 public static class Snapshot { Snapshot()965 private Snapshot() { 966 } 967 public static SnapshotMBeanServerConnection newSnapshot(MBeanServerConnection mbsc)968 newSnapshot(MBeanServerConnection mbsc) { 969 final InvocationHandler ih = new SnapshotInvocationHandler(mbsc); 970 return (SnapshotMBeanServerConnection) Proxy.newProxyInstance( 971 Snapshot.class.getClassLoader(), 972 new Class[] {SnapshotMBeanServerConnection.class}, 973 ih); 974 } 975 } 976 977 static class SnapshotInvocationHandler implements InvocationHandler { 978 979 private final MBeanServerConnection conn; 980 private Map<ObjectName, NameValueMap> cachedValues = newMap(); 981 private Map<ObjectName, Set<String>> cachedNames = newMap(); 982 983 @SuppressWarnings("serial") 984 private static final class NameValueMap 985 extends HashMap<String, Object> {} 986 SnapshotInvocationHandler(MBeanServerConnection conn)987 SnapshotInvocationHandler(MBeanServerConnection conn) { 988 this.conn = conn; 989 } 990 flush()991 synchronized void flush() { 992 cachedValues = newMap(); 993 } 994 invoke(Object proxy, Method method, Object[] args)995 public Object invoke(Object proxy, Method method, Object[] args) 996 throws Throwable { 997 final String methodName = method.getName(); 998 if (methodName.equals("getAttribute")) { 999 return getAttribute((ObjectName) args[0], (String) args[1]); 1000 } else if (methodName.equals("getAttributes")) { 1001 return getAttributes((ObjectName) args[0], (String[]) args[1]); 1002 } else if (methodName.equals("flush")) { 1003 flush(); 1004 return null; 1005 } else { 1006 try { 1007 return method.invoke(conn, args); 1008 } catch (InvocationTargetException e) { 1009 throw e.getCause(); 1010 } 1011 } 1012 } 1013 getAttribute(ObjectName objName, String attrName)1014 private Object getAttribute(ObjectName objName, String attrName) 1015 throws MBeanException, InstanceNotFoundException, 1016 AttributeNotFoundException, ReflectionException, IOException { 1017 final NameValueMap values = getCachedAttributes( 1018 objName, Collections.singleton(attrName)); 1019 Object value = values.get(attrName); 1020 if (value != null || values.containsKey(attrName)) { 1021 return value; 1022 } 1023 // Not in cache, presumably because it was omitted from the 1024 // getAttributes result because of an exception. Following 1025 // call will probably provoke the same exception. 1026 return conn.getAttribute(objName, attrName); 1027 } 1028 getAttributes( ObjectName objName, String[] attrNames)1029 private AttributeList getAttributes( 1030 ObjectName objName, String[] attrNames) throws 1031 InstanceNotFoundException, ReflectionException, IOException { 1032 final NameValueMap values = getCachedAttributes( 1033 objName, 1034 new TreeSet<String>(Arrays.asList(attrNames))); 1035 final AttributeList list = new AttributeList(); 1036 for (String attrName : attrNames) { 1037 final Object value = values.get(attrName); 1038 if (value != null || values.containsKey(attrName)) { 1039 list.add(new Attribute(attrName, value)); 1040 } 1041 } 1042 return list; 1043 } 1044 getCachedAttributes( ObjectName objName, Set<String> attrNames)1045 private synchronized NameValueMap getCachedAttributes( 1046 ObjectName objName, Set<String> attrNames) throws 1047 InstanceNotFoundException, ReflectionException, IOException { 1048 NameValueMap values = cachedValues.get(objName); 1049 if (values != null && values.keySet().containsAll(attrNames)) { 1050 return values; 1051 } 1052 attrNames = new TreeSet<String>(attrNames); 1053 Set<String> oldNames = cachedNames.get(objName); 1054 if (oldNames != null) { 1055 attrNames.addAll(oldNames); 1056 } 1057 values = new NameValueMap(); 1058 final AttributeList attrs = conn.getAttributes( 1059 objName, 1060 attrNames.toArray(new String[attrNames.size()])); 1061 for (Attribute attr : attrs.asList()) { 1062 values.put(attr.getName(), attr.getValue()); 1063 } 1064 cachedValues.put(objName, values); 1065 cachedNames.put(objName, attrNames); 1066 return values; 1067 } 1068 1069 // See http://www.artima.com/weblogs/viewpost.jsp?thread=79394 newMap()1070 private static <K, V> Map<K, V> newMap() { 1071 return new HashMap<K, V>(); 1072 } 1073 } 1074 1075 /** 1076 * @return 1077 */ getProcessCpuTime()1078 public long getProcessCpuTime() throws Exception 1079 { 1080 try 1081 { 1082 String osMXBeanClassName = "com.sun.management.OperatingSystemMXBean"; 1083 if (lvm.isJ9Mode()) 1084 { 1085 osMXBeanClassName = "com.ibm.lang.management.OperatingSystemMXBean"; 1086 } 1087 1088 if (Proxy.isProxyClass(getOperatingSystemMXBean().getClass())) 1089 { 1090 Long cpuTime = (Long) Proxy 1091 .getInvocationHandler(getOperatingSystemMXBean()) 1092 .invoke( 1093 getOperatingSystemMXBean(), 1094 Class.forName(osMXBeanClassName).getMethod("getProcessCpuTime"), 1095 null); 1096 1097 if (lvm.isJ9Mode()) 1098 { 1099 //this is very strange, J9 does return the value in "100ns units" 1100 //which violates the management spec 1101 //see http://publib.boulder.ibm.com/infocenter/javasdk/v6r0/index.jsp?topic=%2Fcom.ibm.java.api.60.doc%2Fcom.ibm.lang.management%2Fcom%2Fibm%2Flang%2Fmanagement%2FOperatingSystemMXBean.html 1102 return cpuTime * 100; 1103 } 1104 else 1105 { 1106 return cpuTime; 1107 } 1108 1109 } 1110 else 1111 { 1112 throw new UnsupportedOperationException( 1113 "Unsupported JDK, please report bug"); 1114 } 1115 } 1116 catch (Throwable e) 1117 { 1118 throw new RuntimeException(e); 1119 } 1120 } 1121 } 1122