0001: /*
0002: * Copyright 2004-2007 Sun Microsystems, Inc. All Rights Reserved.
0003: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
0004: *
0005: * This code is free software; you can redistribute it and/or modify it
0006: * under the terms of the GNU General Public License version 2 only, as
0007: * published by the Free Software Foundation. Sun designates this
0008: * particular file as subject to the "Classpath" exception as provided
0009: * by Sun in the LICENSE file that accompanied this code.
0010: *
0011: * This code is distributed in the hope that it will be useful, but WITHOUT
0012: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
0013: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
0014: * version 2 for more details (a copy is included in the LICENSE file that
0015: * accompanied this code).
0016: *
0017: * You should have received a copy of the GNU General Public License version
0018: * 2 along with this work; if not, write to the Free Software Foundation,
0019: * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
0020: *
0021: * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
0022: * CA 95054 USA or visit www.sun.com if you need additional information or
0023: * have any questions.
0024: */
0025:
0026: package sun.tools.jconsole;
0027:
0028: import com.sun.management.HotSpotDiagnosticMXBean;
0029: import com.sun.tools.jconsole.JConsoleContext;
0030: import com.sun.tools.jconsole.JConsoleContext.ConnectionState;
0031: import java.awt.Component;
0032: import java.beans.PropertyChangeListener;
0033: import java.beans.PropertyChangeEvent;
0034: import java.io.IOException;
0035: import java.lang.management.*;
0036: import static java.lang.management.ManagementFactory.*;
0037: import java.lang.ref.WeakReference;
0038: import java.lang.reflect.*;
0039: import java.rmi.*;
0040: import java.rmi.registry.*;
0041: import java.rmi.server.*;
0042: import java.util.*;
0043: import javax.management.*;
0044: import javax.management.remote.*;
0045: import javax.management.remote.rmi.*;
0046: import javax.rmi.ssl.SslRMIClientSocketFactory;
0047: import javax.swing.event.SwingPropertyChangeSupport;
0048: import sun.rmi.server.UnicastRef2;
0049: import sun.rmi.transport.LiveRef;
0050:
0051: public class ProxyClient implements JConsoleContext {
0052:
0053: private ConnectionState connectionState = ConnectionState.DISCONNECTED;
0054:
0055: // The SwingPropertyChangeSupport will fire events on the EDT
0056: private SwingPropertyChangeSupport propertyChangeSupport = new SwingPropertyChangeSupport(
0057: this , true);
0058:
0059: private static Map<String, ProxyClient> cache = Collections
0060: .synchronizedMap(new HashMap<String, ProxyClient>());
0061:
0062: private volatile boolean isDead = true;
0063: private String hostName = null;
0064: private int port = 0;
0065: private String userName = null;
0066: private String password = null;
0067: private boolean hasPlatformMXBeans = false;
0068: private boolean hasHotSpotDiagnosticMXBean = false;
0069: private boolean hasCompilationMXBean = false;
0070: private boolean supportsLockUsage = false;
0071:
0072: // REVISIT: VMPanel and other places relying using getUrl().
0073:
0074: // set only if it's created for local monitoring
0075: private LocalVirtualMachine lvm;
0076:
0077: // set only if it's created from a given URL via the Advanced tab
0078: private String advancedUrl = null;
0079:
0080: private JMXServiceURL jmxUrl = null;
0081: private SnapshotMBeanServerConnection server = null;
0082: private JMXConnector jmxc = null;
0083: private RMIServer stub = null;
0084: private static final SslRMIClientSocketFactory sslRMIClientSocketFactory = new SslRMIClientSocketFactory();
0085: private String registryHostName = null;
0086: private int registryPort = 0;
0087: private boolean vmConnector = false;
0088: private boolean sslRegistry = false;
0089: private boolean sslStub = false;
0090: final private String connectionName;
0091: final private String displayName;
0092:
0093: private ClassLoadingMXBean classLoadingMBean = null;
0094: private CompilationMXBean compilationMBean = null;
0095: private MemoryMXBean memoryMBean = null;
0096: private OperatingSystemMXBean operatingSystemMBean = null;
0097: private RuntimeMXBean runtimeMBean = null;
0098: private ThreadMXBean threadMBean = null;
0099:
0100: private com.sun.management.OperatingSystemMXBean sunOperatingSystemMXBean = null;
0101: private HotSpotDiagnosticMXBean hotspotDiagnosticMXBean = null;
0102:
0103: private List<MemoryPoolProxy> memoryPoolProxies = null;
0104: private List<GarbageCollectorMXBean> garbageCollectorMBeans = null;
0105: private String detectDeadlocksOperation = null;
0106:
0107: final static private String HOTSPOT_DIAGNOSTIC_MXBEAN_NAME = "com.sun.management:type=HotSpotDiagnostic";
0108:
0109: private ProxyClient(String hostName, int port, String userName,
0110: String password) throws IOException {
0111: this .connectionName = getConnectionName(hostName, port,
0112: userName);
0113: this .displayName = connectionName;
0114: if (hostName.equals("localhost") && port == 0) {
0115: // Monitor self
0116: this .hostName = hostName;
0117: this .port = port;
0118: } else {
0119: // Create an RMI connector client and connect it to
0120: // the RMI connector server
0121: final String urlPath = "/jndi/rmi://" + hostName + ":"
0122: + port + "/jmxrmi";
0123: JMXServiceURL url = new JMXServiceURL("rmi", "", 0, urlPath);
0124: setParameters(url, userName, password);
0125: vmConnector = true;
0126: registryHostName = hostName;
0127: registryPort = port;
0128: checkSslConfig();
0129: }
0130: }
0131:
0132: private ProxyClient(String url, String userName, String password)
0133: throws IOException {
0134: this .advancedUrl = url;
0135: this .connectionName = getConnectionName(url, userName);
0136: this .displayName = connectionName;
0137: setParameters(new JMXServiceURL(url), userName, password);
0138: }
0139:
0140: private ProxyClient(LocalVirtualMachine lvm) throws IOException {
0141: this .lvm = lvm;
0142: this .connectionName = getConnectionName(lvm);
0143: this .displayName = "pid: " + lvm.vmid() + " "
0144: + lvm.displayName();
0145: }
0146:
0147: private void setParameters(JMXServiceURL url, String userName,
0148: String password) {
0149: this .jmxUrl = url;
0150: this .hostName = jmxUrl.getHost();
0151: this .port = jmxUrl.getPort();
0152: this .userName = userName;
0153: this .password = password;
0154: }
0155:
0156: private static void checkStub(Remote stub,
0157: Class<? extends Remote> stubClass) {
0158: // Check remote stub is from the expected class.
0159: //
0160: if (stub.getClass() != stubClass) {
0161: if (!Proxy.isProxyClass(stub.getClass())) {
0162: throw new SecurityException("Expecting a "
0163: + stubClass.getName() + " stub!");
0164: } else {
0165: InvocationHandler handler = Proxy
0166: .getInvocationHandler(stub);
0167: if (handler.getClass() != RemoteObjectInvocationHandler.class) {
0168: throw new SecurityException(
0169: "Expecting a dynamic proxy instance with a "
0170: + RemoteObjectInvocationHandler.class
0171: .getName()
0172: + " invocation handler!");
0173: } else {
0174: stub = (Remote) handler;
0175: }
0176: }
0177: }
0178: // Check RemoteRef in stub is from the expected class
0179: // "sun.rmi.server.UnicastRef2".
0180: //
0181: RemoteRef ref = ((RemoteObject) stub).getRef();
0182: if (ref.getClass() != UnicastRef2.class) {
0183: throw new SecurityException("Expecting a "
0184: + UnicastRef2.class.getName()
0185: + " remote reference in stub!");
0186: }
0187: // Check RMIClientSocketFactory in stub is from the expected class
0188: // "javax.rmi.ssl.SslRMIClientSocketFactory".
0189: //
0190: LiveRef liveRef = ((UnicastRef2) ref).getLiveRef();
0191: RMIClientSocketFactory csf = liveRef.getClientSocketFactory();
0192: if (csf == null
0193: || csf.getClass() != SslRMIClientSocketFactory.class) {
0194: throw new SecurityException("Expecting a "
0195: + SslRMIClientSocketFactory.class.getName()
0196: + " RMI client socket factory in stub!");
0197: }
0198: }
0199:
0200: private static final String rmiServerImplStubClassName = "javax.management.remote.rmi.RMIServerImpl_Stub";
0201: private static final Class<? extends Remote> rmiServerImplStubClass;
0202:
0203: static {
0204: // FIXME: RMIServerImpl_Stub is generated at build time
0205: // after jconsole is built. We need to investigate if
0206: // the Makefile can be fixed to build jconsole in the
0207: // right order. As a workaround for now, we dynamically
0208: // load RMIServerImpl_Stub class instead of statically
0209: // referencing it.
0210: Class<? extends Remote> serverStubClass = null;
0211: try {
0212: serverStubClass = Class.forName(rmiServerImplStubClassName)
0213: .asSubclass(Remote.class);
0214: } catch (ClassNotFoundException e) {
0215: // should never reach here
0216: throw (InternalError) new InternalError(e.getMessage())
0217: .initCause(e);
0218: }
0219: rmiServerImplStubClass = serverStubClass;
0220: }
0221:
0222: private void checkSslConfig() throws IOException {
0223: // Get the reference to the RMI Registry and lookup RMIServer stub
0224: //
0225: Registry registry;
0226: try {
0227: registry = LocateRegistry.getRegistry(registryHostName,
0228: registryPort, sslRMIClientSocketFactory);
0229: try {
0230: stub = (RMIServer) registry.lookup("jmxrmi");
0231: } catch (NotBoundException nbe) {
0232: throw (IOException) new IOException(nbe.getMessage())
0233: .initCause(nbe);
0234: }
0235: sslRegistry = true;
0236: } catch (IOException e) {
0237: registry = LocateRegistry.getRegistry(registryHostName,
0238: registryPort);
0239: try {
0240: stub = (RMIServer) registry.lookup("jmxrmi");
0241: } catch (NotBoundException nbe) {
0242: throw (IOException) new IOException(nbe.getMessage())
0243: .initCause(nbe);
0244: }
0245: sslRegistry = false;
0246: }
0247: // Perform the checks for secure stub
0248: //
0249: try {
0250: checkStub(stub, rmiServerImplStubClass);
0251: sslStub = true;
0252: } catch (SecurityException e) {
0253: sslStub = false;
0254: }
0255: }
0256:
0257: /**
0258: * Returns true if the underlying RMI registry is SSL-protected.
0259: *
0260: * @exception UnsupportedOperationException If this {@code ProxyClient}
0261: * does not denote a JMX connector for a JMX VM agent.
0262: */
0263: public boolean isSslRmiRegistry() {
0264: // Check for VM connector
0265: //
0266: if (!isVmConnector()) {
0267: throw new UnsupportedOperationException(
0268: "ProxyClient.isSslRmiRegistry() is only supported if this "
0269: + "ProxyClient is a JMX connector for a JMX VM agent");
0270: }
0271: return sslRegistry;
0272: }
0273:
0274: /**
0275: * Returns true if the retrieved RMI stub is SSL-protected.
0276: *
0277: * @exception UnsupportedOperationException If this {@code ProxyClient}
0278: * does not denote a JMX connector for a JMX VM agent.
0279: */
0280: public boolean isSslRmiStub() {
0281: // Check for VM connector
0282: //
0283: if (!isVmConnector()) {
0284: throw new UnsupportedOperationException(
0285: "ProxyClient.isSslRmiStub() is only supported if this "
0286: + "ProxyClient is a JMX connector for a JMX VM agent");
0287: }
0288: return sslStub;
0289: }
0290:
0291: /**
0292: * Returns true if this {@code ProxyClient} denotes
0293: * a JMX connector for a JMX VM agent.
0294: */
0295: public boolean isVmConnector() {
0296: return vmConnector;
0297: }
0298:
0299: private void setConnectionState(ConnectionState state) {
0300: ConnectionState oldState = this .connectionState;
0301: this .connectionState = state;
0302: propertyChangeSupport.firePropertyChange(
0303: CONNECTION_STATE_PROPERTY, oldState, state);
0304: }
0305:
0306: public ConnectionState getConnectionState() {
0307: return this .connectionState;
0308: }
0309:
0310: void flush() {
0311: if (server != null) {
0312: server.flush();
0313: }
0314: }
0315:
0316: void connect() {
0317: setConnectionState(ConnectionState.CONNECTING);
0318: Exception exception = null;
0319: try {
0320: tryConnect();
0321: } catch (IOException ex) {
0322: if (JConsole.isDebug()) {
0323: ex.printStackTrace();
0324: }
0325: exception = ex;
0326: } catch (SecurityException ex) {
0327: if (JConsole.isDebug()) {
0328: ex.printStackTrace();
0329: }
0330: exception = ex;
0331: }
0332: if (exception != null) {
0333: // TODO: Include exception message with reason
0334: setConnectionState(ConnectionState.DISCONNECTED);
0335: } else {
0336: setConnectionState(ConnectionState.CONNECTED);
0337: }
0338: }
0339:
0340: private void tryConnect() throws IOException {
0341: if (jmxUrl == null && "localhost".equals(hostName) && port == 0) {
0342: // Monitor self
0343: this .jmxc = null;
0344: this .server = Snapshot.newSnapshot(ManagementFactory
0345: .getPlatformMBeanServer());
0346: } else {
0347: // Monitor another process
0348: if (lvm != null) {
0349: if (!lvm.isManageable()) {
0350: lvm.startManagementAgent();
0351: if (!lvm.isManageable()) {
0352: // FIXME: what to throw
0353: throw new IOException(lvm + "not manageable");
0354: }
0355: }
0356: if (this .jmxUrl == null) {
0357: this .jmxUrl = new JMXServiceURL(lvm
0358: .connectorAddress());
0359: }
0360: }
0361: // Need to pass in credentials ?
0362: if (userName == null && password == null) {
0363: if (isVmConnector()) {
0364: // Check for SSL config on reconnection only
0365: if (stub == null) {
0366: checkSslConfig();
0367: }
0368: this .jmxc = new RMIConnector(stub, null);
0369: jmxc.connect();
0370: } else {
0371: this .jmxc = JMXConnectorFactory.connect(jmxUrl);
0372: }
0373: } else {
0374: Map<String, String[]> env = new HashMap<String, String[]>();
0375: env.put(JMXConnector.CREDENTIALS, new String[] {
0376: userName, password });
0377: if (isVmConnector()) {
0378: // Check for SSL config on reconnection only
0379: if (stub == null) {
0380: checkSslConfig();
0381: }
0382: this .jmxc = new RMIConnector(stub, null);
0383: jmxc.connect(env);
0384: } else {
0385: this .jmxc = JMXConnectorFactory
0386: .connect(jmxUrl, env);
0387: }
0388: }
0389: this .server = Snapshot.newSnapshot(jmxc
0390: .getMBeanServerConnection());
0391: }
0392: this .isDead = false;
0393:
0394: try {
0395: ObjectName on = new ObjectName(THREAD_MXBEAN_NAME);
0396: this .hasPlatformMXBeans = server.isRegistered(on);
0397: this .hasHotSpotDiagnosticMXBean = server
0398: .isRegistered(new ObjectName(
0399: HOTSPOT_DIAGNOSTIC_MXBEAN_NAME));
0400: // check if it has 6.0 new APIs
0401: if (this .hasPlatformMXBeans) {
0402: MBeanOperationInfo[] mopis = server.getMBeanInfo(on)
0403: .getOperations();
0404: // look for findDeadlockedThreads operations;
0405: for (MBeanOperationInfo op : mopis) {
0406: if (op.getName().equals("findDeadlockedThreads")) {
0407: this .supportsLockUsage = true;
0408: break;
0409: }
0410: }
0411:
0412: on = new ObjectName(COMPILATION_MXBEAN_NAME);
0413: this .hasCompilationMXBean = server.isRegistered(on);
0414: }
0415: } catch (MalformedObjectNameException e) {
0416: // should not reach here
0417: throw new InternalError(e.getMessage());
0418: } catch (IntrospectionException e) {
0419: InternalError ie = new InternalError(e.getMessage());
0420: ie.initCause(e);
0421: throw ie;
0422: } catch (InstanceNotFoundException e) {
0423: InternalError ie = new InternalError(e.getMessage());
0424: ie.initCause(e);
0425: throw ie;
0426: } catch (ReflectionException e) {
0427: InternalError ie = new InternalError(e.getMessage());
0428: ie.initCause(e);
0429: throw ie;
0430: }
0431:
0432: if (hasPlatformMXBeans) {
0433: // WORKAROUND for bug 5056632
0434: // Check if the access role is correct by getting a RuntimeMXBean
0435: getRuntimeMXBean();
0436: }
0437: }
0438:
0439: /**
0440: * Gets a proxy client for a given local virtual machine.
0441: */
0442: public static ProxyClient getProxyClient(LocalVirtualMachine lvm)
0443: throws IOException {
0444: final String key = getCacheKey(lvm);
0445: ProxyClient proxyClient = cache.get(key);
0446: if (proxyClient == null) {
0447: proxyClient = new ProxyClient(lvm);
0448: cache.put(key, proxyClient);
0449: }
0450: return proxyClient;
0451: }
0452:
0453: public static String getConnectionName(LocalVirtualMachine lvm) {
0454: return Integer.toString(lvm.vmid());
0455: }
0456:
0457: private static String getCacheKey(LocalVirtualMachine lvm) {
0458: return Integer.toString(lvm.vmid());
0459: }
0460:
0461: /**
0462: * Gets a proxy client for a given JMXServiceURL.
0463: */
0464: public static ProxyClient getProxyClient(String url,
0465: String userName, String password) throws IOException {
0466: final String key = getCacheKey(url, userName, password);
0467: ProxyClient proxyClient = cache.get(key);
0468: if (proxyClient == null) {
0469: proxyClient = new ProxyClient(url, userName, password);
0470: cache.put(key, proxyClient);
0471: }
0472: return proxyClient;
0473: }
0474:
0475: public static String getConnectionName(String url, String userName) {
0476: if (userName != null && userName.length() > 0) {
0477: return userName + "@" + url;
0478: } else {
0479: return url;
0480: }
0481: }
0482:
0483: private static String getCacheKey(String url, String userName,
0484: String password) {
0485: return (url == null ? "" : url) + ":"
0486: + (userName == null ? "" : userName) + ":"
0487: + (password == null ? "" : password);
0488: }
0489:
0490: /**
0491: * Gets a proxy client for a given "hostname:port".
0492: */
0493: public static ProxyClient getProxyClient(String hostName, int port,
0494: String userName, String password) throws IOException {
0495: final String key = getCacheKey(hostName, port, userName,
0496: password);
0497: ProxyClient proxyClient = cache.get(key);
0498: if (proxyClient == null) {
0499: proxyClient = new ProxyClient(hostName, port, userName,
0500: password);
0501: cache.put(key, proxyClient);
0502: }
0503: return proxyClient;
0504: }
0505:
0506: public static String getConnectionName(String hostName, int port,
0507: String userName) {
0508: String name = hostName + ":" + port;
0509: if (userName != null && userName.length() > 0) {
0510: return userName + "@" + name;
0511: } else {
0512: return name;
0513: }
0514: }
0515:
0516: private static String getCacheKey(String hostName, int port,
0517: String userName, String password) {
0518: return (hostName == null ? "" : hostName) + ":" + port + ":"
0519: + (userName == null ? "" : userName) + ":"
0520: + (password == null ? "" : password);
0521: }
0522:
0523: public String connectionName() {
0524: return connectionName;
0525: }
0526:
0527: public String getDisplayName() {
0528: return displayName;
0529: }
0530:
0531: public String toString() {
0532: if (!isConnected()) {
0533: return Resources.getText("ConnectionName (disconnected)",
0534: displayName);
0535: } else {
0536: return displayName;
0537: }
0538: }
0539:
0540: public MBeanServerConnection getMBeanServerConnection() {
0541: return server;
0542: }
0543:
0544: public String getUrl() {
0545: return advancedUrl;
0546: }
0547:
0548: public String getHostName() {
0549: return hostName;
0550: }
0551:
0552: public int getPort() {
0553: return port;
0554: }
0555:
0556: public int getVmid() {
0557: return (lvm != null) ? lvm.vmid() : 0;
0558: }
0559:
0560: public String getUserName() {
0561: return userName;
0562: }
0563:
0564: public String getPassword() {
0565: return password;
0566: }
0567:
0568: public void disconnect() {
0569: // Reset remote stub
0570: stub = null;
0571: // Close MBeanServer connection
0572: if (jmxc != null) {
0573: try {
0574: jmxc.close();
0575: } catch (IOException e) {
0576: // Ignore ???
0577: }
0578: }
0579: // Reset platform MBean references
0580: classLoadingMBean = null;
0581: compilationMBean = null;
0582: memoryMBean = null;
0583: operatingSystemMBean = null;
0584: runtimeMBean = null;
0585: threadMBean = null;
0586: sunOperatingSystemMXBean = null;
0587: garbageCollectorMBeans = null;
0588: // Set connection state to DISCONNECTED
0589: if (!isDead) {
0590: isDead = true;
0591: setConnectionState(ConnectionState.DISCONNECTED);
0592: }
0593: }
0594:
0595: /**
0596: * Returns the list of domains in which any MBean is
0597: * currently registered.
0598: */
0599: public String[] getDomains() throws IOException {
0600: return server.getDomains();
0601: }
0602:
0603: /**
0604: * Returns a map of MBeans with ObjectName as the key and MBeanInfo value
0605: * of a given domain. If domain is <tt>null</tt>, all MBeans
0606: * are returned. If no MBean found, an empty map is returned.
0607: *
0608: */
0609: public Map<ObjectName, MBeanInfo> getMBeans(String domain)
0610: throws IOException {
0611:
0612: ObjectName name = null;
0613: if (domain != null) {
0614: try {
0615: name = new ObjectName(domain + ":*");
0616: } catch (MalformedObjectNameException e) {
0617: // should not reach here
0618: assert (false);
0619: }
0620: }
0621: Set mbeans = server.queryNames(name, null);
0622: Map<ObjectName, MBeanInfo> result = new HashMap<ObjectName, MBeanInfo>(
0623: mbeans.size());
0624: Iterator iterator = mbeans.iterator();
0625: while (iterator.hasNext()) {
0626: Object object = iterator.next();
0627: if (object instanceof ObjectName) {
0628: ObjectName o = (ObjectName) object;
0629: try {
0630: MBeanInfo info = server.getMBeanInfo(o);
0631: result.put(o, info);
0632: } catch (IntrospectionException e) {
0633: // TODO: should log the error
0634: } catch (InstanceNotFoundException e) {
0635: // TODO: should log the error
0636: } catch (ReflectionException e) {
0637: // TODO: should log the error
0638: }
0639: }
0640: }
0641: return result;
0642: }
0643:
0644: /**
0645: * Returns a list of attributes of a named MBean.
0646: *
0647: */
0648: public AttributeList getAttributes(ObjectName name,
0649: String[] attributes) throws IOException {
0650: AttributeList list = null;
0651: try {
0652: list = server.getAttributes(name, attributes);
0653: } catch (InstanceNotFoundException e) {
0654: // TODO: A MBean may have been unregistered.
0655: // need to set up listener to listen for MBeanServerNotification.
0656: } catch (ReflectionException e) {
0657: // TODO: should log the error
0658: }
0659: return list;
0660: }
0661:
0662: /**
0663: * Set the value of a specific attribute of a named MBean.
0664: */
0665: public void setAttribute(ObjectName name, Attribute attribute)
0666: throws InvalidAttributeValueException, MBeanException,
0667: IOException {
0668: try {
0669: server.setAttribute(name, attribute);
0670: } catch (InstanceNotFoundException e) {
0671: // TODO: A MBean may have been unregistered.
0672: } catch (AttributeNotFoundException e) {
0673: assert (false);
0674: } catch (ReflectionException e) {
0675: // TODO: should log the error
0676: }
0677: }
0678:
0679: /**
0680: * Invokes an operation of a named MBean.
0681: *
0682: * @throws MBeanException Wraps an exception thrown by
0683: * the MBean's invoked method.
0684: */
0685: public Object invoke(ObjectName name, String operationName,
0686: Object[] params, String[] signature) throws IOException,
0687: MBeanException {
0688: Object result = null;
0689: try {
0690: result = server.invoke(name, operationName, params,
0691: signature);
0692: } catch (InstanceNotFoundException e) {
0693: // TODO: A MBean may have been unregistered.
0694: } catch (ReflectionException e) {
0695: // TODO: should log the error
0696: }
0697: return result;
0698: }
0699:
0700: public synchronized ClassLoadingMXBean getClassLoadingMXBean()
0701: throws IOException {
0702: if (hasPlatformMXBeans && classLoadingMBean == null) {
0703: classLoadingMBean = newPlatformMXBeanProxy(server,
0704: CLASS_LOADING_MXBEAN_NAME, ClassLoadingMXBean.class);
0705: }
0706: return classLoadingMBean;
0707: }
0708:
0709: public synchronized CompilationMXBean getCompilationMXBean()
0710: throws IOException {
0711: if (hasCompilationMXBean && compilationMBean == null) {
0712: compilationMBean = newPlatformMXBeanProxy(server,
0713: COMPILATION_MXBEAN_NAME, CompilationMXBean.class);
0714: }
0715: return compilationMBean;
0716: }
0717:
0718: public Collection<MemoryPoolProxy> getMemoryPoolProxies()
0719: throws IOException {
0720:
0721: // TODO: How to deal with changes to the list??
0722: if (memoryPoolProxies == null) {
0723: ObjectName poolName = null;
0724: try {
0725: poolName = new ObjectName(
0726: MEMORY_POOL_MXBEAN_DOMAIN_TYPE + ",*");
0727: } catch (MalformedObjectNameException e) {
0728: // should not reach here
0729: assert (false);
0730: }
0731: Set mbeans = server.queryNames(poolName, null);
0732: if (mbeans != null) {
0733: memoryPoolProxies = new ArrayList<MemoryPoolProxy>();
0734: Iterator iterator = mbeans.iterator();
0735: while (iterator.hasNext()) {
0736: ObjectName objName = (ObjectName) iterator.next();
0737: MemoryPoolProxy p = new MemoryPoolProxy(this ,
0738: objName);
0739: memoryPoolProxies.add(p);
0740: }
0741: }
0742: }
0743: return memoryPoolProxies;
0744: }
0745:
0746: public synchronized Collection<GarbageCollectorMXBean> getGarbageCollectorMXBeans()
0747: throws IOException {
0748:
0749: // TODO: How to deal with changes to the list??
0750: if (garbageCollectorMBeans == null) {
0751: ObjectName gcName = null;
0752: try {
0753: gcName = new ObjectName(
0754: GARBAGE_COLLECTOR_MXBEAN_DOMAIN_TYPE + ",*");
0755: } catch (MalformedObjectNameException e) {
0756: // should not reach here
0757: assert (false);
0758: }
0759: Set mbeans = server.queryNames(gcName, null);
0760: if (mbeans != null) {
0761: garbageCollectorMBeans = new ArrayList<GarbageCollectorMXBean>();
0762: Iterator iterator = mbeans.iterator();
0763: while (iterator.hasNext()) {
0764: ObjectName on = (ObjectName) iterator.next();
0765: String name = GARBAGE_COLLECTOR_MXBEAN_DOMAIN_TYPE
0766: + ",name=" + on.getKeyProperty("name");
0767:
0768: GarbageCollectorMXBean mBean = newPlatformMXBeanProxy(
0769: server, name, GarbageCollectorMXBean.class);
0770: garbageCollectorMBeans.add(mBean);
0771: }
0772: }
0773: }
0774: return garbageCollectorMBeans;
0775: }
0776:
0777: public synchronized MemoryMXBean getMemoryMXBean()
0778: throws IOException {
0779: if (hasPlatformMXBeans && memoryMBean == null) {
0780: memoryMBean = newPlatformMXBeanProxy(server,
0781: MEMORY_MXBEAN_NAME, MemoryMXBean.class);
0782: }
0783: return memoryMBean;
0784: }
0785:
0786: public synchronized RuntimeMXBean getRuntimeMXBean()
0787: throws IOException {
0788: if (hasPlatformMXBeans && runtimeMBean == null) {
0789: runtimeMBean = newPlatformMXBeanProxy(server,
0790: RUNTIME_MXBEAN_NAME, RuntimeMXBean.class);
0791: }
0792: return runtimeMBean;
0793: }
0794:
0795: public synchronized ThreadMXBean getThreadMXBean()
0796: throws IOException {
0797: if (hasPlatformMXBeans && threadMBean == null) {
0798: threadMBean = newPlatformMXBeanProxy(server,
0799: THREAD_MXBEAN_NAME, ThreadMXBean.class);
0800: }
0801: return threadMBean;
0802: }
0803:
0804: public synchronized OperatingSystemMXBean getOperatingSystemMXBean()
0805: throws IOException {
0806: if (hasPlatformMXBeans && operatingSystemMBean == null) {
0807: operatingSystemMBean = newPlatformMXBeanProxy(server,
0808: OPERATING_SYSTEM_MXBEAN_NAME,
0809: OperatingSystemMXBean.class);
0810: }
0811: return operatingSystemMBean;
0812: }
0813:
0814: public synchronized com.sun.management.OperatingSystemMXBean getSunOperatingSystemMXBean()
0815: throws IOException {
0816:
0817: try {
0818: ObjectName on = new ObjectName(OPERATING_SYSTEM_MXBEAN_NAME);
0819: if (sunOperatingSystemMXBean == null) {
0820: if (server.isInstanceOf(on,
0821: "com.sun.management.OperatingSystemMXBean")) {
0822: sunOperatingSystemMXBean = newPlatformMXBeanProxy(
0823: server,
0824: OPERATING_SYSTEM_MXBEAN_NAME,
0825: com.sun.management.OperatingSystemMXBean.class);
0826: }
0827: }
0828: } catch (InstanceNotFoundException e) {
0829: return null;
0830: } catch (MalformedObjectNameException e) {
0831: return null; // should never reach here
0832: }
0833: return sunOperatingSystemMXBean;
0834: }
0835:
0836: public synchronized HotSpotDiagnosticMXBean getHotSpotDiagnosticMXBean()
0837: throws IOException {
0838: if (hasHotSpotDiagnosticMXBean
0839: && hotspotDiagnosticMXBean == null) {
0840: hotspotDiagnosticMXBean = newPlatformMXBeanProxy(server,
0841: HOTSPOT_DIAGNOSTIC_MXBEAN_NAME,
0842: HotSpotDiagnosticMXBean.class);
0843: }
0844: return hotspotDiagnosticMXBean;
0845: }
0846:
0847: public <T> T getMXBean(ObjectName objName, Class<T> interfaceClass)
0848: throws IOException {
0849: return newPlatformMXBeanProxy(server, objName.toString(),
0850: interfaceClass);
0851:
0852: }
0853:
0854: // Return thread IDs of deadlocked threads or null if any.
0855: // It finds deadlocks involving only monitors if it's a Tiger VM.
0856: // Otherwise, it finds deadlocks involving both monitors and
0857: // the concurrent locks.
0858: public long[] findDeadlockedThreads() throws IOException {
0859: ThreadMXBean tm = getThreadMXBean();
0860: if (supportsLockUsage && tm.isSynchronizerUsageSupported()) {
0861: return tm.findDeadlockedThreads();
0862: } else {
0863: return tm.findMonitorDeadlockedThreads();
0864: }
0865: }
0866:
0867: public synchronized void markAsDead() {
0868: disconnect();
0869: }
0870:
0871: public boolean isDead() {
0872: return isDead;
0873: }
0874:
0875: boolean isConnected() {
0876: return !isDead();
0877: }
0878:
0879: boolean hasPlatformMXBeans() {
0880: return this .hasPlatformMXBeans;
0881: }
0882:
0883: boolean hasHotSpotDiagnosticMXBean() {
0884: return this .hasHotSpotDiagnosticMXBean;
0885: }
0886:
0887: boolean isLockUsageSupported() {
0888: return supportsLockUsage;
0889: }
0890:
0891: public boolean isRegistered(ObjectName name) throws IOException {
0892: return server.isRegistered(name);
0893: }
0894:
0895: public void addPropertyChangeListener(
0896: PropertyChangeListener listener) {
0897: propertyChangeSupport.addPropertyChangeListener(listener);
0898: }
0899:
0900: public void addWeakPropertyChangeListener(
0901: PropertyChangeListener listener) {
0902: if (!(listener instanceof WeakPCL)) {
0903: listener = new WeakPCL(listener);
0904: }
0905: propertyChangeSupport.addPropertyChangeListener(listener);
0906: }
0907:
0908: public void removePropertyChangeListener(
0909: PropertyChangeListener listener) {
0910: if (!(listener instanceof WeakPCL)) {
0911: // Search for the WeakPCL holding this listener (if any)
0912: for (PropertyChangeListener pcl : propertyChangeSupport
0913: .getPropertyChangeListeners()) {
0914: if (pcl instanceof WeakPCL
0915: && ((WeakPCL) pcl).get() == listener) {
0916: listener = pcl;
0917: break;
0918: }
0919: }
0920: }
0921: propertyChangeSupport.removePropertyChangeListener(listener);
0922: }
0923:
0924: /**
0925: * The PropertyChangeListener is handled via a WeakReference
0926: * so as not to pin down the listener.
0927: */
0928: private class WeakPCL extends WeakReference<PropertyChangeListener>
0929: implements PropertyChangeListener {
0930: WeakPCL(PropertyChangeListener referent) {
0931: super (referent);
0932: }
0933:
0934: public void propertyChange(PropertyChangeEvent pce) {
0935: PropertyChangeListener pcl = get();
0936:
0937: if (pcl == null) {
0938: // The referent listener was GC'ed, we're no longer
0939: // interested in PropertyChanges, remove the listener.
0940: dispose();
0941: } else {
0942: pcl.propertyChange(pce);
0943: }
0944: }
0945:
0946: private void dispose() {
0947: removePropertyChangeListener(this );
0948: }
0949: }
0950:
0951: //
0952: // Snapshot MBeanServerConnection:
0953: //
0954: // This is an object that wraps an existing MBeanServerConnection and adds
0955: // caching to it, as follows:
0956: //
0957: // - The first time an attribute is called in a given MBean, the result is
0958: // cached. Every subsequent time getAttribute is called for that attribute
0959: // the cached result is returned.
0960: //
0961: // - Before every call to VMPanel.update() or when the Refresh button in the
0962: // Attributes table is pressed down the attributes cache is flushed. Then
0963: // any subsequent call to getAttribute will retrieve all the values for
0964: // the attributes that are known to the cache.
0965: //
0966: // - The attributes cache uses a learning approach and only the attributes
0967: // that are in the cache will be retrieved between two subsequent updates.
0968: //
0969:
0970: public interface SnapshotMBeanServerConnection extends
0971: MBeanServerConnection {
0972: /**
0973: * Flush all cached values of attributes.
0974: */
0975: public void flush();
0976: }
0977:
0978: public static class Snapshot {
0979: private Snapshot() {
0980: }
0981:
0982: public static SnapshotMBeanServerConnection newSnapshot(
0983: MBeanServerConnection mbsc) {
0984: final InvocationHandler ih = new SnapshotInvocationHandler(
0985: mbsc);
0986: return (SnapshotMBeanServerConnection) Proxy
0987: .newProxyInstance(
0988: Snapshot.class.getClassLoader(),
0989: new Class[] { SnapshotMBeanServerConnection.class },
0990: ih);
0991: }
0992: }
0993:
0994: static class SnapshotInvocationHandler implements InvocationHandler {
0995:
0996: private final MBeanServerConnection conn;
0997: private Map<ObjectName, NameValueMap> cachedValues = newMap();
0998: private Map<ObjectName, Set<String>> cachedNames = newMap();
0999:
1000: @SuppressWarnings("serial")
1001: private static final class NameValueMap extends
1002: HashMap<String, Object> {
1003: }
1004:
1005: SnapshotInvocationHandler(MBeanServerConnection conn) {
1006: this .conn = conn;
1007: }
1008:
1009: synchronized void flush() {
1010: cachedValues = newMap();
1011: }
1012:
1013: public Object invoke(Object proxy, Method method, Object[] args)
1014: throws Throwable {
1015: final String methodName = method.getName();
1016: if (methodName.equals("getAttribute")) {
1017: return getAttribute((ObjectName) args[0],
1018: (String) args[1]);
1019: } else if (methodName.equals("getAttributes")) {
1020: return getAttributes((ObjectName) args[0],
1021: (String[]) args[1]);
1022: } else if (methodName.equals("flush")) {
1023: flush();
1024: return null;
1025: } else {
1026: try {
1027: return method.invoke(conn, args);
1028: } catch (InvocationTargetException e) {
1029: throw e.getCause();
1030: }
1031: }
1032: }
1033:
1034: private Object getAttribute(ObjectName objName, String attrName)
1035: throws MBeanException, InstanceNotFoundException,
1036: AttributeNotFoundException, ReflectionException,
1037: IOException {
1038: final NameValueMap values = getCachedAttributes(objName,
1039: Collections.singleton(attrName));
1040: Object value = values.get(attrName);
1041: if (value != null || values.containsKey(attrName)) {
1042: return value;
1043: }
1044: // Not in cache, presumably because it was omitted from the
1045: // getAttributes result because of an exception. Following
1046: // call will probably provoke the same exception.
1047: return conn.getAttribute(objName, attrName);
1048: }
1049:
1050: private AttributeList getAttributes(ObjectName objName,
1051: String[] attrNames) throws InstanceNotFoundException,
1052: ReflectionException, IOException {
1053: final NameValueMap values = getCachedAttributes(objName,
1054: new TreeSet<String>(Arrays.asList(attrNames)));
1055: final AttributeList list = new AttributeList();
1056: for (String attrName : attrNames) {
1057: final Object value = values.get(attrName);
1058: if (value != null || values.containsKey(attrName)) {
1059: list.add(new Attribute(attrName, value));
1060: }
1061: }
1062: return list;
1063: }
1064:
1065: private synchronized NameValueMap getCachedAttributes(
1066: ObjectName objName, Set<String> attrNames)
1067: throws InstanceNotFoundException, ReflectionException,
1068: IOException {
1069: NameValueMap values = cachedValues.get(objName);
1070: if (values != null
1071: && values.keySet().containsAll(attrNames)) {
1072: return values;
1073: }
1074: attrNames = new TreeSet<String>(attrNames);
1075: Set<String> oldNames = cachedNames.get(objName);
1076: if (oldNames != null) {
1077: attrNames.addAll(oldNames);
1078: }
1079: values = new NameValueMap();
1080: final AttributeList attrs = conn.getAttributes(objName,
1081: attrNames.toArray(new String[attrNames.size()]));
1082: for (Attribute attr : attrs.asList()) {
1083: values.put(attr.getName(), attr.getValue());
1084: }
1085: cachedValues.put(objName, values);
1086: cachedNames.put(objName, attrNames);
1087: return values;
1088: }
1089:
1090: // See http://www.artima.com/weblogs/viewpost.jsp?thread=79394
1091: private static <K, V> Map<K, V> newMap() {
1092: return new HashMap<K, V>();
1093: }
1094: }
1095: }
|