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
26package sun.tools.jconsole;
27
28import com.sun.management.HotSpotDiagnosticMXBean;
29import com.sun.tools.jconsole.JConsoleContext;
30import com.sun.tools.jconsole.JConsoleContext.ConnectionState;
31import java.beans.PropertyChangeListener;
32import java.beans.PropertyChangeEvent;
33import java.io.IOException;
34import java.lang.management.*;
35import static java.lang.management.ManagementFactory.*;
36import java.lang.ref.WeakReference;
37import java.lang.reflect.*;
38import java.rmi.*;
39import java.rmi.registry.*;
40import java.rmi.server.*;
41import java.util.*;
42import javax.management.*;
43import javax.management.remote.*;
44import javax.management.remote.rmi.*;
45import javax.rmi.ssl.SslRMIClientSocketFactory;
46import javax.swing.event.SwingPropertyChangeSupport;
47import sun.rmi.server.UnicastRef2;
48import sun.rmi.transport.LiveRef;
49
50public 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}