History

My first initial commit with all of mine experimental changes.

Dmitry Kabak (userdima committed 13 years ago

Commit d37b88d1 parent 3f5b46618352 files · +2598093 −-8690Browse files

Showing the changes to java/pkg/sun/tools/jconsole/ProxyClient.java (1 of 18352 files). Show all

Unified · Split

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