Path: blob/master/src/jdk.jconsole/share/classes/sun/tools/jconsole/ProxyClient.java
40948 views
/*1* Copyright (c) 2004, 2014, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/2425package sun.tools.jconsole;2627import com.sun.management.HotSpotDiagnosticMXBean;28import com.sun.tools.jconsole.JConsoleContext;29import java.beans.PropertyChangeListener;30import java.beans.PropertyChangeEvent;31import java.io.IOException;32import java.lang.management.*;33import static java.lang.management.ManagementFactory.*;34import java.lang.ref.WeakReference;35import java.lang.reflect.*;36import java.rmi.*;37import java.rmi.registry.*;38import java.rmi.server.*;39import java.util.*;40import javax.management.*;41import javax.management.remote.*;42import javax.management.remote.rmi.*;43import javax.rmi.ssl.SslRMIClientSocketFactory;44import javax.swing.event.SwingPropertyChangeSupport;45import sun.rmi.server.UnicastRef2;46import sun.rmi.transport.LiveRef;4748public class ProxyClient implements JConsoleContext {4950private ConnectionState connectionState = ConnectionState.DISCONNECTED;5152// The SwingPropertyChangeSupport will fire events on the EDT53private SwingPropertyChangeSupport propertyChangeSupport =54new SwingPropertyChangeSupport(this, true);5556private static Map<String, ProxyClient> cache =57Collections.synchronizedMap(new HashMap<String, ProxyClient>());5859private volatile boolean isDead = true;60private String hostName = null;61private int port = 0;62private String userName = null;63private String password = null;64private boolean hasPlatformMXBeans = false;65private boolean hasHotSpotDiagnosticMXBean= false;66private boolean hasCompilationMXBean = false;67private boolean supportsLockUsage = false;6869// REVISIT: VMPanel and other places relying using getUrl().7071// set only if it's created for local monitoring72private LocalVirtualMachine lvm;7374// set only if it's created from a given URL via the Advanced tab75private String advancedUrl = null;7677private JMXServiceURL jmxUrl = null;78private MBeanServerConnection mbsc = null;79private SnapshotMBeanServerConnection server = null;80private JMXConnector jmxc = null;81private RMIServer stub = null;82private static final SslRMIClientSocketFactory sslRMIClientSocketFactory =83new SslRMIClientSocketFactory();84private String registryHostName = null;85private int registryPort = 0;86private boolean vmConnector = false;87private boolean sslRegistry = false;88private boolean sslStub = false;89private final String connectionName;90private final String displayName;9192private ClassLoadingMXBean classLoadingMBean = null;93private CompilationMXBean compilationMBean = null;94private MemoryMXBean memoryMBean = null;95private OperatingSystemMXBean operatingSystemMBean = null;96private RuntimeMXBean runtimeMBean = null;97private ThreadMXBean threadMBean = null;9899private com.sun.management.OperatingSystemMXBean sunOperatingSystemMXBean = null;100private HotSpotDiagnosticMXBean hotspotDiagnosticMXBean = null;101102private List<MemoryPoolProxy> memoryPoolProxies = null;103private List<GarbageCollectorMXBean> garbageCollectorMBeans = null;104105private static final String HOTSPOT_DIAGNOSTIC_MXBEAN_NAME =106"com.sun.management:type=HotSpotDiagnostic";107108private ProxyClient(String hostName, int port,109String userName, String password) throws IOException {110this.connectionName = getConnectionName(hostName, port, userName);111this.displayName = connectionName;112if (hostName.equals("localhost") && port == 0) {113// Monitor self114this.hostName = hostName;115this.port = port;116} else {117// Create an RMI connector client and connect it to118// the RMI connector server119final String urlPath = "/jndi/rmi://" + hostName + ":" + port +120"/jmxrmi";121JMXServiceURL url = new JMXServiceURL("rmi", "", 0, urlPath);122setParameters(url, userName, password);123vmConnector = true;124registryHostName = hostName;125registryPort = port;126checkSslConfig();127}128}129130private ProxyClient(String url,131String userName, String password) throws IOException {132this.advancedUrl = url;133this.connectionName = getConnectionName(url, userName);134this.displayName = connectionName;135setParameters(new JMXServiceURL(url), userName, password);136}137138private ProxyClient(LocalVirtualMachine lvm) throws IOException {139this.lvm = lvm;140this.connectionName = getConnectionName(lvm);141this.displayName = "pid: " + lvm.vmid() + " " + lvm.displayName();142}143144private void setParameters(JMXServiceURL url, String userName, String password) {145this.jmxUrl = url;146this.hostName = jmxUrl.getHost();147this.port = jmxUrl.getPort();148this.userName = userName;149this.password = password;150}151152private static void checkStub(Remote stub,153Class<? extends Remote> stubClass) {154// Check remote stub is from the expected class.155//156if (stub.getClass() != stubClass) {157if (!Proxy.isProxyClass(stub.getClass())) {158throw new SecurityException(159"Expecting a " + stubClass.getName() + " stub!");160} else {161InvocationHandler handler = Proxy.getInvocationHandler(stub);162if (handler.getClass() != RemoteObjectInvocationHandler.class) {163throw new SecurityException(164"Expecting a dynamic proxy instance with a " +165RemoteObjectInvocationHandler.class.getName() +166" invocation handler!");167} else {168stub = (Remote) handler;169}170}171}172// Check RemoteRef in stub is from the expected class173// "sun.rmi.server.UnicastRef2".174//175RemoteRef ref = ((RemoteObject)stub).getRef();176if (ref.getClass() != UnicastRef2.class) {177throw new SecurityException(178"Expecting a " + UnicastRef2.class.getName() +179" remote reference in stub!");180}181// Check RMIClientSocketFactory in stub is from the expected class182// "javax.rmi.ssl.SslRMIClientSocketFactory".183//184LiveRef liveRef = ((UnicastRef2)ref).getLiveRef();185RMIClientSocketFactory csf = liveRef.getClientSocketFactory();186if (csf == null || csf.getClass() != SslRMIClientSocketFactory.class) {187throw new SecurityException(188"Expecting a " + SslRMIClientSocketFactory.class.getName() +189" RMI client socket factory in stub!");190}191}192193private static final String rmiServerImplStubClassName =194"javax.management.remote.rmi.RMIServerImpl_Stub";195private static final Class<? extends Remote> rmiServerImplStubClass;196197static {198// FIXME: RMIServerImpl_Stub is generated at build time199// after jconsole is built. We need to investigate if200// the Makefile can be fixed to build jconsole in the201// right order. As a workaround for now, we dynamically202// load RMIServerImpl_Stub class instead of statically203// referencing it.204Class<? extends Remote> serverStubClass = null;205try {206serverStubClass = Class.forName(rmiServerImplStubClassName).asSubclass(Remote.class);207} catch (ClassNotFoundException e) {208// should never reach here209throw new InternalError(e.getMessage(), e);210}211rmiServerImplStubClass = serverStubClass;212}213214private void checkSslConfig() throws IOException {215// Get the reference to the RMI Registry and lookup RMIServer stub216//217Registry registry;218try {219registry =220LocateRegistry.getRegistry(registryHostName, registryPort,221sslRMIClientSocketFactory);222try {223stub = (RMIServer) registry.lookup("jmxrmi");224} catch (NotBoundException nbe) {225throw (IOException)226new IOException(nbe.getMessage()).initCause(nbe);227}228sslRegistry = true;229} catch (IOException e) {230registry =231LocateRegistry.getRegistry(registryHostName, registryPort);232try {233stub = (RMIServer) registry.lookup("jmxrmi");234} catch (NotBoundException nbe) {235throw (IOException)236new IOException(nbe.getMessage()).initCause(nbe);237}238sslRegistry = false;239}240// Perform the checks for secure stub241//242try {243checkStub(stub, rmiServerImplStubClass);244sslStub = true;245} catch (SecurityException e) {246sslStub = false;247}248}249250/**251* Returns true if the underlying RMI registry is SSL-protected.252*253* @exception UnsupportedOperationException If this {@code ProxyClient}254* does not denote a JMX connector for a JMX VM agent.255*/256public boolean isSslRmiRegistry() {257// Check for VM connector258//259if (!isVmConnector()) {260throw new UnsupportedOperationException(261"ProxyClient.isSslRmiRegistry() is only supported if this " +262"ProxyClient is a JMX connector for a JMX VM agent");263}264return sslRegistry;265}266267/**268* Returns true if the retrieved RMI stub is SSL-protected.269*270* @exception UnsupportedOperationException If this {@code ProxyClient}271* does not denote a JMX connector for a JMX VM agent.272*/273public boolean isSslRmiStub() {274// Check for VM connector275//276if (!isVmConnector()) {277throw new UnsupportedOperationException(278"ProxyClient.isSslRmiStub() is only supported if this " +279"ProxyClient is a JMX connector for a JMX VM agent");280}281return sslStub;282}283284/**285* Returns true if this {@code ProxyClient} denotes286* a JMX connector for a JMX VM agent.287*/288public boolean isVmConnector() {289return vmConnector;290}291292private void setConnectionState(ConnectionState state) {293ConnectionState oldState = this.connectionState;294this.connectionState = state;295propertyChangeSupport.firePropertyChange(CONNECTION_STATE_PROPERTY,296oldState, state);297}298299public ConnectionState getConnectionState() {300return this.connectionState;301}302303void flush() {304if (server != null) {305server.flush();306}307}308309void connect(boolean requireSSL) {310setConnectionState(ConnectionState.CONNECTING);311try {312tryConnect(requireSSL);313setConnectionState(ConnectionState.CONNECTED);314} catch (Exception e) {315if (JConsole.isDebug()) {316e.printStackTrace();317}318setConnectionState(ConnectionState.DISCONNECTED);319}320}321322private void tryConnect(boolean requireRemoteSSL) throws IOException {323if (jmxUrl == null && "localhost".equals(hostName) && port == 0) {324// Monitor self325this.jmxc = null;326this.mbsc = ManagementFactory.getPlatformMBeanServer();327this.server = Snapshot.newSnapshot(mbsc);328} else {329// Monitor another process330if (lvm != null) {331if (!lvm.isManageable()) {332lvm.startManagementAgent();333if (!lvm.isManageable()) {334// FIXME: what to throw335throw new IOException(lvm + "not manageable");336}337}338if (this.jmxUrl == null) {339this.jmxUrl = new JMXServiceURL(lvm.connectorAddress());340}341}342Map<String, Object> env = new HashMap<String, Object>();343if (requireRemoteSSL) {344env.put("jmx.remote.x.check.stub", "true");345}346// Need to pass in credentials ?347if (userName == null && password == null) {348if (isVmConnector()) {349// Check for SSL config on reconnection only350if (stub == null) {351checkSslConfig();352}353this.jmxc = new RMIConnector(stub, null);354jmxc.connect(env);355} else {356this.jmxc = JMXConnectorFactory.connect(jmxUrl, env);357}358} else {359env.put(JMXConnector.CREDENTIALS,360new String[] {userName, password});361if (isVmConnector()) {362// Check for SSL config on reconnection only363if (stub == null) {364checkSslConfig();365}366this.jmxc = new RMIConnector(stub, null);367jmxc.connect(env);368} else {369this.jmxc = JMXConnectorFactory.connect(jmxUrl, env);370}371}372this.mbsc = jmxc.getMBeanServerConnection();373this.server = Snapshot.newSnapshot(mbsc);374}375this.isDead = false;376377try {378ObjectName on = new ObjectName(THREAD_MXBEAN_NAME);379this.hasPlatformMXBeans = server.isRegistered(on);380this.hasHotSpotDiagnosticMXBean =381server.isRegistered(new ObjectName(HOTSPOT_DIAGNOSTIC_MXBEAN_NAME));382// check if it has 6.0 new APIs383if (this.hasPlatformMXBeans) {384MBeanOperationInfo[] mopis = server.getMBeanInfo(on).getOperations();385// look for findDeadlockedThreads operations;386for (MBeanOperationInfo op : mopis) {387if (op.getName().equals("findDeadlockedThreads")) {388this.supportsLockUsage = true;389break;390}391}392393on = new ObjectName(COMPILATION_MXBEAN_NAME);394this.hasCompilationMXBean = server.isRegistered(on);395}396} catch (MalformedObjectNameException e) {397// should not reach here398throw new InternalError(e.getMessage());399} catch (IntrospectionException |400InstanceNotFoundException |401ReflectionException e) {402throw new InternalError(e.getMessage(), e);403}404405if (hasPlatformMXBeans) {406// WORKAROUND for bug 5056632407// Check if the access role is correct by getting a RuntimeMXBean408getRuntimeMXBean();409}410}411412/**413* Gets a proxy client for a given local virtual machine.414*/415public static ProxyClient getProxyClient(LocalVirtualMachine lvm)416throws IOException {417final String key = getCacheKey(lvm);418ProxyClient proxyClient = cache.get(key);419if (proxyClient == null) {420proxyClient = new ProxyClient(lvm);421cache.put(key, proxyClient);422}423return proxyClient;424}425426public static String getConnectionName(LocalVirtualMachine lvm) {427return Integer.toString(lvm.vmid());428}429430private static String getCacheKey(LocalVirtualMachine lvm) {431return Integer.toString(lvm.vmid());432}433434/**435* Gets a proxy client for a given JMXServiceURL.436*/437public static ProxyClient getProxyClient(String url,438String userName, String password)439throws IOException {440final String key = getCacheKey(url, userName, password);441ProxyClient proxyClient = cache.get(key);442if (proxyClient == null) {443proxyClient = new ProxyClient(url, userName, password);444cache.put(key, proxyClient);445}446return proxyClient;447}448449public static String getConnectionName(String url,450String userName) {451if (userName != null && userName.length() > 0) {452return userName + "@" + url;453} else {454return url;455}456}457458private static String getCacheKey(String url,459String userName, String password) {460return (url == null ? "" : url) + ":" +461(userName == null ? "" : userName) + ":" +462(password == null ? "" : password);463}464465/**466* Gets a proxy client for a given "hostname:port".467*/468public static ProxyClient getProxyClient(String hostName, int port,469String userName, String password)470throws IOException {471final String key = getCacheKey(hostName, port, userName, password);472ProxyClient proxyClient = cache.get(key);473if (proxyClient == null) {474proxyClient = new ProxyClient(hostName, port, userName, password);475cache.put(key, proxyClient);476}477return proxyClient;478}479480public static String getConnectionName(String hostName, int port,481String userName) {482String name = hostName + ":" + port;483if (userName != null && userName.length() > 0) {484return userName + "@" + name;485} else {486return name;487}488}489490private static String getCacheKey(String hostName, int port,491String userName, String password) {492return (hostName == null ? "" : hostName) + ":" +493port + ":" +494(userName == null ? "" : userName) + ":" +495(password == null ? "" : password);496}497498public String connectionName() {499return connectionName;500}501502public String getDisplayName() {503return displayName;504}505506public String toString() {507if (!isConnected()) {508return Resources.format(Messages.CONNECTION_NAME__DISCONNECTED_, displayName);509} else {510return displayName;511}512}513514public MBeanServerConnection getMBeanServerConnection() {515return mbsc;516}517518public SnapshotMBeanServerConnection getSnapshotMBeanServerConnection() {519return server;520}521522public String getUrl() {523return advancedUrl;524}525526public String getHostName() {527return hostName;528}529530public int getPort() {531return port;532}533534public int getVmid() {535return (lvm != null) ? lvm.vmid() : 0;536}537538public String getUserName() {539return userName;540}541542public String getPassword() {543return password;544}545546public void disconnect() {547// Reset remote stub548stub = null;549// Close MBeanServer connection550if (jmxc != null) {551try {552jmxc.close();553} catch (IOException e) {554// Ignore ???555}556}557// Reset platform MBean references558classLoadingMBean = null;559compilationMBean = null;560memoryMBean = null;561operatingSystemMBean = null;562runtimeMBean = null;563threadMBean = null;564sunOperatingSystemMXBean = null;565garbageCollectorMBeans = null;566// Set connection state to DISCONNECTED567if (!isDead) {568isDead = true;569setConnectionState(ConnectionState.DISCONNECTED);570}571}572573/**574* Returns the list of domains in which any MBean is575* currently registered.576*/577public String[] getDomains() throws IOException {578return server.getDomains();579}580581/**582* Returns a map of MBeans with ObjectName as the key and MBeanInfo value583* of a given domain. If domain is {@code null}, all MBeans584* are returned. If no MBean found, an empty map is returned.585*586*/587public Map<ObjectName, MBeanInfo> getMBeans(String domain)588throws IOException {589590ObjectName name = null;591if (domain != null) {592try {593name = new ObjectName(domain + ":*");594} catch (MalformedObjectNameException e) {595// should not reach here596assert(false);597}598}599Set<ObjectName> mbeans = server.queryNames(name, null);600Map<ObjectName,MBeanInfo> result =601new HashMap<ObjectName,MBeanInfo>(mbeans.size());602Iterator<ObjectName> iterator = mbeans.iterator();603while (iterator.hasNext()) {604Object object = iterator.next();605if (object instanceof ObjectName) {606ObjectName o = (ObjectName)object;607try {608MBeanInfo info = server.getMBeanInfo(o);609result.put(o, info);610} catch (IntrospectionException e) {611// TODO: should log the error612} catch (InstanceNotFoundException e) {613// TODO: should log the error614} catch (ReflectionException e) {615// TODO: should log the error616}617}618}619return result;620}621622/**623* Returns a list of attributes of a named MBean.624*625*/626public AttributeList getAttributes(ObjectName name, String[] attributes)627throws IOException {628AttributeList list = null;629try {630list = server.getAttributes(name, attributes);631} catch (InstanceNotFoundException e) {632// TODO: A MBean may have been unregistered.633// need to set up listener to listen for MBeanServerNotification.634} catch (ReflectionException e) {635// TODO: should log the error636}637return list;638}639640/**641* Set the value of a specific attribute of a named MBean.642*/643public void setAttribute(ObjectName name, Attribute attribute)644throws InvalidAttributeValueException,645MBeanException,646IOException {647try {648server.setAttribute(name, attribute);649} catch (InstanceNotFoundException e) {650// TODO: A MBean may have been unregistered.651} catch (AttributeNotFoundException e) {652assert(false);653} catch (ReflectionException e) {654// TODO: should log the error655}656}657658/**659* Invokes an operation of a named MBean.660*661* @throws MBeanException Wraps an exception thrown by662* the MBean's invoked method.663*/664public Object invoke(ObjectName name, String operationName,665Object[] params, String[] signature)666throws IOException, MBeanException {667Object result = null;668try {669result = server.invoke(name, operationName, params, signature);670} catch (InstanceNotFoundException e) {671// TODO: A MBean may have been unregistered.672} catch (ReflectionException e) {673// TODO: should log the error674}675return result;676}677678public synchronized ClassLoadingMXBean getClassLoadingMXBean() throws IOException {679if (hasPlatformMXBeans && classLoadingMBean == null) {680classLoadingMBean =681newPlatformMXBeanProxy(server, CLASS_LOADING_MXBEAN_NAME,682ClassLoadingMXBean.class);683}684return classLoadingMBean;685}686687public synchronized CompilationMXBean getCompilationMXBean() throws IOException {688if (hasCompilationMXBean && compilationMBean == null) {689compilationMBean =690newPlatformMXBeanProxy(server, COMPILATION_MXBEAN_NAME,691CompilationMXBean.class);692}693return compilationMBean;694}695696public Collection<MemoryPoolProxy> getMemoryPoolProxies()697throws IOException {698699// TODO: How to deal with changes to the list??700if (memoryPoolProxies == null) {701ObjectName poolName = null;702try {703poolName = new ObjectName(MEMORY_POOL_MXBEAN_DOMAIN_TYPE + ",*");704} catch (MalformedObjectNameException e) {705// should not reach here706assert(false);707}708Set<ObjectName> mbeans = server.queryNames(poolName, null);709if (mbeans != null) {710memoryPoolProxies = new ArrayList<MemoryPoolProxy>();711Iterator<ObjectName> iterator = mbeans.iterator();712while (iterator.hasNext()) {713ObjectName objName = iterator.next();714MemoryPoolProxy p = new MemoryPoolProxy(this, objName);715memoryPoolProxies.add(p);716}717}718}719return memoryPoolProxies;720}721722public synchronized Collection<GarbageCollectorMXBean> getGarbageCollectorMXBeans()723throws IOException {724725// TODO: How to deal with changes to the list??726if (garbageCollectorMBeans == null) {727ObjectName gcName = null;728try {729gcName = new ObjectName(GARBAGE_COLLECTOR_MXBEAN_DOMAIN_TYPE + ",*");730} catch (MalformedObjectNameException e) {731// should not reach here732assert(false);733}734Set<ObjectName> mbeans = server.queryNames(gcName, null);735if (mbeans != null) {736garbageCollectorMBeans = new ArrayList<GarbageCollectorMXBean>();737Iterator<ObjectName> iterator = mbeans.iterator();738while (iterator.hasNext()) {739ObjectName on = iterator.next();740String name = GARBAGE_COLLECTOR_MXBEAN_DOMAIN_TYPE +741",name=" + on.getKeyProperty("name");742743GarbageCollectorMXBean mBean =744newPlatformMXBeanProxy(server, name,745GarbageCollectorMXBean.class);746garbageCollectorMBeans.add(mBean);747}748}749}750return garbageCollectorMBeans;751}752753public synchronized MemoryMXBean getMemoryMXBean() throws IOException {754if (hasPlatformMXBeans && memoryMBean == null) {755memoryMBean =756newPlatformMXBeanProxy(server, MEMORY_MXBEAN_NAME,757MemoryMXBean.class);758}759return memoryMBean;760}761762public synchronized RuntimeMXBean getRuntimeMXBean() throws IOException {763if (hasPlatformMXBeans && runtimeMBean == null) {764runtimeMBean =765newPlatformMXBeanProxy(server, RUNTIME_MXBEAN_NAME,766RuntimeMXBean.class);767}768return runtimeMBean;769}770771772public synchronized ThreadMXBean getThreadMXBean() throws IOException {773if (hasPlatformMXBeans && threadMBean == null) {774threadMBean =775newPlatformMXBeanProxy(server, THREAD_MXBEAN_NAME,776ThreadMXBean.class);777}778return threadMBean;779}780781public synchronized OperatingSystemMXBean getOperatingSystemMXBean() throws IOException {782if (hasPlatformMXBeans && operatingSystemMBean == null) {783operatingSystemMBean =784newPlatformMXBeanProxy(server, OPERATING_SYSTEM_MXBEAN_NAME,785OperatingSystemMXBean.class);786}787return operatingSystemMBean;788}789790public synchronized com.sun.management.OperatingSystemMXBean791getSunOperatingSystemMXBean() throws IOException {792793try {794ObjectName on = new ObjectName(OPERATING_SYSTEM_MXBEAN_NAME);795if (sunOperatingSystemMXBean == null) {796if (server.isInstanceOf(on,797"com.sun.management.OperatingSystemMXBean")) {798sunOperatingSystemMXBean =799newPlatformMXBeanProxy(server,800OPERATING_SYSTEM_MXBEAN_NAME,801com.sun.management.OperatingSystemMXBean.class);802}803}804} catch (InstanceNotFoundException e) {805return null;806} catch (MalformedObjectNameException e) {807return null; // should never reach here808}809return sunOperatingSystemMXBean;810}811812public synchronized HotSpotDiagnosticMXBean getHotSpotDiagnosticMXBean() throws IOException {813if (hasHotSpotDiagnosticMXBean && hotspotDiagnosticMXBean == null) {814hotspotDiagnosticMXBean =815newPlatformMXBeanProxy(server, HOTSPOT_DIAGNOSTIC_MXBEAN_NAME,816HotSpotDiagnosticMXBean.class);817}818return hotspotDiagnosticMXBean;819}820821public <T> T getMXBean(ObjectName objName, Class<T> interfaceClass)822throws IOException {823return newPlatformMXBeanProxy(server,824objName.toString(),825interfaceClass);826827}828829// Return thread IDs of deadlocked threads or null if any.830// It finds deadlocks involving only monitors if it's a Tiger VM.831// Otherwise, it finds deadlocks involving both monitors and832// the concurrent locks.833public long[] findDeadlockedThreads() throws IOException {834ThreadMXBean tm = getThreadMXBean();835if (supportsLockUsage && tm.isSynchronizerUsageSupported()) {836return tm.findDeadlockedThreads();837} else {838return tm.findMonitorDeadlockedThreads();839}840}841842public synchronized void markAsDead() {843disconnect();844}845846public boolean isDead() {847return isDead;848}849850boolean isConnected() {851return !isDead();852}853854boolean hasPlatformMXBeans() {855return this.hasPlatformMXBeans;856}857858boolean hasHotSpotDiagnosticMXBean() {859return this.hasHotSpotDiagnosticMXBean;860}861862boolean isLockUsageSupported() {863return supportsLockUsage;864}865866public boolean isRegistered(ObjectName name) throws IOException {867return server.isRegistered(name);868}869870public void addPropertyChangeListener(PropertyChangeListener listener) {871propertyChangeSupport.addPropertyChangeListener(listener);872}873874public void addWeakPropertyChangeListener(PropertyChangeListener listener) {875if (!(listener instanceof WeakPCL)) {876listener = new WeakPCL(listener);877}878propertyChangeSupport.addPropertyChangeListener(listener);879}880881public void removePropertyChangeListener(PropertyChangeListener listener) {882if (!(listener instanceof WeakPCL)) {883// Search for the WeakPCL holding this listener (if any)884for (PropertyChangeListener pcl : propertyChangeSupport.getPropertyChangeListeners()) {885if (pcl instanceof WeakPCL && ((WeakPCL)pcl).get() == listener) {886listener = pcl;887break;888}889}890}891propertyChangeSupport.removePropertyChangeListener(listener);892}893894/**895* The PropertyChangeListener is handled via a WeakReference896* so as not to pin down the listener.897*/898private class WeakPCL extends WeakReference<PropertyChangeListener>899implements PropertyChangeListener {900WeakPCL(PropertyChangeListener referent) {901super(referent);902}903904public void propertyChange(PropertyChangeEvent pce) {905PropertyChangeListener pcl = get();906907if (pcl == null) {908// The referent listener was GC'ed, we're no longer909// interested in PropertyChanges, remove the listener.910dispose();911} else {912pcl.propertyChange(pce);913}914}915916private void dispose() {917removePropertyChangeListener(this);918}919}920921//922// Snapshot MBeanServerConnection:923//924// This is an object that wraps an existing MBeanServerConnection and adds925// caching to it, as follows:926//927// - The first time an attribute is called in a given MBean, the result is928// cached. Every subsequent time getAttribute is called for that attribute929// the cached result is returned.930//931// - Before every call to VMPanel.update() or when the Refresh button in the932// Attributes table is pressed down the attributes cache is flushed. Then933// any subsequent call to getAttribute will retrieve all the values for934// the attributes that are known to the cache.935//936// - The attributes cache uses a learning approach and only the attributes937// that are in the cache will be retrieved between two subsequent updates.938//939940public interface SnapshotMBeanServerConnection941extends MBeanServerConnection {942/**943* Flush all cached values of attributes.944*/945public void flush();946}947948public static class Snapshot {949private Snapshot() {950}951public static SnapshotMBeanServerConnection952newSnapshot(MBeanServerConnection mbsc) {953final InvocationHandler ih = new SnapshotInvocationHandler(mbsc);954return (SnapshotMBeanServerConnection) Proxy.newProxyInstance(955Snapshot.class.getClassLoader(),956new Class<?>[] {SnapshotMBeanServerConnection.class},957ih);958}959}960961static class SnapshotInvocationHandler implements InvocationHandler {962963private final MBeanServerConnection conn;964private Map<ObjectName, NameValueMap> cachedValues = newMap();965private Map<ObjectName, Set<String>> cachedNames = newMap();966967@SuppressWarnings("serial")968private static final class NameValueMap969extends HashMap<String, Object> {}970971SnapshotInvocationHandler(MBeanServerConnection conn) {972this.conn = conn;973}974975synchronized void flush() {976cachedValues = newMap();977}978979public Object invoke(Object proxy, Method method, Object[] args)980throws Throwable {981final String methodName = method.getName();982if (methodName.equals("getAttribute")) {983return getAttribute((ObjectName) args[0], (String) args[1]);984} else if (methodName.equals("getAttributes")) {985return getAttributes((ObjectName) args[0], (String[]) args[1]);986} else if (methodName.equals("flush")) {987flush();988return null;989} else {990try {991return method.invoke(conn, args);992} catch (InvocationTargetException e) {993throw e.getCause();994}995}996}997998private Object getAttribute(ObjectName objName, String attrName)999throws MBeanException, InstanceNotFoundException,1000AttributeNotFoundException, ReflectionException, IOException {1001final NameValueMap values = getCachedAttributes(1002objName, Collections.singleton(attrName));1003Object value = values.get(attrName);1004if (value != null || values.containsKey(attrName)) {1005return value;1006}1007// Not in cache, presumably because it was omitted from the1008// getAttributes result because of an exception. Following1009// call will probably provoke the same exception.1010return conn.getAttribute(objName, attrName);1011}10121013private AttributeList getAttributes(1014ObjectName objName, String[] attrNames) throws1015InstanceNotFoundException, ReflectionException, IOException {1016final NameValueMap values = getCachedAttributes(1017objName,1018new TreeSet<String>(Arrays.asList(attrNames)));1019final AttributeList list = new AttributeList();1020for (String attrName : attrNames) {1021final Object value = values.get(attrName);1022if (value != null || values.containsKey(attrName)) {1023list.add(new Attribute(attrName, value));1024}1025}1026return list;1027}10281029private synchronized NameValueMap getCachedAttributes(1030ObjectName objName, Set<String> attrNames) throws1031InstanceNotFoundException, ReflectionException, IOException {1032NameValueMap values = cachedValues.get(objName);1033if (values != null && values.keySet().containsAll(attrNames)) {1034return values;1035}1036attrNames = new TreeSet<String>(attrNames);1037Set<String> oldNames = cachedNames.get(objName);1038if (oldNames != null) {1039attrNames.addAll(oldNames);1040}1041values = new NameValueMap();1042final AttributeList attrs = conn.getAttributes(1043objName,1044attrNames.toArray(new String[attrNames.size()]));1045for (Attribute attr : attrs.asList()) {1046values.put(attr.getName(), attr.getValue());1047}1048cachedValues.put(objName, values);1049cachedNames.put(objName, attrNames);1050return values;1051}10521053// See http://www.artima.com/weblogs/viewpost.jsp?thread=793941054private static <K, V> Map<K, V> newMap() {1055return new HashMap<K, V>();1056}1057}1058}105910601061