--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/share/classes/sun/tools/jconsole/ProxyClient.java Sat Dec 01 00:00:00 2007 +0000
@@ -0,0 +1,1061 @@
+/*
+ * Copyright 2004-2007 Sun Microsystems, Inc. All Rights Reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package sun.tools.jconsole;
+
+import com.sun.management.HotSpotDiagnosticMXBean;
+import com.sun.tools.jconsole.JConsoleContext;
+import com.sun.tools.jconsole.JConsoleContext.ConnectionState;
+import java.awt.Component;
+import java.beans.PropertyChangeListener;
+import java.beans.PropertyChangeEvent;
+import java.io.IOException;
+import java.lang.management.*;
+import static java.lang.management.ManagementFactory.*;
+import java.lang.ref.WeakReference;
+import java.lang.reflect.*;
+import java.rmi.*;
+import java.rmi.registry.*;
+import java.rmi.server.*;
+import java.util.*;
+import javax.management.*;
+import javax.management.remote.*;
+import javax.management.remote.rmi.*;
+import javax.rmi.ssl.SslRMIClientSocketFactory;
+import javax.swing.event.SwingPropertyChangeSupport;
+import sun.rmi.server.UnicastRef2;
+import sun.rmi.transport.LiveRef;
+
+public class ProxyClient implements JConsoleContext {
+
+ private ConnectionState connectionState = ConnectionState.DISCONNECTED;
+
+ // The SwingPropertyChangeSupport will fire events on the EDT
+ private SwingPropertyChangeSupport propertyChangeSupport =
+ new SwingPropertyChangeSupport(this, true);
+
+ private static Map<String, ProxyClient> cache =
+ Collections.synchronizedMap(new HashMap<String, ProxyClient>());
+
+ private volatile boolean isDead = true;
+ private String hostName = null;
+ private int port = 0;
+ private String userName = null;
+ private String password = null;
+ private boolean hasPlatformMXBeans = false;
+ private boolean hasHotSpotDiagnosticMXBean= false;
+ private boolean hasCompilationMXBean = false;
+ private boolean supportsLockUsage = false;
+
+ // REVISIT: VMPanel and other places relying using getUrl().
+
+ // set only if it's created for local monitoring
+ private LocalVirtualMachine lvm;
+
+ // set only if it's created from a given URL via the Advanced tab
+ private String advancedUrl = null;
+
+ private JMXServiceURL jmxUrl = null;
+ private SnapshotMBeanServerConnection server = null;
+ private JMXConnector jmxc = null;
+ private RMIServer stub = null;
+ private static final SslRMIClientSocketFactory sslRMIClientSocketFactory =
+ new SslRMIClientSocketFactory();
+ private String registryHostName = null;
+ private int registryPort = 0;
+ private boolean vmConnector = false;
+ private boolean sslRegistry = false;
+ private boolean sslStub = false;
+ final private String connectionName;
+ final private String displayName;
+
+ private ClassLoadingMXBean classLoadingMBean = null;
+ private CompilationMXBean compilationMBean = null;
+ private MemoryMXBean memoryMBean = null;
+ private OperatingSystemMXBean operatingSystemMBean = null;
+ private RuntimeMXBean runtimeMBean = null;
+ private ThreadMXBean threadMBean = null;
+
+ private com.sun.management.OperatingSystemMXBean sunOperatingSystemMXBean = null;
+ private HotSpotDiagnosticMXBean hotspotDiagnosticMXBean = null;
+
+ private List<MemoryPoolProxy> memoryPoolProxies = null;
+ private List<GarbageCollectorMXBean> garbageCollectorMBeans = null;
+ private String detectDeadlocksOperation = null;
+
+ final static private String HOTSPOT_DIAGNOSTIC_MXBEAN_NAME =
+ "com.sun.management:type=HotSpotDiagnostic";
+
+ private ProxyClient(String hostName, int port,
+ String userName, String password) throws IOException {
+ this.connectionName = getConnectionName(hostName, port, userName);
+ this.displayName = connectionName;
+ if (hostName.equals("localhost") && port == 0) {
+ // Monitor self
+ this.hostName = hostName;
+ this.port = port;
+ } else {
+ // Create an RMI connector client and connect it to
+ // the RMI connector server
+ final String urlPath = "/jndi/rmi://" + hostName + ":" + port +
+ "/jmxrmi";
+ JMXServiceURL url = new JMXServiceURL("rmi", "", 0, urlPath);
+ setParameters(url, userName, password);
+ vmConnector = true;
+ registryHostName = hostName;
+ registryPort = port;
+ checkSslConfig();
+ }
+ }
+
+ private ProxyClient(String url,
+ String userName, String password) throws IOException {
+ this.advancedUrl = url;
+ this.connectionName = getConnectionName(url, userName);
+ this.displayName = connectionName;
+ setParameters(new JMXServiceURL(url), userName, password);
+ }
+
+ private ProxyClient(LocalVirtualMachine lvm) throws IOException {
+ this.lvm = lvm;
+ this.connectionName = getConnectionName(lvm);
+ this.displayName = "pid: " + lvm.vmid() + " " + lvm.displayName();
+ }
+
+ private void setParameters(JMXServiceURL url, String userName, String password) {
+ this.jmxUrl = url;
+ this.hostName = jmxUrl.getHost();
+ this.port = jmxUrl.getPort();
+ this.userName = userName;
+ this.password = password;
+ }
+
+ private static void checkStub(Remote stub,
+ Class<? extends Remote> stubClass) {
+ // Check remote stub is from the expected class.
+ //
+ if (stub.getClass() != stubClass) {
+ if (!Proxy.isProxyClass(stub.getClass())) {
+ throw new SecurityException(
+ "Expecting a " + stubClass.getName() + " stub!");
+ } else {
+ InvocationHandler handler = Proxy.getInvocationHandler(stub);
+ if (handler.getClass() != RemoteObjectInvocationHandler.class) {
+ throw new SecurityException(
+ "Expecting a dynamic proxy instance with a " +
+ RemoteObjectInvocationHandler.class.getName() +
+ " invocation handler!");
+ } else {
+ stub = (Remote) handler;
+ }
+ }
+ }
+ // Check RemoteRef in stub is from the expected class
+ // "sun.rmi.server.UnicastRef2".
+ //
+ RemoteRef ref = ((RemoteObject)stub).getRef();
+ if (ref.getClass() != UnicastRef2.class) {
+ throw new SecurityException(
+ "Expecting a " + UnicastRef2.class.getName() +
+ " remote reference in stub!");
+ }
+ // Check RMIClientSocketFactory in stub is from the expected class
+ // "javax.rmi.ssl.SslRMIClientSocketFactory".
+ //
+ LiveRef liveRef = ((UnicastRef2)ref).getLiveRef();
+ RMIClientSocketFactory csf = liveRef.getClientSocketFactory();
+ if (csf == null || csf.getClass() != SslRMIClientSocketFactory.class) {
+ throw new SecurityException(
+ "Expecting a " + SslRMIClientSocketFactory.class.getName() +
+ " RMI client socket factory in stub!");
+ }
+ }
+
+ private static final String rmiServerImplStubClassName =
+ "javax.management.remote.rmi.RMIServerImpl_Stub";
+ private static final Class<? extends Remote> rmiServerImplStubClass;
+
+ static {
+ // FIXME: RMIServerImpl_Stub is generated at build time
+ // after jconsole is built. We need to investigate if
+ // the Makefile can be fixed to build jconsole in the
+ // right order. As a workaround for now, we dynamically
+ // load RMIServerImpl_Stub class instead of statically
+ // referencing it.
+ Class<? extends Remote> serverStubClass = null;
+ try {
+ serverStubClass = Class.forName(rmiServerImplStubClassName).asSubclass(Remote.class);
+ } catch (ClassNotFoundException e) {
+ // should never reach here
+ throw (InternalError) new InternalError(e.getMessage()).initCause(e);
+ }
+ rmiServerImplStubClass = serverStubClass;
+ }
+
+ private void checkSslConfig() throws IOException {
+ // Get the reference to the RMI Registry and lookup RMIServer stub
+ //
+ Registry registry;
+ try {
+ registry =
+ LocateRegistry.getRegistry(registryHostName, registryPort,
+ sslRMIClientSocketFactory);
+ try {
+ stub = (RMIServer) registry.lookup("jmxrmi");
+ } catch (NotBoundException nbe) {
+ throw (IOException)
+ new IOException(nbe.getMessage()).initCause(nbe);
+ }
+ sslRegistry = true;
+ } catch (IOException e) {
+ registry =
+ LocateRegistry.getRegistry(registryHostName, registryPort);
+ try {
+ stub = (RMIServer) registry.lookup("jmxrmi");
+ } catch (NotBoundException nbe) {
+ throw (IOException)
+ new IOException(nbe.getMessage()).initCause(nbe);
+ }
+ sslRegistry = false;
+ }
+ // Perform the checks for secure stub
+ //
+ try {
+ checkStub(stub, rmiServerImplStubClass);
+ sslStub = true;
+ } catch (SecurityException e) {
+ sslStub = false;
+ }
+ }
+
+ /**
+ * Returns true if the underlying RMI registry is SSL-protected.
+ *
+ * @exception UnsupportedOperationException If this {@code ProxyClient}
+ * does not denote a JMX connector for a JMX VM agent.
+ */
+ public boolean isSslRmiRegistry() {
+ // Check for VM connector
+ //
+ if (!isVmConnector()) {
+ throw new UnsupportedOperationException(
+ "ProxyClient.isSslRmiRegistry() is only supported if this " +
+ "ProxyClient is a JMX connector for a JMX VM agent");
+ }
+ return sslRegistry;
+ }
+
+ /**
+ * Returns true if the retrieved RMI stub is SSL-protected.
+ *
+ * @exception UnsupportedOperationException If this {@code ProxyClient}
+ * does not denote a JMX connector for a JMX VM agent.
+ */
+ public boolean isSslRmiStub() {
+ // Check for VM connector
+ //
+ if (!isVmConnector()) {
+ throw new UnsupportedOperationException(
+ "ProxyClient.isSslRmiStub() is only supported if this " +
+ "ProxyClient is a JMX connector for a JMX VM agent");
+ }
+ return sslStub;
+ }
+
+ /**
+ * Returns true if this {@code ProxyClient} denotes
+ * a JMX connector for a JMX VM agent.
+ */
+ public boolean isVmConnector() {
+ return vmConnector;
+ }
+
+ private void setConnectionState(ConnectionState state) {
+ ConnectionState oldState = this.connectionState;
+ this.connectionState = state;
+ propertyChangeSupport.firePropertyChange(CONNECTION_STATE_PROPERTY,
+ oldState, state);
+ }
+
+ public ConnectionState getConnectionState() {
+ return this.connectionState;
+ }
+
+ void flush() {
+ if (server != null) {
+ server.flush();
+ }
+ }
+
+ void connect() {
+ setConnectionState(ConnectionState.CONNECTING);
+ try {
+ tryConnect();
+ setConnectionState(ConnectionState.CONNECTED);
+ } catch (Exception e) {
+ if (JConsole.isDebug()) {
+ e.printStackTrace();
+ }
+ setConnectionState(ConnectionState.DISCONNECTED);
+ }
+ }
+
+ private void tryConnect() throws IOException {
+ if (jmxUrl == null && "localhost".equals(hostName) && port == 0) {
+ // Monitor self
+ this.jmxc = null;
+ this.server = Snapshot.newSnapshot(
+ ManagementFactory.getPlatformMBeanServer());
+ } else {
+ // Monitor another process
+ if (lvm != null) {
+ if (!lvm.isManageable()) {
+ lvm.startManagementAgent();
+ if (!lvm.isManageable()) {
+ // FIXME: what to throw
+ throw new IOException(lvm + "not manageable");
+ }
+ }
+ if (this.jmxUrl == null) {
+ this.jmxUrl = new JMXServiceURL(lvm.connectorAddress());
+ }
+ }
+ // Need to pass in credentials ?
+ if (userName == null && password == null) {
+ if (isVmConnector()) {
+ // Check for SSL config on reconnection only
+ if (stub == null) {
+ checkSslConfig();
+ }
+ this.jmxc = new RMIConnector(stub, null);
+ jmxc.connect();
+ } else {
+ this.jmxc = JMXConnectorFactory.connect(jmxUrl);
+ }
+ } else {
+ Map<String, String[]> env = new HashMap<String, String[]>();
+ env.put(JMXConnector.CREDENTIALS,
+ new String[] {userName, password});
+ if (isVmConnector()) {
+ // Check for SSL config on reconnection only
+ if (stub == null) {
+ checkSslConfig();
+ }
+ this.jmxc = new RMIConnector(stub, null);
+ jmxc.connect(env);
+ } else {
+ this.jmxc = JMXConnectorFactory.connect(jmxUrl, env);
+ }
+ }
+ this.server = Snapshot.newSnapshot(jmxc.getMBeanServerConnection());
+ }
+ this.isDead = false;
+
+ try {
+ ObjectName on = new ObjectName(THREAD_MXBEAN_NAME);
+ this.hasPlatformMXBeans = server.isRegistered(on);
+ this.hasHotSpotDiagnosticMXBean =
+ server.isRegistered(new ObjectName(HOTSPOT_DIAGNOSTIC_MXBEAN_NAME));
+ // check if it has 6.0 new APIs
+ if (this.hasPlatformMXBeans) {
+ MBeanOperationInfo[] mopis = server.getMBeanInfo(on).getOperations();
+ // look for findDeadlockedThreads operations;
+ for (MBeanOperationInfo op : mopis) {
+ if (op.getName().equals("findDeadlockedThreads")) {
+ this.supportsLockUsage = true;
+ break;
+ }
+ }
+
+ on = new ObjectName(COMPILATION_MXBEAN_NAME);
+ this.hasCompilationMXBean = server.isRegistered(on);
+ }
+ } catch (MalformedObjectNameException e) {
+ // should not reach here
+ throw new InternalError(e.getMessage());
+ } catch (IntrospectionException e) {
+ InternalError ie = new InternalError(e.getMessage());
+ ie.initCause(e);
+ throw ie;
+ } catch (InstanceNotFoundException e) {
+ InternalError ie = new InternalError(e.getMessage());
+ ie.initCause(e);
+ throw ie;
+ } catch (ReflectionException e) {
+ InternalError ie = new InternalError(e.getMessage());
+ ie.initCause(e);
+ throw ie;
+ }
+
+ if (hasPlatformMXBeans) {
+ // WORKAROUND for bug 5056632
+ // Check if the access role is correct by getting a RuntimeMXBean
+ getRuntimeMXBean();
+ }
+ }
+
+ /**
+ * Gets a proxy client for a given local virtual machine.
+ */
+ public static ProxyClient getProxyClient(LocalVirtualMachine lvm)
+ throws IOException {
+ final String key = getCacheKey(lvm);
+ ProxyClient proxyClient = cache.get(key);
+ if (proxyClient == null) {
+ proxyClient = new ProxyClient(lvm);
+ cache.put(key, proxyClient);
+ }
+ return proxyClient;
+ }
+
+ public static String getConnectionName(LocalVirtualMachine lvm) {
+ return Integer.toString(lvm.vmid());
+ }
+
+ private static String getCacheKey(LocalVirtualMachine lvm) {
+ return Integer.toString(lvm.vmid());
+ }
+
+ /**
+ * Gets a proxy client for a given JMXServiceURL.
+ */
+ public static ProxyClient getProxyClient(String url,
+ String userName, String password)
+ throws IOException {
+ final String key = getCacheKey(url, userName, password);
+ ProxyClient proxyClient = cache.get(key);
+ if (proxyClient == null) {
+ proxyClient = new ProxyClient(url, userName, password);
+ cache.put(key, proxyClient);
+ }
+ return proxyClient;
+ }
+
+ public static String getConnectionName(String url,
+ String userName) {
+ if (userName != null && userName.length() > 0) {
+ return userName + "@" + url;
+ } else {
+ return url;
+ }
+ }
+
+ private static String getCacheKey(String url,
+ String userName, String password) {
+ return (url == null ? "" : url) + ":" +
+ (userName == null ? "" : userName) + ":" +
+ (password == null ? "" : password);
+ }
+
+ /**
+ * Gets a proxy client for a given "hostname:port".
+ */
+ public static ProxyClient getProxyClient(String hostName, int port,
+ String userName, String password)
+ throws IOException {
+ final String key = getCacheKey(hostName, port, userName, password);
+ ProxyClient proxyClient = cache.get(key);
+ if (proxyClient == null) {
+ proxyClient = new ProxyClient(hostName, port, userName, password);
+ cache.put(key, proxyClient);
+ }
+ return proxyClient;
+ }
+
+ public static String getConnectionName(String hostName, int port,
+ String userName) {
+ String name = hostName + ":" + port;
+ if (userName != null && userName.length() > 0) {
+ return userName + "@" + name;
+ } else {
+ return name;
+ }
+ }
+
+ private static String getCacheKey(String hostName, int port,
+ String userName, String password) {
+ return (hostName == null ? "" : hostName) + ":" +
+ port + ":" +
+ (userName == null ? "" : userName) + ":" +
+ (password == null ? "" : password);
+ }
+
+ public String connectionName() {
+ return connectionName;
+ }
+
+ public String getDisplayName() {
+ return displayName;
+ }
+
+ public String toString() {
+ if (!isConnected()) {
+ return Resources.getText("ConnectionName (disconnected)", displayName);
+ } else {
+ return displayName;
+ }
+ }
+
+ public MBeanServerConnection getMBeanServerConnection() {
+ return server;
+ }
+
+ public String getUrl() {
+ return advancedUrl;
+ }
+
+ public String getHostName() {
+ return hostName;
+ }
+
+ public int getPort() {
+ return port;
+ }
+
+ public int getVmid() {
+ return (lvm != null) ? lvm.vmid() : 0;
+ }
+
+ public String getUserName() {
+ return userName;
+ }
+
+ public String getPassword() {
+ return password;
+ }
+
+ public void disconnect() {
+ // Reset remote stub
+ stub = null;
+ // Close MBeanServer connection
+ if (jmxc != null) {
+ try {
+ jmxc.close();
+ } catch (IOException e) {
+ // Ignore ???
+ }
+ }
+ // Reset platform MBean references
+ classLoadingMBean = null;
+ compilationMBean = null;
+ memoryMBean = null;
+ operatingSystemMBean = null;
+ runtimeMBean = null;
+ threadMBean = null;
+ sunOperatingSystemMXBean = null;
+ garbageCollectorMBeans = null;
+ // Set connection state to DISCONNECTED
+ if (!isDead) {
+ isDead = true;
+ setConnectionState(ConnectionState.DISCONNECTED);
+ }
+ }
+
+ /**
+ * Returns the list of domains in which any MBean is
+ * currently registered.
+ */
+ public String[] getDomains() throws IOException {
+ return server.getDomains();
+ }
+
+ /**
+ * Returns a map of MBeans with ObjectName as the key and MBeanInfo value
+ * of a given domain. If domain is <tt>null</tt>, all MBeans
+ * are returned. If no MBean found, an empty map is returned.
+ *
+ */
+ public Map<ObjectName, MBeanInfo> getMBeans(String domain)
+ throws IOException {
+
+ ObjectName name = null;
+ if (domain != null) {
+ try {
+ name = new ObjectName(domain + ":*");
+ } catch (MalformedObjectNameException e) {
+ // should not reach here
+ assert(false);
+ }
+ }
+ Set mbeans = server.queryNames(name, null);
+ Map<ObjectName,MBeanInfo> result =
+ new HashMap<ObjectName,MBeanInfo>(mbeans.size());
+ Iterator iterator = mbeans.iterator();
+ while (iterator.hasNext()) {
+ Object object = iterator.next();
+ if (object instanceof ObjectName) {
+ ObjectName o = (ObjectName)object;
+ try {
+ MBeanInfo info = server.getMBeanInfo(o);
+ result.put(o, info);
+ } catch (IntrospectionException e) {
+ // TODO: should log the error
+ } catch (InstanceNotFoundException e) {
+ // TODO: should log the error
+ } catch (ReflectionException e) {
+ // TODO: should log the error
+ }
+ }
+ }
+ return result;
+ }
+
+ /**
+ * Returns a list of attributes of a named MBean.
+ *
+ */
+ public AttributeList getAttributes(ObjectName name, String[] attributes)
+ throws IOException {
+ AttributeList list = null;
+ try {
+ list = server.getAttributes(name, attributes);
+ } catch (InstanceNotFoundException e) {
+ // TODO: A MBean may have been unregistered.
+ // need to set up listener to listen for MBeanServerNotification.
+ } catch (ReflectionException e) {
+ // TODO: should log the error
+ }
+ return list;
+ }
+
+ /**
+ * Set the value of a specific attribute of a named MBean.
+ */
+ public void setAttribute(ObjectName name, Attribute attribute)
+ throws InvalidAttributeValueException,
+ MBeanException,
+ IOException {
+ try {
+ server.setAttribute(name, attribute);
+ } catch (InstanceNotFoundException e) {
+ // TODO: A MBean may have been unregistered.
+ } catch (AttributeNotFoundException e) {
+ assert(false);
+ } catch (ReflectionException e) {
+ // TODO: should log the error
+ }
+ }
+
+ /**
+ * Invokes an operation of a named MBean.
+ *
+ * @throws MBeanException Wraps an exception thrown by
+ * the MBean's invoked method.
+ */
+ public Object invoke(ObjectName name, String operationName,
+ Object[] params, String[] signature)
+ throws IOException, MBeanException {
+ Object result = null;
+ try {
+ result = server.invoke(name, operationName, params, signature);
+ } catch (InstanceNotFoundException e) {
+ // TODO: A MBean may have been unregistered.
+ } catch (ReflectionException e) {
+ // TODO: should log the error
+ }
+ return result;
+ }
+
+ public synchronized ClassLoadingMXBean getClassLoadingMXBean() throws IOException {
+ if (hasPlatformMXBeans && classLoadingMBean == null) {
+ classLoadingMBean =
+ newPlatformMXBeanProxy(server, CLASS_LOADING_MXBEAN_NAME,
+ ClassLoadingMXBean.class);
+ }
+ return classLoadingMBean;
+ }
+
+ public synchronized CompilationMXBean getCompilationMXBean() throws IOException {
+ if (hasCompilationMXBean && compilationMBean == null) {
+ compilationMBean =
+ newPlatformMXBeanProxy(server, COMPILATION_MXBEAN_NAME,
+ CompilationMXBean.class);
+ }
+ return compilationMBean;
+ }
+
+ public Collection<MemoryPoolProxy> getMemoryPoolProxies()
+ throws IOException {
+
+ // TODO: How to deal with changes to the list??
+ if (memoryPoolProxies == null) {
+ ObjectName poolName = null;
+ try {
+ poolName = new ObjectName(MEMORY_POOL_MXBEAN_DOMAIN_TYPE + ",*");
+ } catch (MalformedObjectNameException e) {
+ // should not reach here
+ assert(false);
+ }
+ Set mbeans = server.queryNames(poolName, null);
+ if (mbeans != null) {
+ memoryPoolProxies = new ArrayList<MemoryPoolProxy>();
+ Iterator iterator = mbeans.iterator();
+ while (iterator.hasNext()) {
+ ObjectName objName = (ObjectName) iterator.next();
+ MemoryPoolProxy p = new MemoryPoolProxy(this, objName);
+ memoryPoolProxies.add(p);
+ }
+ }
+ }
+ return memoryPoolProxies;
+ }
+
+ public synchronized Collection<GarbageCollectorMXBean> getGarbageCollectorMXBeans()
+ throws IOException {
+
+ // TODO: How to deal with changes to the list??
+ if (garbageCollectorMBeans == null) {
+ ObjectName gcName = null;
+ try {
+ gcName = new ObjectName(GARBAGE_COLLECTOR_MXBEAN_DOMAIN_TYPE + ",*");
+ } catch (MalformedObjectNameException e) {
+ // should not reach here
+ assert(false);
+ }
+ Set mbeans = server.queryNames(gcName, null);
+ if (mbeans != null) {
+ garbageCollectorMBeans = new ArrayList<GarbageCollectorMXBean>();
+ Iterator iterator = mbeans.iterator();
+ while (iterator.hasNext()) {
+ ObjectName on = (ObjectName) iterator.next();
+ String name = GARBAGE_COLLECTOR_MXBEAN_DOMAIN_TYPE +
+ ",name=" + on.getKeyProperty("name");
+
+ GarbageCollectorMXBean mBean =
+ newPlatformMXBeanProxy(server, name,
+ GarbageCollectorMXBean.class);
+ garbageCollectorMBeans.add(mBean);
+ }
+ }
+ }
+ return garbageCollectorMBeans;
+ }
+
+ public synchronized MemoryMXBean getMemoryMXBean() throws IOException {
+ if (hasPlatformMXBeans && memoryMBean == null) {
+ memoryMBean =
+ newPlatformMXBeanProxy(server, MEMORY_MXBEAN_NAME,
+ MemoryMXBean.class);
+ }
+ return memoryMBean;
+ }
+
+ public synchronized RuntimeMXBean getRuntimeMXBean() throws IOException {
+ if (hasPlatformMXBeans && runtimeMBean == null) {
+ runtimeMBean =
+ newPlatformMXBeanProxy(server, RUNTIME_MXBEAN_NAME,
+ RuntimeMXBean.class);
+ }
+ return runtimeMBean;
+ }
+
+
+ public synchronized ThreadMXBean getThreadMXBean() throws IOException {
+ if (hasPlatformMXBeans && threadMBean == null) {
+ threadMBean =
+ newPlatformMXBeanProxy(server, THREAD_MXBEAN_NAME,
+ ThreadMXBean.class);
+ }
+ return threadMBean;
+ }
+
+ public synchronized OperatingSystemMXBean getOperatingSystemMXBean() throws IOException {
+ if (hasPlatformMXBeans && operatingSystemMBean == null) {
+ operatingSystemMBean =
+ newPlatformMXBeanProxy(server, OPERATING_SYSTEM_MXBEAN_NAME,
+ OperatingSystemMXBean.class);
+ }
+ return operatingSystemMBean;
+ }
+
+ public synchronized com.sun.management.OperatingSystemMXBean
+ getSunOperatingSystemMXBean() throws IOException {
+
+ try {
+ ObjectName on = new ObjectName(OPERATING_SYSTEM_MXBEAN_NAME);
+ if (sunOperatingSystemMXBean == null) {
+ if (server.isInstanceOf(on,
+ "com.sun.management.OperatingSystemMXBean")) {
+ sunOperatingSystemMXBean =
+ newPlatformMXBeanProxy(server,
+ OPERATING_SYSTEM_MXBEAN_NAME,
+ com.sun.management.OperatingSystemMXBean.class);
+ }
+ }
+ } catch (InstanceNotFoundException e) {
+ return null;
+ } catch (MalformedObjectNameException e) {
+ return null; // should never reach here
+ }
+ return sunOperatingSystemMXBean;
+ }
+
+ public synchronized HotSpotDiagnosticMXBean getHotSpotDiagnosticMXBean() throws IOException {
+ if (hasHotSpotDiagnosticMXBean && hotspotDiagnosticMXBean == null) {
+ hotspotDiagnosticMXBean =
+ newPlatformMXBeanProxy(server, HOTSPOT_DIAGNOSTIC_MXBEAN_NAME,
+ HotSpotDiagnosticMXBean.class);
+ }
+ return hotspotDiagnosticMXBean;
+ }
+
+ public <T> T getMXBean(ObjectName objName, Class<T> interfaceClass)
+ throws IOException {
+ return newPlatformMXBeanProxy(server,
+ objName.toString(),
+ interfaceClass);
+
+ }
+
+ // Return thread IDs of deadlocked threads or null if any.
+ // It finds deadlocks involving only monitors if it's a Tiger VM.
+ // Otherwise, it finds deadlocks involving both monitors and
+ // the concurrent locks.
+ public long[] findDeadlockedThreads() throws IOException {
+ ThreadMXBean tm = getThreadMXBean();
+ if (supportsLockUsage && tm.isSynchronizerUsageSupported()) {
+ return tm.findDeadlockedThreads();
+ } else {
+ return tm.findMonitorDeadlockedThreads();
+ }
+ }
+
+ public synchronized void markAsDead() {
+ disconnect();
+ }
+
+ public boolean isDead() {
+ return isDead;
+ }
+
+ boolean isConnected() {
+ return !isDead();
+ }
+
+ boolean hasPlatformMXBeans() {
+ return this.hasPlatformMXBeans;
+ }
+
+ boolean hasHotSpotDiagnosticMXBean() {
+ return this.hasHotSpotDiagnosticMXBean;
+ }
+
+ boolean isLockUsageSupported() {
+ return supportsLockUsage;
+ }
+
+ public boolean isRegistered(ObjectName name) throws IOException {
+ return server.isRegistered(name);
+ }
+
+ public void addPropertyChangeListener(PropertyChangeListener listener) {
+ propertyChangeSupport.addPropertyChangeListener(listener);
+ }
+
+ public void addWeakPropertyChangeListener(PropertyChangeListener listener) {
+ if (!(listener instanceof WeakPCL)) {
+ listener = new WeakPCL(listener);
+ }
+ propertyChangeSupport.addPropertyChangeListener(listener);
+ }
+
+ public void removePropertyChangeListener(PropertyChangeListener listener) {
+ if (!(listener instanceof WeakPCL)) {
+ // Search for the WeakPCL holding this listener (if any)
+ for (PropertyChangeListener pcl : propertyChangeSupport.getPropertyChangeListeners()) {
+ if (pcl instanceof WeakPCL && ((WeakPCL)pcl).get() == listener) {
+ listener = pcl;
+ break;
+ }
+ }
+ }
+ propertyChangeSupport.removePropertyChangeListener(listener);
+ }
+
+ /**
+ * The PropertyChangeListener is handled via a WeakReference
+ * so as not to pin down the listener.
+ */
+ private class WeakPCL extends WeakReference<PropertyChangeListener>
+ implements PropertyChangeListener {
+ WeakPCL(PropertyChangeListener referent) {
+ super(referent);
+ }
+
+ public void propertyChange(PropertyChangeEvent pce) {
+ PropertyChangeListener pcl = get();
+
+ if (pcl == null) {
+ // The referent listener was GC'ed, we're no longer
+ // interested in PropertyChanges, remove the listener.
+ dispose();
+ } else {
+ pcl.propertyChange(pce);
+ }
+ }
+
+ private void dispose() {
+ removePropertyChangeListener(this);
+ }
+ }
+
+ //
+ // Snapshot MBeanServerConnection:
+ //
+ // This is an object that wraps an existing MBeanServerConnection and adds
+ // caching to it, as follows:
+ //
+ // - The first time an attribute is called in a given MBean, the result is
+ // cached. Every subsequent time getAttribute is called for that attribute
+ // the cached result is returned.
+ //
+ // - Before every call to VMPanel.update() or when the Refresh button in the
+ // Attributes table is pressed down the attributes cache is flushed. Then
+ // any subsequent call to getAttribute will retrieve all the values for
+ // the attributes that are known to the cache.
+ //
+ // - The attributes cache uses a learning approach and only the attributes
+ // that are in the cache will be retrieved between two subsequent updates.
+ //
+
+ public interface SnapshotMBeanServerConnection
+ extends MBeanServerConnection {
+ /**
+ * Flush all cached values of attributes.
+ */
+ public void flush();
+ }
+
+ public static class Snapshot {
+ private Snapshot() {
+ }
+ public static SnapshotMBeanServerConnection
+ newSnapshot(MBeanServerConnection mbsc) {
+ final InvocationHandler ih = new SnapshotInvocationHandler(mbsc);
+ return (SnapshotMBeanServerConnection) Proxy.newProxyInstance(
+ Snapshot.class.getClassLoader(),
+ new Class[] {SnapshotMBeanServerConnection.class},
+ ih);
+ }
+ }
+
+ static class SnapshotInvocationHandler implements InvocationHandler {
+
+ private final MBeanServerConnection conn;
+ private Map<ObjectName, NameValueMap> cachedValues = newMap();
+ private Map<ObjectName, Set<String>> cachedNames = newMap();
+
+ @SuppressWarnings("serial")
+ private static final class NameValueMap
+ extends HashMap<String, Object> {}
+
+ SnapshotInvocationHandler(MBeanServerConnection conn) {
+ this.conn = conn;
+ }
+
+ synchronized void flush() {
+ cachedValues = newMap();
+ }
+
+ public Object invoke(Object proxy, Method method, Object[] args)
+ throws Throwable {
+ final String methodName = method.getName();
+ if (methodName.equals("getAttribute")) {
+ return getAttribute((ObjectName) args[0], (String) args[1]);
+ } else if (methodName.equals("getAttributes")) {
+ return getAttributes((ObjectName) args[0], (String[]) args[1]);
+ } else if (methodName.equals("flush")) {
+ flush();
+ return null;
+ } else {
+ try {
+ return method.invoke(conn, args);
+ } catch (InvocationTargetException e) {
+ throw e.getCause();
+ }
+ }
+ }
+
+ private Object getAttribute(ObjectName objName, String attrName)
+ throws MBeanException, InstanceNotFoundException,
+ AttributeNotFoundException, ReflectionException, IOException {
+ final NameValueMap values = getCachedAttributes(
+ objName, Collections.singleton(attrName));
+ Object value = values.get(attrName);
+ if (value != null || values.containsKey(attrName)) {
+ return value;
+ }
+ // Not in cache, presumably because it was omitted from the
+ // getAttributes result because of an exception. Following
+ // call will probably provoke the same exception.
+ return conn.getAttribute(objName, attrName);
+ }
+
+ private AttributeList getAttributes(
+ ObjectName objName, String[] attrNames) throws
+ InstanceNotFoundException, ReflectionException, IOException {
+ final NameValueMap values = getCachedAttributes(
+ objName,
+ new TreeSet<String>(Arrays.asList(attrNames)));
+ final AttributeList list = new AttributeList();
+ for (String attrName : attrNames) {
+ final Object value = values.get(attrName);
+ if (value != null || values.containsKey(attrName)) {
+ list.add(new Attribute(attrName, value));
+ }
+ }
+ return list;
+ }
+
+ private synchronized NameValueMap getCachedAttributes(
+ ObjectName objName, Set<String> attrNames) throws
+ InstanceNotFoundException, ReflectionException, IOException {
+ NameValueMap values = cachedValues.get(objName);
+ if (values != null && values.keySet().containsAll(attrNames)) {
+ return values;
+ }
+ attrNames = new TreeSet<String>(attrNames);
+ Set<String> oldNames = cachedNames.get(objName);
+ if (oldNames != null) {
+ attrNames.addAll(oldNames);
+ }
+ values = new NameValueMap();
+ final AttributeList attrs = conn.getAttributes(
+ objName,
+ attrNames.toArray(new String[attrNames.size()]));
+ for (Attribute attr : attrs.asList()) {
+ values.put(attr.getName(), attr.getValue());
+ }
+ cachedValues.put(objName, values);
+ cachedNames.put(objName, attrNames);
+ return values;
+ }
+
+ // See http://www.artima.com/weblogs/viewpost.jsp?thread=79394
+ private static <K, V> Map<K, V> newMap() {
+ return new HashMap<K, V>();
+ }
+ }
+}