readMapFromString(String jsonStr)
70 | throws JsonParseException, JsonMappingException, IOException {
71 | return om.readValue(jsonStr, Map.class);
72 | }
73 | }
74 |
--------------------------------------------------------------------------------
/src/main/java/com/stephan/tof/jmxmon/jmxutil/JConsoleContext.java:
--------------------------------------------------------------------------------
1 | /*
2 | * %W% %E%
3 | *
4 | * Copyright (c) 2006, Oracle and/or its affiliates. All rights reserved.
5 | * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
6 | */
7 |
8 | package com.stephan.tof.jmxmon.jmxutil;
9 |
10 | import java.beans.PropertyChangeListener;
11 |
12 | import javax.management.MBeanServerConnection;
13 |
14 | /**
15 | * {@code JConsoleContext} represents a JConsole connection to a target
16 | * application.
17 | *
18 | * {@code JConsoleContext} notifies any {@code PropertyChangeListeners}
19 | * about the {@linkplain #CONNECTION_STATE_PROPERTY ConnectionState}
20 | * property change to {@link ConnectionState#CONNECTED CONNECTED} and
21 | * {@link ConnectionState#DISCONNECTED DISCONNECTED}.
22 | * The {@code JConsoleContext} instance will be the source for
23 | * any generated events.
24 | *
25 | *
26 | * @since 1.6
27 | */
28 | public interface JConsoleContext {
29 | /**
30 | * The {@link ConnectionState ConnectionState} bound property name.
31 | */
32 | public static String CONNECTION_STATE_PROPERTY = "connectionState";
33 |
34 | /**
35 | * Values for the {@linkplain #CONNECTION_STATE_PROPERTY
36 | * ConnectionState} bound property.
37 | */
38 | public enum ConnectionState {
39 | /**
40 | * The connection has been successfully established.
41 | */
42 | CONNECTED,
43 | /**
44 | * No connection present.
45 | */
46 | DISCONNECTED,
47 | /**
48 | * The connection is being attempted.
49 | */
50 | CONNECTING
51 | }
52 |
53 | /**
54 | * Returns the {@link MBeanServerConnection MBeanServerConnection} for the
55 | * connection to an application. The returned
56 | * {@code MBeanServerConnection} object becomes invalid when
57 | * the connection state is changed to the
58 | * {@link ConnectionState#DISCONNECTED DISCONNECTED} state.
59 | *
60 | * @return the {@code MBeanServerConnection} for the
61 | * connection to an application.
62 | */
63 | public MBeanServerConnection getMBeanServerConnection();
64 |
65 | /**
66 | * Returns the current connection state.
67 | * @return the current connection state.
68 | */
69 | public ConnectionState getConnectionState();
70 |
71 | /**
72 | * Add a {@link java.beans.PropertyChangeListener PropertyChangeListener}
73 | * to the listener list.
74 | * The listener is registered for all properties.
75 | * The same listener object may be added more than once, and will be called
76 | * as many times as it is added.
77 | * If {@code listener} is {@code null}, no exception is thrown and
78 | * no action is taken.
79 | *
80 | * @param listener The {@code PropertyChangeListener} to be added
81 | */
82 | public void addPropertyChangeListener(PropertyChangeListener listener);
83 |
84 | /**
85 | * Removes a {@link java.beans.PropertyChangeListener PropertyChangeListener}
86 | * from the listener list. This
87 | * removes a {@code PropertyChangeListener} that was registered for all
88 | * properties. If {@code listener} was added more than once to the same
89 | * event source, it will be notified one less time after being removed. If
90 | * {@code listener} is {@code null}, or was never added, no exception is
91 | * thrown and no action is taken.
92 | *
93 | * @param listener the {@code PropertyChangeListener} to be removed
94 | */
95 | public void removePropertyChangeListener(PropertyChangeListener listener);
96 | }
97 |
--------------------------------------------------------------------------------
/src/main/java/com/stephan/tof/jmxmon/jmxutil/LocalVirtualMachine.java:
--------------------------------------------------------------------------------
1 | /*
2 | * %W% %E%
3 | *
4 | * Copyright (c) 2006, Oracle and/or its affiliates. All rights reserved.
5 | * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
6 | */
7 |
8 | package com.stephan.tof.jmxmon.jmxutil;
9 |
10 | import java.io.File;
11 | import java.io.IOException;
12 | import java.util.HashMap;
13 | import java.util.List;
14 | import java.util.Map;
15 | import java.util.Properties;
16 | import java.util.Set;
17 |
18 | import sun.jvmstat.monitor.HostIdentifier;
19 | import sun.jvmstat.monitor.MonitorException;
20 | import sun.jvmstat.monitor.MonitoredHost;
21 | import sun.jvmstat.monitor.MonitoredVm;
22 | import sun.jvmstat.monitor.MonitoredVmUtil;
23 | import sun.jvmstat.monitor.VmIdentifier;
24 | // Sun private
25 | import sun.management.ConnectorAddressLink;
26 |
27 | import com.sun.tools.attach.AgentInitializationException;
28 | import com.sun.tools.attach.AgentLoadException;
29 | import com.sun.tools.attach.AttachNotSupportedException;
30 | // Sun specific
31 | import com.sun.tools.attach.VirtualMachine;
32 | import com.sun.tools.attach.VirtualMachineDescriptor;
33 |
34 | public class LocalVirtualMachine {
35 | private String address;
36 | private String commandLine;
37 | private String displayName;
38 | private int vmid;
39 | private boolean isAttachSupported;
40 |
41 | public LocalVirtualMachine(int vmid, String commandLine, boolean canAttach, String connectorAddress) {
42 | this.vmid = vmid;
43 | this.commandLine = commandLine;
44 | this.address = connectorAddress;
45 | this.isAttachSupported = canAttach;
46 | this.displayName = getDisplayName(commandLine);
47 | }
48 |
49 | private static String getDisplayName(String commandLine) {
50 | // trim the pathname of jar file if it's a jar
51 | String[] res = commandLine.split(" ", 2);
52 | if (res[0].endsWith(".jar")) {
53 | File jarfile = new File(res[0]);
54 | String displayName = jarfile.getName();
55 | if (res.length == 2) {
56 | displayName += " " + res[1];
57 | }
58 | return displayName;
59 | }
60 | return commandLine;
61 | }
62 |
63 | public int vmid() {
64 | return vmid;
65 | }
66 |
67 | public boolean isManageable() {
68 | return (address != null);
69 | }
70 |
71 | public boolean isAttachable() {
72 | return isAttachSupported;
73 | }
74 |
75 | public void startManagementAgent() throws IOException {
76 | if (address != null) {
77 | // already started
78 | return;
79 | }
80 |
81 | if (!isAttachable()) {
82 | throw new IOException("This virtual machine \"" + vmid +
83 | "\" does not support dynamic attach.");
84 | }
85 |
86 | loadManagementAgent();
87 | // fails to load or start the management agent
88 | if (address == null) {
89 | // should never reach here
90 | throw new IOException("Fails to find connector address");
91 | }
92 | }
93 |
94 | public String connectorAddress() {
95 | // return null if not available or no JMX agent
96 | return address;
97 | }
98 |
99 | public String displayName() {
100 | return displayName;
101 | }
102 |
103 | public String toString() {
104 | return commandLine;
105 | }
106 |
107 | // This method returns the list of all virtual machines currently
108 | // running on the machine
109 | public static Map getAllVirtualMachines() {
110 | Map map =
111 | new HashMap();
112 | getMonitoredVMs(map);
113 | getAttachableVMs(map);
114 | return map;
115 | }
116 |
117 | private static void getMonitoredVMs(Map map) {
118 | MonitoredHost host;
119 | Set vms;
120 | try {
121 | host = MonitoredHost.getMonitoredHost(new HostIdentifier((String)null));
122 | vms = host.activeVms();
123 | } catch (java.net.URISyntaxException sx) {
124 | throw new InternalError(sx.getMessage());
125 | } catch (MonitorException mx) {
126 | throw new InternalError(mx.getMessage());
127 | }
128 | for (Object vmid: vms) {
129 | if (vmid instanceof Integer) {
130 | int pid = ((Integer) vmid).intValue();
131 | String name = vmid.toString(); // default to pid if name not available
132 | boolean attachable = false;
133 | String address = null;
134 | try {
135 | MonitoredVm mvm = host.getMonitoredVm(new VmIdentifier(name));
136 | // use the command line as the display name
137 | name = MonitoredVmUtil.commandLine(mvm);
138 | attachable = MonitoredVmUtil.isAttachable(mvm);
139 | address = ConnectorAddressLink.importFrom(pid);
140 | mvm.detach();
141 | } catch (Exception x) {
142 | // ignore
143 | }
144 | map.put((Integer) vmid,
145 | new LocalVirtualMachine(pid, name, attachable, address));
146 | }
147 | }
148 | }
149 |
150 | private static final String LOCAL_CONNECTOR_ADDRESS_PROP =
151 | "com.sun.management.jmxremote.localConnectorAddress";
152 |
153 | private static void getAttachableVMs(Map map) {
154 | List vms = VirtualMachine.list();
155 | for (VirtualMachineDescriptor vmd : vms) {
156 | try {
157 | Integer vmid = Integer.valueOf(vmd.id());
158 | if (!map.containsKey(vmid)) {
159 | boolean attachable = false;
160 | String address = null;
161 | try {
162 | VirtualMachine vm = VirtualMachine.attach(vmd);
163 | attachable = true;
164 | Properties agentProps = vm.getAgentProperties();
165 | address = (String) agentProps.get(LOCAL_CONNECTOR_ADDRESS_PROP);
166 | vm.detach();
167 | } catch (AttachNotSupportedException x) {
168 | // not attachable
169 | } catch (IOException x) {
170 | // ignore
171 | }
172 | map.put(vmid, new LocalVirtualMachine(vmid.intValue(),
173 | vmd.displayName(),
174 | attachable,
175 | address));
176 | }
177 | } catch (NumberFormatException e) {
178 | // do not support vmid different than pid
179 | }
180 | }
181 | }
182 |
183 | public static LocalVirtualMachine getLocalVirtualMachine(int vmid) {
184 | Map map = getAllVirtualMachines();
185 | LocalVirtualMachine lvm = map.get(vmid);
186 | if (lvm == null) {
187 | // Check if the VM is attachable but not included in the list
188 | // if it's running with a different security context.
189 | // For example, Windows services running
190 | // local SYSTEM account are attachable if you have Adminstrator
191 | // privileges.
192 | boolean attachable = false;
193 | String address = null;
194 | String name = String.valueOf(vmid); // default display name to pid
195 | try {
196 | VirtualMachine vm = VirtualMachine.attach(name);
197 | attachable = true;
198 | Properties agentProps = vm.getAgentProperties();
199 | address = (String) agentProps.get(LOCAL_CONNECTOR_ADDRESS_PROP);
200 | vm.detach();
201 | lvm = new LocalVirtualMachine(vmid, name, attachable, address);
202 | } catch (AttachNotSupportedException x) {
203 | // not attachable
204 | throw new IllegalStateException(x);
205 | } catch (IOException x) {
206 | // ignore
207 | throw new IllegalStateException(x);
208 | }
209 | }
210 | return lvm;
211 | }
212 |
213 | // load the management agent into the target VM
214 | private void loadManagementAgent() throws IOException {
215 | VirtualMachine vm = null;
216 | String name = String.valueOf(vmid);
217 | try {
218 | vm = VirtualMachine.attach(name);
219 | } catch (AttachNotSupportedException x) {
220 | IOException ioe = new IOException(x.getMessage());
221 | ioe.initCause(x);
222 | throw ioe;
223 | }
224 |
225 | String home = vm.getSystemProperties().getProperty("java.home");
226 |
227 | // Normally in ${java.home}/jre/lib/management-agent.jar but might
228 | // be in ${java.home}/lib in build environments.
229 |
230 | String agent = home + File.separator + "jre" + File.separator +
231 | "lib" + File.separator + "management-agent.jar";
232 | File f = new File(agent);
233 | if (!f.exists()) {
234 | agent = home + File.separator + "lib" + File.separator +
235 | "management-agent.jar";
236 | f = new File(agent);
237 | if (!f.exists()) {
238 | throw new IOException("Management agent not found");
239 | }
240 | }
241 |
242 | agent = f.getCanonicalPath();
243 | try {
244 | vm.loadAgent(agent, "com.sun.management.jmxremote");
245 | } catch (AgentLoadException x) {
246 | IOException ioe = new IOException(x.getMessage());
247 | ioe.initCause(x);
248 | throw ioe;
249 | } catch (AgentInitializationException x) {
250 | IOException ioe = new IOException(x.getMessage());
251 | ioe.initCause(x);
252 | throw ioe;
253 | }
254 |
255 | // get the connector address
256 | Properties agentProps = vm.getAgentProperties();
257 | address = (String) agentProps.get(LOCAL_CONNECTOR_ADDRESS_PROP);
258 |
259 | vm.detach();
260 | }
261 | }
262 |
--------------------------------------------------------------------------------
/src/main/java/com/stephan/tof/jmxmon/jmxutil/MemoryPoolProxy.java:
--------------------------------------------------------------------------------
1 | /*
2 | * %W% %E%
3 | *
4 | * Copyright (c) 2006, Oracle and/or its affiliates. All rights reserved.
5 | * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
6 | */
7 |
8 | package com.stephan.tof.jmxmon.jmxutil;
9 |
10 | import static java.lang.management.ManagementFactory.GARBAGE_COLLECTOR_MXBEAN_DOMAIN_TYPE;
11 |
12 | import java.lang.management.MemoryPoolMXBean;
13 | import java.lang.management.MemoryUsage;
14 | import java.util.HashMap;
15 | import java.util.Map;
16 | import java.util.Set;
17 |
18 | import javax.management.ObjectName;
19 |
20 | import com.sun.management.GarbageCollectorMXBean;
21 | import com.sun.management.GcInfo;
22 |
23 | public class MemoryPoolProxy {
24 | private String poolName;
25 | private ProxyClient client;
26 | private ObjectName objName;
27 | private MemoryPoolMXBean pool;
28 | private Map gcMBeans;
29 | private GcInfo lastGcInfo;
30 |
31 | public MemoryPoolProxy(ProxyClient client, ObjectName poolName) throws java.io.IOException {
32 | this.client = client;
33 | this.objName = objName;
34 | this.pool = client.getMXBean(poolName, MemoryPoolMXBean.class);
35 | this.poolName = this.pool.getName();
36 | this.gcMBeans = new HashMap();
37 | this.lastGcInfo = null;
38 |
39 | String[] mgrNames = pool.getMemoryManagerNames();
40 | for (String name : mgrNames) {
41 | try {
42 | ObjectName mbeanName = new ObjectName(GARBAGE_COLLECTOR_MXBEAN_DOMAIN_TYPE +
43 | ",name=" + name);
44 | if (client.isRegistered(mbeanName)) {
45 | gcMBeans.put(mbeanName, new Long(0));
46 | }
47 | } catch (Exception e) {
48 | throw new IllegalStateException(e);
49 | }
50 |
51 | }
52 | }
53 |
54 | public boolean isCollectedMemoryPool() {
55 | return (gcMBeans.size() != 0);
56 | }
57 |
58 | public ObjectName getObjectName() {
59 | return objName;
60 | }
61 |
62 | public MemoryPoolStat getStat() throws java.io.IOException {
63 | long usageThreshold = (pool.isUsageThresholdSupported()
64 | ? pool.getUsageThreshold()
65 | : -1);
66 | long collectThreshold = (pool.isCollectionUsageThresholdSupported()
67 | ? pool.getCollectionUsageThreshold()
68 | : -1);
69 | long lastGcStartTime = 0;
70 | long lastGcEndTime = 0;
71 | MemoryUsage beforeGcUsage = null;
72 | MemoryUsage afterGcUsage = null;
73 | long gcId = 0;
74 | if (lastGcInfo != null) {
75 | gcId = lastGcInfo.getId();
76 | lastGcStartTime = lastGcInfo.getStartTime();
77 | lastGcEndTime = lastGcInfo.getEndTime();
78 | beforeGcUsage = lastGcInfo.getMemoryUsageBeforeGc().get(poolName);
79 | afterGcUsage = lastGcInfo.getMemoryUsageAfterGc().get(poolName);
80 | }
81 |
82 | Set> set = gcMBeans.entrySet();
83 | for (Map.Entry e : set) {
84 | GarbageCollectorMXBean gc =
85 | client.getMXBean(e.getKey(),
86 | com.sun.management.GarbageCollectorMXBean.class);
87 | Long gcCount = e.getValue();
88 | Long newCount = gc.getCollectionCount();
89 | if (newCount > gcCount) {
90 | gcMBeans.put(e.getKey(), new Long(newCount));
91 | lastGcInfo = gc.getLastGcInfo();
92 | if (lastGcInfo.getEndTime() > lastGcEndTime) {
93 | gcId = lastGcInfo.getId();
94 | lastGcStartTime = lastGcInfo.getStartTime();
95 | lastGcEndTime = lastGcInfo.getEndTime();
96 | beforeGcUsage = lastGcInfo.getMemoryUsageBeforeGc().get(poolName);
97 | afterGcUsage = lastGcInfo.getMemoryUsageAfterGc().get(poolName);
98 | assert(beforeGcUsage != null);
99 | assert(afterGcUsage != null);
100 | }
101 | }
102 | }
103 |
104 | MemoryUsage usage = pool.getUsage();
105 | return new MemoryPoolStat(poolName,
106 | usageThreshold,
107 | usage,
108 | gcId,
109 | lastGcStartTime,
110 | lastGcEndTime,
111 | collectThreshold,
112 | beforeGcUsage,
113 | afterGcUsage);
114 | }
115 | }
116 |
--------------------------------------------------------------------------------
/src/main/java/com/stephan/tof/jmxmon/jmxutil/MemoryPoolStat.java:
--------------------------------------------------------------------------------
1 | /*
2 | * %W% %E%
3 | *
4 | * Copyright (c) 2006, Oracle and/or its affiliates. All rights reserved.
5 | * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
6 | */
7 | package com.stephan.tof.jmxmon.jmxutil;
8 |
9 | import java.lang.management.MemoryUsage;
10 |
11 | public class MemoryPoolStat {
12 | private String poolName;
13 | private long usageThreshold;
14 | private MemoryUsage usage;
15 | private long lastGcId;
16 | private long lastGcStartTime;
17 | private long lastGcEndTime;
18 | private long collectThreshold;
19 | private MemoryUsage beforeGcUsage;
20 | private MemoryUsage afterGcUsage;
21 |
22 | MemoryPoolStat(String name,
23 | long usageThreshold,
24 | MemoryUsage usage,
25 | long lastGcId,
26 | long lastGcStartTime,
27 | long lastGcEndTime,
28 | long collectThreshold,
29 | MemoryUsage beforeGcUsage,
30 | MemoryUsage afterGcUsage) {
31 | this.poolName = name;
32 | this.usageThreshold = usageThreshold;
33 | this.usage = usage;
34 | this.lastGcId = lastGcId;
35 | this.lastGcStartTime = lastGcStartTime;
36 | this.lastGcEndTime = lastGcEndTime;
37 | this.collectThreshold = collectThreshold;
38 | this.beforeGcUsage = beforeGcUsage;
39 | this.afterGcUsage = afterGcUsage;
40 | }
41 |
42 | /**
43 | * Returns the memory pool name.
44 | */
45 | public String getPoolName() {
46 | return poolName;
47 | }
48 |
49 | /**
50 | * Returns the current memory usage.
51 | */
52 | public MemoryUsage getUsage() {
53 | return usage;
54 | }
55 |
56 | /**
57 | * Returns the current usage threshold.
58 | * -1 if not supported.
59 | */
60 | public long getUsageThreshold() {
61 | return usageThreshold;
62 | }
63 |
64 | /**
65 | * Returns the current collection usage threshold.
66 | * -1 if not supported.
67 | */
68 | public long getCollectionUsageThreshold() {
69 | return collectThreshold;
70 | }
71 |
72 | /**
73 | * Returns the Id of GC.
74 | */
75 | public long getLastGcId() {
76 | return lastGcId;
77 | }
78 |
79 |
80 | /**
81 | * Returns the start time of the most recent GC on
82 | * the memory pool for this statistics in milliseconds.
83 | *
84 | * Return 0 if no GC occurs.
85 | */
86 | public long getLastGcStartTime() {
87 | return lastGcStartTime;
88 | }
89 |
90 | /**
91 | * Returns the end time of the most recent GC on
92 | * the memory pool for this statistics in milliseconds.
93 | *
94 | * Return 0 if no GC occurs.
95 | */
96 | public long getLastGcEndTime() {
97 | return lastGcEndTime;
98 | }
99 |
100 | /**
101 | * Returns the memory usage before the most recent GC started.
102 | * null if no GC occurs.
103 | */
104 | public MemoryUsage getBeforeGcUsage() {
105 | return beforeGcUsage;
106 | }
107 |
108 | /**
109 | * Returns the memory usage after the most recent GC finished.
110 | * null if no GC occurs.
111 | */
112 | public MemoryUsage getAfterGcUsage() {
113 | return beforeGcUsage;
114 | }
115 | }
116 |
--------------------------------------------------------------------------------
/src/main/java/com/stephan/tof/jmxmon/jmxutil/NamedThreadFactory.java:
--------------------------------------------------------------------------------
1 | package com.stephan.tof.jmxmon.jmxutil;
2 |
3 | import java.util.concurrent.ThreadFactory;
4 | import java.util.concurrent.atomic.AtomicInteger;
5 |
6 | /**
7 | * Help for threadpool to set thread name
8 | *
9 | * @author bluedavy
10 | */
11 | public class NamedThreadFactory implements ThreadFactory {
12 |
13 | static final AtomicInteger poolNumber = new AtomicInteger(1);
14 |
15 | final AtomicInteger threadNumber = new AtomicInteger(1);
16 | final ThreadGroup group;
17 | final String namePrefix;
18 | final boolean isDaemon;
19 |
20 | public NamedThreadFactory() {
21 | this("pool");
22 | }
23 | public NamedThreadFactory(String name) {
24 | this(name, false);
25 | }
26 | public NamedThreadFactory(String preffix, boolean daemon) {
27 | SecurityManager s = System.getSecurityManager();
28 | group = (s != null) ? s.getThreadGroup() : Thread.currentThread()
29 | .getThreadGroup();
30 | namePrefix = preffix + "-" + poolNumber.getAndIncrement() + "-thread-";
31 | isDaemon = daemon;
32 | }
33 |
34 |
35 | public Thread newThread(Runnable r) {
36 | Thread t = new Thread(group, r, namePrefix
37 | + threadNumber.getAndIncrement(), 0);
38 | t.setDaemon(isDaemon);
39 | if (t.getPriority() != Thread.NORM_PRIORITY) {
40 | t.setPriority(Thread.NORM_PRIORITY);
41 | }
42 | return t;
43 | }
44 |
45 | }
--------------------------------------------------------------------------------
/src/main/java/com/stephan/tof/jmxmon/jmxutil/ProxyClient.java:
--------------------------------------------------------------------------------
1 | /*
2 | * %W% %E%
3 | *
4 | * Copyright (c) 2006, Oracle and/or its affiliates. All rights reserved.
5 | * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
6 | */
7 |
8 | package com.stephan.tof.jmxmon.jmxutil;
9 |
10 | import static java.lang.management.ManagementFactory.CLASS_LOADING_MXBEAN_NAME;
11 | import static java.lang.management.ManagementFactory.COMPILATION_MXBEAN_NAME;
12 | import static java.lang.management.ManagementFactory.GARBAGE_COLLECTOR_MXBEAN_DOMAIN_TYPE;
13 | import static java.lang.management.ManagementFactory.MEMORY_MXBEAN_NAME;
14 | import static java.lang.management.ManagementFactory.MEMORY_POOL_MXBEAN_DOMAIN_TYPE;
15 | import static java.lang.management.ManagementFactory.OPERATING_SYSTEM_MXBEAN_NAME;
16 | import static java.lang.management.ManagementFactory.RUNTIME_MXBEAN_NAME;
17 | import static java.lang.management.ManagementFactory.THREAD_MXBEAN_NAME;
18 | import static java.lang.management.ManagementFactory.newPlatformMXBeanProxy;
19 |
20 | import java.beans.PropertyChangeEvent;
21 | import java.beans.PropertyChangeListener;
22 | import java.io.IOException;
23 | import java.lang.management.BufferPoolMXBean;
24 | import java.lang.management.ClassLoadingMXBean;
25 | import java.lang.management.CompilationMXBean;
26 | import java.lang.management.GarbageCollectorMXBean;
27 | import java.lang.management.ManagementFactory;
28 | import java.lang.management.MemoryMXBean;
29 | import java.lang.management.OperatingSystemMXBean;
30 | import java.lang.management.RuntimeMXBean;
31 | import java.lang.management.ThreadMXBean;
32 | import java.lang.ref.WeakReference;
33 | import java.lang.reflect.InvocationHandler;
34 | import java.lang.reflect.InvocationTargetException;
35 | import java.lang.reflect.Method;
36 | import java.lang.reflect.Proxy;
37 | import java.rmi.NotBoundException;
38 | import java.rmi.Remote;
39 | import java.rmi.registry.LocateRegistry;
40 | import java.rmi.registry.Registry;
41 | import java.rmi.server.RMIClientSocketFactory;
42 | import java.rmi.server.RemoteObject;
43 | import java.rmi.server.RemoteObjectInvocationHandler;
44 | import java.rmi.server.RemoteRef;
45 | import java.util.ArrayList;
46 | import java.util.Arrays;
47 | import java.util.Collection;
48 | import java.util.Collections;
49 | import java.util.HashMap;
50 | import java.util.Iterator;
51 | import java.util.List;
52 | import java.util.Map;
53 | import java.util.Set;
54 | import java.util.TreeSet;
55 |
56 | import javax.management.Attribute;
57 | import javax.management.AttributeList;
58 | import javax.management.AttributeNotFoundException;
59 | import javax.management.InstanceNotFoundException;
60 | import javax.management.IntrospectionException;
61 | import javax.management.InvalidAttributeValueException;
62 | import javax.management.MBeanException;
63 | import javax.management.MBeanInfo;
64 | import javax.management.MBeanOperationInfo;
65 | import javax.management.MBeanServerConnection;
66 | import javax.management.MalformedObjectNameException;
67 | import javax.management.ObjectName;
68 | import javax.management.ReflectionException;
69 | import javax.management.remote.JMXConnector;
70 | import javax.management.remote.JMXConnectorFactory;
71 | import javax.management.remote.JMXServiceURL;
72 | import javax.management.remote.rmi.RMIConnector;
73 | import javax.management.remote.rmi.RMIServer;
74 | import javax.rmi.ssl.SslRMIClientSocketFactory;
75 | import javax.swing.event.SwingPropertyChangeSupport;
76 |
77 | import org.slf4j.Logger;
78 | import org.slf4j.LoggerFactory;
79 |
80 | import sun.rmi.server.UnicastRef2;
81 | import sun.rmi.transport.LiveRef;
82 |
83 | import com.sun.management.HotSpotDiagnosticMXBean;
84 |
85 | public class ProxyClient implements JConsoleContext {
86 |
87 | private static Logger logger = LoggerFactory.getLogger(ProxyClient.class);
88 |
89 | private ConnectionState connectionState = ConnectionState.DISCONNECTED;
90 |
91 | // The SwingPropertyChangeSupport will fire events on the EDT
92 | private SwingPropertyChangeSupport propertyChangeSupport =
93 | new SwingPropertyChangeSupport(this, true);
94 |
95 | private static Map cache =
96 | Collections.synchronizedMap(new HashMap());
97 |
98 | private volatile boolean isDead = true;
99 | private String hostName = null;
100 | private int port = 0;
101 | private String userName = null;
102 | private String password = null;
103 | private boolean hasPlatformMXBeans = false;
104 | private boolean hasHotSpotDiagnosticMXBean= false;
105 | private boolean hasCompilationMXBean = false;
106 | private boolean supportsLockUsage = false;
107 |
108 | // REVISIT: VMPanel and other places relying using getUrl().
109 |
110 | // set only if it's created for local monitoring
111 | private LocalVirtualMachine lvm;
112 |
113 | // set only if it's created from a given URL via the Advanced tab
114 | private String advancedUrl = null;
115 |
116 | private JMXServiceURL jmxUrl = null;
117 | private MBeanServerConnection mbsc = null;
118 | private SnapshotMBeanServerConnection server = null;
119 | private JMXConnector jmxc = null;
120 | private RMIServer stub = null;
121 | private static final SslRMIClientSocketFactory sslRMIClientSocketFactory =
122 | new SslRMIClientSocketFactory();
123 | private String registryHostName = null;
124 | private int registryPort = 0;
125 | private boolean vmConnector = false;
126 | private boolean sslRegistry = false;
127 | private boolean sslStub = false;
128 | final private String connectionName;
129 | final private String displayName;
130 |
131 | private ClassLoadingMXBean classLoadingMBean = null;
132 | private CompilationMXBean compilationMBean = null;
133 | private MemoryMXBean memoryMBean = null;
134 | private OperatingSystemMXBean operatingSystemMBean = null;
135 | private RuntimeMXBean runtimeMBean = null;
136 | private ThreadMXBean threadMBean = null;
137 |
138 | private com.sun.management.OperatingSystemMXBean sunOperatingSystemMXBean = null;
139 | private HotSpotDiagnosticMXBean hotspotDiagnosticMXBean = null;
140 |
141 | private List memoryPoolProxies = null;
142 | private List garbageCollectorMBeans = null;
143 | private List bufferPoolMXBeans = null;
144 |
145 |
146 | final static private String HOTSPOT_DIAGNOSTIC_MXBEAN_NAME =
147 | "com.sun.management:type=HotSpotDiagnostic";
148 |
149 | private ProxyClient(String hostName, int port,
150 | String userName, String password) throws IOException {
151 | this.connectionName = getConnectionName(hostName, port, userName);
152 | this.displayName = connectionName;
153 | if (hostName.equals("localhost") && port == 0) {
154 | // Monitor self
155 | this.hostName = hostName;
156 | this.port = port;
157 | } else {
158 | // Create an RMI connector client and connect it to
159 | // the RMI connector server
160 | final String urlPath = "/jndi/rmi://" + hostName + ":" + port +
161 | "/jmxrmi";
162 | JMXServiceURL url = new JMXServiceURL("rmi", "", 0, urlPath);
163 | setParameters(url, userName, password);
164 | vmConnector = true;
165 | registryHostName = hostName;
166 | registryPort = port;
167 | checkSslConfig();
168 | }
169 | }
170 |
171 | private ProxyClient(String url,
172 | String userName, String password) throws IOException {
173 | this.advancedUrl = url;
174 | this.connectionName = getConnectionName(url, userName);
175 | this.displayName = connectionName;
176 | setParameters(new JMXServiceURL(url), userName, password);
177 | }
178 |
179 | private ProxyClient(LocalVirtualMachine lvm) throws IOException {
180 | this.lvm = lvm;
181 | this.connectionName = getConnectionName(lvm);
182 | this.displayName = "pid: " + lvm.vmid() + " " + lvm.displayName();
183 | }
184 |
185 | private void setParameters(JMXServiceURL url, String userName, String password) {
186 | this.jmxUrl = url;
187 | this.hostName = jmxUrl.getHost();
188 | this.port = jmxUrl.getPort();
189 | this.userName = userName;
190 | this.password = password;
191 | }
192 |
193 | private static void checkStub(Remote stub,
194 | Class extends Remote> stubClass) {
195 | // Check remote stub is from the expected class.
196 | //
197 | if (stub.getClass() != stubClass) {
198 | if (!Proxy.isProxyClass(stub.getClass())) {
199 | throw new SecurityException(
200 | "Expecting a " + stubClass.getName() + " stub!");
201 | } else {
202 | InvocationHandler handler = Proxy.getInvocationHandler(stub);
203 | if (handler.getClass() != RemoteObjectInvocationHandler.class) {
204 | throw new SecurityException(
205 | "Expecting a dynamic proxy instance with a " +
206 | RemoteObjectInvocationHandler.class.getName() +
207 | " invocation handler!");
208 | } else {
209 | stub = (Remote) handler;
210 | }
211 | }
212 | }
213 | // Check RemoteRef in stub is from the expected class
214 | // "sun.rmi.server.UnicastRef2".
215 | //
216 | RemoteRef ref = ((RemoteObject)stub).getRef();
217 | if (ref.getClass() != UnicastRef2.class) {
218 | throw new SecurityException(
219 | "Expecting a " + UnicastRef2.class.getName() +
220 | " remote reference in stub!");
221 | }
222 | // Check RMIClientSocketFactory in stub is from the expected class
223 | // "javax.rmi.ssl.SslRMIClientSocketFactory".
224 | //
225 | LiveRef liveRef = ((UnicastRef2)ref).getLiveRef();
226 | RMIClientSocketFactory csf = liveRef.getClientSocketFactory();
227 | if (csf == null || csf.getClass() != SslRMIClientSocketFactory.class) {
228 | throw new SecurityException(
229 | "Expecting a " + SslRMIClientSocketFactory.class.getName() +
230 | " RMI client socket factory in stub!");
231 | }
232 | }
233 |
234 | private static final String rmiServerImplStubClassName =
235 | "javax.management.remote.rmi.RMIServerImpl_Stub";
236 | private static final Class extends Remote> rmiServerImplStubClass;
237 |
238 | static {
239 | // FIXME: RMIServerImpl_Stub is generated at build time
240 | // after jconsole is built. We need to investigate if
241 | // the Makefile can be fixed to build jconsole in the
242 | // right order. As a workaround for now, we dynamically
243 | // load RMIServerImpl_Stub class instead of statically
244 | // referencing it.
245 | Class extends Remote> serverStubClass = null;
246 | try {
247 | serverStubClass = Class.forName(rmiServerImplStubClassName).asSubclass(Remote.class);
248 | } catch (ClassNotFoundException e) {
249 | // should never reach here
250 | throw (InternalError) new InternalError(e.getMessage()).initCause(e);
251 | }
252 | rmiServerImplStubClass = serverStubClass;
253 | }
254 |
255 | private void checkSslConfig() throws IOException {
256 | // Get the reference to the RMI Registry and lookup RMIServer stub
257 | //
258 | Registry registry;
259 | try {
260 | registry =
261 | LocateRegistry.getRegistry(registryHostName, registryPort,
262 | sslRMIClientSocketFactory);
263 | try {
264 | stub = (RMIServer) registry.lookup("jmxrmi");
265 | } catch (NotBoundException nbe) {
266 | throw (IOException)
267 | new IOException(nbe.getMessage()).initCause(nbe);
268 | }
269 | sslRegistry = true;
270 | } catch (IOException e) {
271 | registry =
272 | LocateRegistry.getRegistry(registryHostName, registryPort);
273 | try {
274 | stub = (RMIServer) registry.lookup("jmxrmi");
275 | } catch (NotBoundException nbe) {
276 | throw (IOException)
277 | new IOException(nbe.getMessage()).initCause(nbe);
278 | }
279 | sslRegistry = false;
280 | }
281 | // Perform the checks for secure stub
282 | //
283 | try {
284 | checkStub(stub, rmiServerImplStubClass);
285 | sslStub = true;
286 | } catch (SecurityException e) {
287 | sslStub = false;
288 | }
289 | }
290 |
291 | /**
292 | * Returns true if the underlying RMI registry is SSL-protected.
293 | *
294 | * @exception UnsupportedOperationException If this {@code ProxyClient}
295 | * does not denote a JMX connector for a JMX VM agent.
296 | */
297 | public boolean isSslRmiRegistry() {
298 | // Check for VM connector
299 | //
300 | if (!isVmConnector()) {
301 | throw new UnsupportedOperationException(
302 | "ProxyClient.isSslRmiRegistry() is only supported if this " +
303 | "ProxyClient is a JMX connector for a JMX VM agent");
304 | }
305 | return sslRegistry;
306 | }
307 |
308 | /**
309 | * Returns true if the retrieved RMI stub is SSL-protected.
310 | *
311 | * @exception UnsupportedOperationException If this {@code ProxyClient}
312 | * does not denote a JMX connector for a JMX VM agent.
313 | */
314 | public boolean isSslRmiStub() {
315 | // Check for VM connector
316 | //
317 | if (!isVmConnector()) {
318 | throw new UnsupportedOperationException(
319 | "ProxyClient.isSslRmiStub() is only supported if this " +
320 | "ProxyClient is a JMX connector for a JMX VM agent");
321 | }
322 | return sslStub;
323 | }
324 |
325 | /**
326 | * Returns true if this {@code ProxyClient} denotes
327 | * a JMX connector for a JMX VM agent.
328 | */
329 | public boolean isVmConnector() {
330 | return vmConnector;
331 | }
332 |
333 | private void setConnectionState(ConnectionState state) {
334 | ConnectionState oldState = this.connectionState;
335 | this.connectionState = state;
336 | propertyChangeSupport.firePropertyChange(CONNECTION_STATE_PROPERTY,
337 | oldState, state);
338 | }
339 |
340 | public ConnectionState getConnectionState() {
341 | return this.connectionState;
342 | }
343 |
344 | void flush() {
345 | if (server != null) {
346 | server.flush();
347 | }
348 | }
349 |
350 | public void connect() {
351 | setConnectionState(ConnectionState.CONNECTING);
352 | try {
353 | tryConnect();
354 | setConnectionState(ConnectionState.CONNECTED);
355 | } catch (Exception e) {
356 | setConnectionState(ConnectionState.DISCONNECTED);
357 | throw new IllegalStateException(e);
358 | }
359 | }
360 |
361 | private void tryConnect() throws IOException {
362 | if (jmxUrl == null && "localhost".equals(hostName) && port == 0) {
363 | // Monitor self
364 | this.jmxc = null;
365 | this.mbsc = ManagementFactory.getPlatformMBeanServer();
366 | this.server = Snapshot.newSnapshot(mbsc);
367 | } else {
368 | // Monitor another process
369 | if (lvm != null) {
370 | if (!lvm.isManageable()) {
371 | lvm.startManagementAgent();
372 | if (!lvm.isManageable()) {
373 | // FIXME: what to throw
374 | throw new IOException(lvm + "not manageable");
375 | }
376 | }
377 | if (this.jmxUrl == null) {
378 | this.jmxUrl = new JMXServiceURL(lvm.connectorAddress());
379 | }
380 | }
381 | // Need to pass in credentials ?
382 | if (userName == null && password == null) {
383 | if (isVmConnector()) {
384 | // Check for SSL config on reconnection only
385 | if (stub == null) {
386 | checkSslConfig();
387 | }
388 | this.jmxc = new RMIConnector(stub, null);
389 | jmxc.connect();
390 | } else {
391 | this.jmxc = JMXConnectorFactory.connect(jmxUrl);
392 | }
393 | } else {
394 | Map env = new HashMap();
395 | env.put(JMXConnector.CREDENTIALS,
396 | new String[] {userName, password});
397 | if (isVmConnector()) {
398 | // Check for SSL config on reconnection only
399 | if (stub == null) {
400 | checkSslConfig();
401 | }
402 | this.jmxc = new RMIConnector(stub, null);
403 | jmxc.connect(env);
404 | } else {
405 | this.jmxc = JMXConnectorFactory.connect(jmxUrl, env);
406 | }
407 | }
408 | this.mbsc = jmxc.getMBeanServerConnection();
409 | this.server = Snapshot.newSnapshot(mbsc);
410 | }
411 | this.isDead = false;
412 |
413 | try {
414 | ObjectName on = new ObjectName(THREAD_MXBEAN_NAME);
415 | this.hasPlatformMXBeans = server.isRegistered(on);
416 | this.hasHotSpotDiagnosticMXBean =
417 | server.isRegistered(new ObjectName(HOTSPOT_DIAGNOSTIC_MXBEAN_NAME));
418 | // check if it has 6.0 new APIs
419 | if (this.hasPlatformMXBeans) {
420 | MBeanOperationInfo[] mopis = server.getMBeanInfo(on).getOperations();
421 | // look for findDeadlockedThreads operations;
422 | for (MBeanOperationInfo op : mopis) {
423 | if (op.getName().equals("findDeadlockedThreads")) {
424 | this.supportsLockUsage = true;
425 | break;
426 | }
427 | }
428 |
429 | on = new ObjectName(COMPILATION_MXBEAN_NAME);
430 | this.hasCompilationMXBean = server.isRegistered(on);
431 | }
432 | } catch (MalformedObjectNameException e) {
433 | // should not reach here
434 | throw new InternalError(e.getMessage());
435 | } catch (IntrospectionException e) {
436 | InternalError ie = new InternalError(e.getMessage());
437 | ie.initCause(e);
438 | throw ie;
439 | } catch (InstanceNotFoundException e) {
440 | InternalError ie = new InternalError(e.getMessage());
441 | ie.initCause(e);
442 | throw ie;
443 | } catch (ReflectionException e) {
444 | InternalError ie = new InternalError(e.getMessage());
445 | ie.initCause(e);
446 | throw ie;
447 | }
448 |
449 | if (hasPlatformMXBeans) {
450 | // WORKAROUND for bug 5056632
451 | // Check if the access role is correct by getting a RuntimeMXBean
452 | getRuntimeMXBean();
453 | }
454 | }
455 |
456 | /**
457 | * Gets a proxy client for a given local virtual machine.
458 | */
459 | public static ProxyClient getProxyClient(LocalVirtualMachine lvm)
460 | throws IOException {
461 | final String key = getCacheKey(lvm);
462 | ProxyClient proxyClient = cache.get(key);
463 | if (proxyClient == null) {
464 | proxyClient = new ProxyClient(lvm);
465 | cache.put(key, proxyClient);
466 | }
467 | return proxyClient;
468 | }
469 |
470 | public static String getConnectionName(LocalVirtualMachine lvm) {
471 | return Integer.toString(lvm.vmid());
472 | }
473 |
474 | private static String getCacheKey(LocalVirtualMachine lvm) {
475 | return Integer.toString(lvm.vmid());
476 | }
477 |
478 | /**
479 | * Gets a proxy client for a given JMXServiceURL.
480 | */
481 | public static ProxyClient getProxyClient(String url,
482 | String userName, String password)
483 | throws IOException {
484 | final String key = getCacheKey(url, userName, password);
485 | ProxyClient proxyClient = cache.get(key);
486 | if (proxyClient == null) {
487 | proxyClient = new ProxyClient(url, userName, password);
488 | cache.put(key, proxyClient);
489 | }
490 | return proxyClient;
491 | }
492 |
493 | public static String getConnectionName(String url,
494 | String userName) {
495 | if (userName != null && userName.length() > 0) {
496 | return userName + "@" + url;
497 | } else {
498 | return url;
499 | }
500 | }
501 |
502 | private static String getCacheKey(String url,
503 | String userName, String password) {
504 | return (url == null ? "" : url) + ":" +
505 | (userName == null ? "" : userName) + ":" +
506 | (password == null ? "" : password);
507 | }
508 |
509 | /**
510 | * Gets a proxy client for a given "hostname:port".
511 | */
512 | public static ProxyClient getProxyClient(String hostName, int port,
513 | String userName, String password)
514 | throws IOException {
515 | final String key = getCacheKey(hostName, port, userName, password);
516 | ProxyClient proxyClient = cache.get(key);
517 | if (proxyClient == null) {
518 | proxyClient = new ProxyClient(hostName, port, userName, password);
519 | cache.put(key, proxyClient);
520 | }
521 | return cache.get(key);
522 | }
523 |
524 | public static String getConnectionName(String hostName, int port,
525 | String userName) {
526 | String name = hostName + ":" + port;
527 | if (userName != null && userName.length() > 0) {
528 | return userName + "@" + name;
529 | } else {
530 | return name;
531 | }
532 | }
533 |
534 | private static String getCacheKey(String hostName, int port,
535 | String userName, String password) {
536 | return (hostName == null ? "" : hostName) + ":" +
537 | port + ":" +
538 | (userName == null ? "" : userName) + ":" +
539 | (password == null ? "" : password);
540 | }
541 |
542 | public String connectionName() {
543 | return connectionName;
544 | }
545 |
546 | public String getDisplayName() {
547 | return displayName;
548 | }
549 |
550 | public String toString() {
551 | if (!isConnected()) {
552 | return null;
553 | //return Resources.getText("ConnectionName (disconnected)", displayName);
554 | } else {
555 | return displayName;
556 | }
557 | }
558 |
559 | public MBeanServerConnection getMBeanServerConnection() {
560 | return mbsc;
561 | }
562 |
563 | public SnapshotMBeanServerConnection getSnapshotMBeanServerConnection() {
564 | return server;
565 | }
566 |
567 | public String getUrl() {
568 | return advancedUrl;
569 | }
570 |
571 | public String getHostName() {
572 | return hostName;
573 | }
574 |
575 | public int getPort() {
576 | return port;
577 | }
578 |
579 | public int getVmid() {
580 | return (lvm != null) ? lvm.vmid() : 0;
581 | }
582 |
583 | public String getUserName() {
584 | return userName;
585 | }
586 |
587 | public String getPassword() {
588 | return password;
589 | }
590 |
591 | public void disconnect() {
592 | // Reset remote stub
593 | stub = null;
594 | // Close MBeanServer connection
595 | if (jmxc != null) {
596 | try {
597 | jmxc.close();
598 | } catch (IOException e) {
599 | // Ignore ???
600 | }
601 | }
602 | // Reset platform MBean references
603 | classLoadingMBean = null;
604 | compilationMBean = null;
605 | memoryMBean = null;
606 | operatingSystemMBean = null;
607 | runtimeMBean = null;
608 | threadMBean = null;
609 | sunOperatingSystemMXBean = null;
610 | garbageCollectorMBeans = null;
611 | memoryPoolProxies = null;
612 | // Set connection state to DISCONNECTED
613 | if (!isDead) {
614 | isDead = true;
615 | setConnectionState(ConnectionState.DISCONNECTED);
616 | }
617 | }
618 |
619 | /**
620 | * Returns the list of domains in which any MBean is
621 | * currently registered.
622 | */
623 | public String[] getDomains() throws IOException {
624 | return server.getDomains();
625 | }
626 |
627 | /**
628 | * Returns a map of MBeans with ObjectName as the key and MBeanInfo value
629 | * of a given domain. If domain is null, all MBeans
630 | * are returned. If no MBean found, an empty map is returned.
631 | *
632 | */
633 | public Map getMBeans(String domain)
634 | throws IOException {
635 |
636 | ObjectName name = null;
637 | if (domain != null) {
638 | try {
639 | name = new ObjectName(domain + ":*");
640 | } catch (MalformedObjectNameException e) {
641 | // should not reach here
642 | assert(false);
643 | }
644 | }
645 | Set mbeans = server.queryNames(name, null);
646 | Map result =
647 | new HashMap(mbeans.size());
648 | Iterator iterator = mbeans.iterator();
649 | while (iterator.hasNext()) {
650 | Object object = iterator.next();
651 | if (object instanceof ObjectName) {
652 | ObjectName o = (ObjectName)object;
653 | try {
654 | MBeanInfo info = server.getMBeanInfo(o);
655 | result.put(o, info);
656 | } catch (IntrospectionException e) {
657 | // TODO: should log the error
658 | logger.error(e.getMessage(), e);
659 | } catch (InstanceNotFoundException e) {
660 | // TODO: should log the error
661 | logger.error(e.getMessage(), e);
662 | } catch (ReflectionException e) {
663 | // TODO: should log the error
664 | logger.error(e.getMessage(), e);
665 | }
666 | }
667 | }
668 | return result;
669 | }
670 |
671 | /**
672 | * Returns a list of attributes of a named MBean.
673 | *
674 | */
675 | public AttributeList getAttributes(ObjectName name, String[] attributes)
676 | throws IOException {
677 | AttributeList list = null;
678 | try {
679 | list = server.getAttributes(name, attributes);
680 | } catch (InstanceNotFoundException e) {
681 | // TODO: A MBean may have been unregistered.
682 | // need to set up listener to listen for MBeanServerNotification.
683 | logger.error(e.getMessage(), e);
684 | } catch (ReflectionException e) {
685 | // TODO: should log the error
686 | logger.error(e.getMessage(), e);
687 | }
688 | return list;
689 | }
690 |
691 | /**
692 | * Set the value of a specific attribute of a named MBean.
693 | */
694 | public void setAttribute(ObjectName name, Attribute attribute)
695 | throws InvalidAttributeValueException,
696 | MBeanException,
697 | IOException {
698 | try {
699 | server.setAttribute(name, attribute);
700 | } catch (InstanceNotFoundException e) {
701 | // TODO: A MBean may have been unregistered.
702 | logger.error(e.getMessage(), e);
703 | } catch (AttributeNotFoundException e) {
704 | throw new IllegalStateException(e);
705 | } catch (ReflectionException e) {
706 | // TODO: should log the error
707 | logger.error(e.getMessage(), e);
708 | }
709 | }
710 |
711 | /**
712 | * Invokes an operation of a named MBean.
713 | *
714 | * @throws MBeanException Wraps an exception thrown by
715 | * the MBean's invoked method.
716 | */
717 | public Object invoke(ObjectName name, String operationName,
718 | Object[] params, String[] signature)
719 | throws IOException, MBeanException {
720 | Object result = null;
721 | try {
722 | result = server.invoke(name, operationName, params, signature);
723 | } catch (InstanceNotFoundException e) {
724 | // TODO: A MBean may have been unregistered.
725 | } catch (ReflectionException e) {
726 | // TODO: should log the error
727 | logger.error(e.getMessage(), e);
728 | }
729 | return result;
730 | }
731 |
732 | public synchronized ClassLoadingMXBean getClassLoadingMXBean() throws IOException {
733 | if (hasPlatformMXBeans && classLoadingMBean == null) {
734 | classLoadingMBean =
735 | newPlatformMXBeanProxy(server, CLASS_LOADING_MXBEAN_NAME,
736 | ClassLoadingMXBean.class);
737 | }
738 | return classLoadingMBean;
739 | }
740 |
741 | public synchronized CompilationMXBean getCompilationMXBean() throws IOException {
742 | if (hasCompilationMXBean && compilationMBean == null) {
743 | compilationMBean =
744 | newPlatformMXBeanProxy(server, COMPILATION_MXBEAN_NAME,
745 | CompilationMXBean.class);
746 | }
747 | return compilationMBean;
748 | }
749 |
750 | public Collection getMemoryPoolProxies()
751 | throws IOException {
752 |
753 | // TODO: How to deal with changes to the list??
754 | if (memoryPoolProxies == null) {
755 | ObjectName poolName = null;
756 | try {
757 | poolName = new ObjectName(MEMORY_POOL_MXBEAN_DOMAIN_TYPE + ",*");
758 | } catch (MalformedObjectNameException e) {
759 | // should not reach here
760 | assert(false);
761 | }
762 | Set mbeans = server.queryNames(poolName, null);
763 | if (mbeans != null) {
764 | memoryPoolProxies = new ArrayList();
765 | Iterator iterator = mbeans.iterator();
766 | while (iterator.hasNext()) {
767 | ObjectName objName = (ObjectName) iterator.next();
768 | MemoryPoolProxy p = new MemoryPoolProxy(this, objName);
769 | memoryPoolProxies.add(p);
770 | }
771 | }
772 | }
773 | return memoryPoolProxies;
774 | }
775 |
776 |
777 | public synchronized Collection getBufferPoolMXBeans()
778 | throws IOException {
779 | String oName = "java.nio:type=BufferPool";
780 | // TODO: How to deal with changes to the list??
781 | if (bufferPoolMXBeans == null) {
782 | ObjectName bpName = null;
783 | try {
784 | bpName = new ObjectName(oName + ",*");
785 | } catch (MalformedObjectNameException e) {
786 | // should not reach here
787 | assert(false);
788 | }
789 | Set mbeans = server.queryNames(bpName, null);
790 | if (mbeans != null) {
791 | bufferPoolMXBeans = new ArrayList();
792 | Iterator iterator = mbeans.iterator();
793 | while (iterator.hasNext()) {
794 | ObjectName on = (ObjectName) iterator.next();
795 | String name = oName +",name=" + on.getKeyProperty("name");
796 |
797 | BufferPoolMXBean mBean =
798 | newPlatformMXBeanProxy(server, name,
799 | BufferPoolMXBean.class);
800 | bufferPoolMXBeans.add(mBean);
801 | }
802 | }
803 | }
804 | return bufferPoolMXBeans;
805 | }
806 |
807 | public synchronized Collection getGarbageCollectorMXBeans()
808 | throws IOException {
809 |
810 | // TODO: How to deal with changes to the list??
811 | if (garbageCollectorMBeans == null) {
812 | ObjectName gcName = null;
813 | try {
814 | gcName = new ObjectName(GARBAGE_COLLECTOR_MXBEAN_DOMAIN_TYPE + ",*");
815 | } catch (MalformedObjectNameException e) {
816 | // should not reach here
817 | assert(false);
818 | }
819 | Set mbeans = server.queryNames(gcName, null);
820 | if (mbeans != null) {
821 | garbageCollectorMBeans = new ArrayList();
822 | Iterator iterator = mbeans.iterator();
823 | while (iterator.hasNext()) {
824 | ObjectName on = (ObjectName) iterator.next();
825 | String name = GARBAGE_COLLECTOR_MXBEAN_DOMAIN_TYPE +
826 | ",name=" + on.getKeyProperty("name");
827 |
828 | GarbageCollectorMXBean mBean =
829 | newPlatformMXBeanProxy(server, name,
830 | GarbageCollectorMXBean.class);
831 | garbageCollectorMBeans.add(mBean);
832 | }
833 | }
834 | }
835 | return garbageCollectorMBeans;
836 | }
837 |
838 | public synchronized MemoryMXBean getMemoryMXBean() throws IOException {
839 | if (hasPlatformMXBeans && memoryMBean == null) {
840 | memoryMBean =
841 | newPlatformMXBeanProxy(server, MEMORY_MXBEAN_NAME,
842 | MemoryMXBean.class);
843 | }
844 | return memoryMBean;
845 | }
846 |
847 | public synchronized RuntimeMXBean getRuntimeMXBean() throws IOException {
848 | if (hasPlatformMXBeans && runtimeMBean == null) {
849 | runtimeMBean =
850 | newPlatformMXBeanProxy(server, RUNTIME_MXBEAN_NAME,
851 | RuntimeMXBean.class);
852 | }
853 | return runtimeMBean;
854 | }
855 |
856 |
857 | public synchronized ThreadMXBean getThreadMXBean() throws IOException {
858 | if (hasPlatformMXBeans && threadMBean == null) {
859 | threadMBean =
860 | newPlatformMXBeanProxy(server, THREAD_MXBEAN_NAME,
861 | ThreadMXBean.class);
862 | }
863 | return threadMBean;
864 | }
865 |
866 | public synchronized OperatingSystemMXBean getOperatingSystemMXBean() throws IOException {
867 | if (hasPlatformMXBeans && operatingSystemMBean == null) {
868 | operatingSystemMBean =
869 | newPlatformMXBeanProxy(server, OPERATING_SYSTEM_MXBEAN_NAME,
870 | OperatingSystemMXBean.class);
871 | }
872 | return operatingSystemMBean;
873 | }
874 |
875 | public synchronized com.sun.management.OperatingSystemMXBean
876 | getSunOperatingSystemMXBean() throws IOException {
877 |
878 | try {
879 | ObjectName on = new ObjectName(OPERATING_SYSTEM_MXBEAN_NAME);
880 | if (sunOperatingSystemMXBean == null) {
881 | if (server.isInstanceOf(on,
882 | "com.sun.management.OperatingSystemMXBean")) {
883 | sunOperatingSystemMXBean =
884 | newPlatformMXBeanProxy(server,
885 | OPERATING_SYSTEM_MXBEAN_NAME,
886 | com.sun.management.OperatingSystemMXBean.class);
887 | }
888 | }
889 | } catch (InstanceNotFoundException e) {
890 | return null;
891 | } catch (MalformedObjectNameException e) {
892 | return null; // should never reach here
893 | }
894 | return sunOperatingSystemMXBean;
895 | }
896 |
897 | public synchronized HotSpotDiagnosticMXBean getHotSpotDiagnosticMXBean() throws IOException {
898 | if (hasHotSpotDiagnosticMXBean && hotspotDiagnosticMXBean == null) {
899 | hotspotDiagnosticMXBean =
900 | newPlatformMXBeanProxy(server, HOTSPOT_DIAGNOSTIC_MXBEAN_NAME,
901 | HotSpotDiagnosticMXBean.class);
902 | }
903 | return hotspotDiagnosticMXBean;
904 | }
905 |
906 | public T getMXBean(ObjectName objName, Class interfaceClass)
907 | throws IOException {
908 | return newPlatformMXBeanProxy(server,
909 | objName.toString(),
910 | interfaceClass);
911 |
912 | }
913 |
914 | // Return thread IDs of deadlocked threads or null if any.
915 | // It finds deadlocks involving only monitors if it's a Tiger VM.
916 | // Otherwise, it finds deadlocks involving both monitors and
917 | // the concurrent locks.
918 | public long[] findDeadlockedThreads() throws IOException {
919 | ThreadMXBean tm = getThreadMXBean();
920 | if (supportsLockUsage && tm.isSynchronizerUsageSupported()) {
921 | return tm.findDeadlockedThreads();
922 | } else {
923 | return tm.findMonitorDeadlockedThreads();
924 | }
925 | }
926 |
927 | public synchronized void markAsDead() {
928 | disconnect();
929 | }
930 |
931 | public boolean isDead() {
932 | return isDead;
933 | }
934 |
935 | boolean isConnected() {
936 | return !isDead();
937 | }
938 |
939 | boolean hasPlatformMXBeans() {
940 | return this.hasPlatformMXBeans;
941 | }
942 |
943 | boolean hasHotSpotDiagnosticMXBean() {
944 | return this.hasHotSpotDiagnosticMXBean;
945 | }
946 |
947 | boolean isLockUsageSupported() {
948 | return supportsLockUsage;
949 | }
950 |
951 | public boolean isRegistered(ObjectName name) throws IOException {
952 | return server.isRegistered(name);
953 | }
954 |
955 | public void addPropertyChangeListener(PropertyChangeListener listener) {
956 | propertyChangeSupport.addPropertyChangeListener(listener);
957 | }
958 |
959 | public void addWeakPropertyChangeListener(PropertyChangeListener listener) {
960 | if (!(listener instanceof WeakPCL)) {
961 | listener = new WeakPCL(listener);
962 | }
963 | propertyChangeSupport.addPropertyChangeListener(listener);
964 | }
965 |
966 | public void removePropertyChangeListener(PropertyChangeListener listener) {
967 | if (!(listener instanceof WeakPCL)) {
968 | // Search for the WeakPCL holding this listener (if any)
969 | for (PropertyChangeListener pcl : propertyChangeSupport.getPropertyChangeListeners()) {
970 | if (pcl instanceof WeakPCL && ((WeakPCL)pcl).get() == listener) {
971 | listener = pcl;
972 | break;
973 | }
974 | }
975 | }
976 | propertyChangeSupport.removePropertyChangeListener(listener);
977 | }
978 |
979 | /**
980 | * The PropertyChangeListener is handled via a WeakReference
981 | * so as not to pin down the listener.
982 | */
983 | private class WeakPCL extends WeakReference
984 | implements PropertyChangeListener {
985 | WeakPCL(PropertyChangeListener referent) {
986 | super(referent);
987 | }
988 |
989 | public void propertyChange(PropertyChangeEvent pce) {
990 | PropertyChangeListener pcl = get();
991 |
992 | if (pcl == null) {
993 | // The referent listener was GC'ed, we're no longer
994 | // interested in PropertyChanges, remove the listener.
995 | dispose();
996 | } else {
997 | pcl.propertyChange(pce);
998 | }
999 | }
1000 |
1001 | private void dispose() {
1002 | removePropertyChangeListener(this);
1003 | }
1004 | }
1005 |
1006 | //
1007 | // Snapshot MBeanServerConnection:
1008 | //
1009 | // This is an object that wraps an existing MBeanServerConnection and adds
1010 | // caching to it, as follows:
1011 | //
1012 | // - The first time an attribute is called in a given MBean, the result is
1013 | // cached. Every subsequent time getAttribute is called for that attribute
1014 | // the cached result is returned.
1015 | //
1016 | // - Before every call to VMPanel.update() or when the Refresh button in the
1017 | // Attributes table is pressed down the attributes cache is flushed. Then
1018 | // any subsequent call to getAttribute will retrieve all the values for
1019 | // the attributes that are known to the cache.
1020 | //
1021 | // - The attributes cache uses a learning approach and only the attributes
1022 | // that are in the cache will be retrieved between two subsequent updates.
1023 | //
1024 |
1025 | public interface SnapshotMBeanServerConnection
1026 | extends MBeanServerConnection {
1027 | /**
1028 | * Flush all cached values of attributes.
1029 | */
1030 | public void flush();
1031 | }
1032 |
1033 | public static class Snapshot {
1034 | private Snapshot() {
1035 | }
1036 | public static SnapshotMBeanServerConnection
1037 | newSnapshot(MBeanServerConnection mbsc) {
1038 | final InvocationHandler ih = new SnapshotInvocationHandler(mbsc);
1039 | return (SnapshotMBeanServerConnection) Proxy.newProxyInstance(
1040 | Snapshot.class.getClassLoader(),
1041 | new Class[] {SnapshotMBeanServerConnection.class},
1042 | ih);
1043 | }
1044 | }
1045 |
1046 | static class SnapshotInvocationHandler implements InvocationHandler {
1047 |
1048 | private final MBeanServerConnection conn;
1049 | private Map cachedValues = newMap();
1050 | private Map> cachedNames = newMap();
1051 |
1052 | @SuppressWarnings("serial")
1053 | private static final class NameValueMap
1054 | extends HashMap {}
1055 |
1056 | SnapshotInvocationHandler(MBeanServerConnection conn) {
1057 | this.conn = conn;
1058 | }
1059 |
1060 | synchronized void flush() {
1061 | cachedValues = newMap();
1062 | }
1063 |
1064 | public Object invoke(Object proxy, Method method, Object[] args)
1065 | throws Throwable {
1066 | final String methodName = method.getName();
1067 | if (methodName.equals("getAttribute")) {
1068 | return getAttribute((ObjectName) args[0], (String) args[1]);
1069 | } else if (methodName.equals("getAttributes")) {
1070 | return getAttributes((ObjectName) args[0], (String[]) args[1]);
1071 | } else if (methodName.equals("flush")) {
1072 | flush();
1073 | return null;
1074 | } else {
1075 | try {
1076 | return method.invoke(conn, args);
1077 | } catch (InvocationTargetException e) {
1078 | throw e.getCause();
1079 | }
1080 | }
1081 | }
1082 |
1083 | private Object getAttribute(ObjectName objName, String attrName)
1084 | throws MBeanException, InstanceNotFoundException,
1085 | AttributeNotFoundException, ReflectionException, IOException {
1086 | final NameValueMap values = getCachedAttributes(
1087 | objName, Collections.singleton(attrName));
1088 | Object value = values.get(attrName);
1089 | if (value != null || values.containsKey(attrName)) {
1090 | return value;
1091 | }
1092 | // Not in cache, presumably because it was omitted from the
1093 | // getAttributes result because of an exception. Following
1094 | // call will probably provoke the same exception.
1095 | return conn.getAttribute(objName, attrName);
1096 | }
1097 |
1098 | private AttributeList getAttributes(
1099 | ObjectName objName, String[] attrNames) throws
1100 | InstanceNotFoundException, ReflectionException, IOException {
1101 | final NameValueMap values = getCachedAttributes(
1102 | objName,
1103 | new TreeSet(Arrays.asList(attrNames)));
1104 | final AttributeList list = new AttributeList();
1105 | for (String attrName : attrNames) {
1106 | final Object value = values.get(attrName);
1107 | if (value != null || values.containsKey(attrName)) {
1108 | list.add(new Attribute(attrName, value));
1109 | }
1110 | }
1111 | return list;
1112 | }
1113 |
1114 | private synchronized NameValueMap getCachedAttributes(
1115 | ObjectName objName, Set attrNames) throws
1116 | InstanceNotFoundException, ReflectionException, IOException {
1117 | NameValueMap values = cachedValues.get(objName);
1118 | if (values != null && values.keySet().containsAll(attrNames)) {
1119 | return values;
1120 | }
1121 | attrNames = new TreeSet(attrNames);
1122 | Set oldNames = cachedNames.get(objName);
1123 | if (oldNames != null) {
1124 | attrNames.addAll(oldNames);
1125 | }
1126 | values = new NameValueMap();
1127 | final AttributeList attrs = conn.getAttributes(
1128 | objName,
1129 | attrNames.toArray(new String[attrNames.size()]));
1130 | for (Attribute attr : attrs.asList()) {
1131 | values.put(attr.getName(), attr.getValue());
1132 | }
1133 | cachedValues.put(objName, values);
1134 | cachedNames.put(objName, attrNames);
1135 | return values;
1136 | }
1137 |
1138 | // See http://www.artima.com/weblogs/viewpost.jsp?thread=79394
1139 | private static Map newMap() {
1140 | return new HashMap();
1141 | }
1142 | }
1143 | }
1144 |
--------------------------------------------------------------------------------
/src/main/resources/conf.properties:
--------------------------------------------------------------------------------
1 | # the working dir
2 | workDir=./
3 |
4 | # localhost jmx ports, split by comma
5 | jmx.ports=10000,10001,10002,10003
6 |
7 | # agent port url
8 | agent.posturl=http://localhost:1988/v1/push
9 |
--------------------------------------------------------------------------------
/src/main/resources/log4j.properties:
--------------------------------------------------------------------------------
1 | # Output pattern : date [thread] priority category - message
2 | #log4j.rootLogger=INFO, Console, RollingFile
3 | log4j.rootLogger=INFO, Console
4 |
5 | #Console
6 | log4j.appender.Console=org.apache.log4j.ConsoleAppender
7 | log4j.appender.Console.layout=org.apache.log4j.PatternLayout
8 | log4j.appender.Console.layout.ConversionPattern=%d [%t] %-5p [%c] - %m%n
9 |
--------------------------------------------------------------------------------
/src/test/java/com/stephan/tof/jmxmon/bean/FalconPostDataTest.java:
--------------------------------------------------------------------------------
1 | package com.stephan.tof.jmxmon.bean;
2 |
3 | import static org.assertj.core.api.Assertions.assertThat;
4 | import static org.assertj.core.api.Assertions.tuple;
5 |
6 | import java.io.File;
7 | import java.io.IOException;
8 | import java.util.ArrayList;
9 | import java.util.List;
10 |
11 | import org.junit.Before;
12 | import org.junit.BeforeClass;
13 | import org.junit.Test;
14 |
15 | import com.stephan.tof.jmxmon.Constants.CounterType;
16 |
17 | public class FalconPostDataTest {
18 |
19 | @BeforeClass
20 | public static void setUpBeforeClass() throws Exception {
21 | }
22 |
23 | @Before
24 | public void setUp() throws Exception {
25 | }
26 |
27 | @Test
28 | public void testJsonTransfer() throws IOException {
29 | FalconItem item1 = new FalconItem("m1", "ep1",
30 | System.currentTimeMillis() / 1000, 60, 123,
31 | CounterType.COUNTER.toString(), "tag1=a,tag2=b");
32 | FalconItem item2 = new FalconItem("m2", "ep2",
33 | System.currentTimeMillis() / 1000, 60, 156789.123456F,
34 | CounterType.GAUGE.toString(), "tag3=a,tag4=b");
35 | List writeItems = new ArrayList();
36 | writeItems.add(item1);
37 | writeItems.add(item2);
38 |
39 | File f = new File("test.falconPostData.json");
40 | JacksonUtil.writeBeanToFile(f, writeItems, true);
41 | System.out.println("json file path=" + f.getAbsolutePath());
42 |
43 | @SuppressWarnings("unchecked")
44 | List readItems = JacksonUtil
45 | .readBeanFromFile(f, List.class);
46 | assertThat(readItems).hasSize(2);
47 | assertThat(readItems).extracting(
48 | "metric", "endpoint", "step", "value", "counterType", "tags").containsExactly(
49 | tuple("m1", "ep1", 60, 123D, CounterType.COUNTER.toString(), "tag1=a,tag2=b"),
50 | tuple("m2", "ep2", 60, 156789.12D, CounterType.GAUGE.toString(), "tag3=a,tag4=b"));
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/src/test/java/com/stephan/tof/jmxmon/bean/JVMContextTest.java:
--------------------------------------------------------------------------------
1 | package com.stephan.tof.jmxmon.bean;
2 |
3 | import static org.assertj.core.api.Assertions.assertThat;
4 |
5 | import java.io.File;
6 | import java.io.IOException;
7 |
8 | import org.junit.Before;
9 | import org.junit.BeforeClass;
10 | import org.junit.Test;
11 |
12 | public class JVMContextTest {
13 |
14 | @BeforeClass
15 | public static void setUpBeforeClass() throws Exception {
16 | }
17 |
18 | @Before
19 | public void setUp() throws Exception {
20 | }
21 |
22 | @Test
23 | public void testJsonTransfer() throws IOException {
24 | JVMContext context1 = new JVMContext();
25 | context1.getJvmData(1111).getGcData("1a").setCollectionCount(11);
26 | context1.getJvmData(1111).getGcData("1b").setCollectionCount(12);
27 | context1.getJvmData(2222).getGcData("2a").setCollectionCount(21);
28 | context1.getJvmData(2222).getGcData("2b").setCollectionCount(22);
29 | context1.getJvmData(2222).getGcData("2c").setCollectionCount(23);
30 | File f = new File("test.jvmcontext.json");
31 | JacksonUtil.writeBeanToFile(f, context1, true);
32 | System.out.println("json file path=" + f.getAbsolutePath());
33 |
34 | JVMContext context2 = JacksonUtil.readBeanFromFile(f, JVMContext.class);
35 | assertThat(context2.getJvmData(1111).getGcData("1b").getCollectionCount()).isEqualTo(12);
36 | assertThat(context2.getJvmData(2222).getGcData("2a").getCollectionCount()).isEqualTo(21);
37 | assertThat(context2.getJvmData(2222).getGcData("2c").getCollectionCount()).isEqualTo(23);
38 | assertThat(context2.getJvmData(2222).getGcData("2a").getCollectionTime()).isEqualTo(0);
39 | assertThat(context2.getJvmData(2222).getGcData("2b").getCollectionTime()).isEqualTo(0);
40 | assertThat(context2.getJvmData(2222).getGcData("2c").getCollectionTime()).isEqualTo(0);
41 | }
42 |
43 | }
44 |
--------------------------------------------------------------------------------