1 /*
2 * Copyright (c) 2005, 2013, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation. Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26 package javax.management;
27
28 import com.sun.jmx.mbeanserver.Introspector;
29 import java.lang.reflect.InvocationHandler;
30 import java.lang.reflect.Modifier;
31 import java.lang.reflect.Proxy;
32 import sun.reflect.misc.ReflectUtil;
33
34 /**
35 * Static methods from the JMX API. There are no instances of this class.
36 *
37 * @since 1.6
38 */
39 public class JMX {
40 /* Code within this package can prove that by providing this instance of
41 * this class.
42 */
43 static final JMX proof = new JMX();
44
45 private JMX() {}
46
47 /**
48 * The name of the <a href="Descriptor.html#defaultValue">{@code
49 * defaultValue}</a> field.
50 */
51 public static final String DEFAULT_VALUE_FIELD = "defaultValue";
52
53 /**
54 * The name of the <a href="Descriptor.html#immutableInfo">{@code
55 * immutableInfo}</a> field.
56 */
57 public static final String IMMUTABLE_INFO_FIELD = "immutableInfo";
58
59 /**
60 * The name of the <a href="Descriptor.html#interfaceClassName">{@code
61 * interfaceClassName}</a> field.
62 */
63 public static final String INTERFACE_CLASS_NAME_FIELD = "interfaceClassName";
64
65 /**
66 * The name of the <a href="Descriptor.html#legalValues">{@code
67 * legalValues}</a> field.
68 */
69 public static final String LEGAL_VALUES_FIELD = "legalValues";
70
71 /**
72 * The name of the <a href="Descriptor.html#maxValue">{@code
73 * maxValue}</a> field.
74 */
75 public static final String MAX_VALUE_FIELD = "maxValue";
76
77 /**
78 * The name of the <a href="Descriptor.html#minValue">{@code
79 * minValue}</a> field.
80 */
81 public static final String MIN_VALUE_FIELD = "minValue";
82
83 /**
84 * The name of the <a href="Descriptor.html#mxbean">{@code
85 * mxbean}</a> field.
86 */
87 public static final String MXBEAN_FIELD = "mxbean";
88
89 /**
90 * The name of the <a href="Descriptor.html#openType">{@code
91 * openType}</a> field.
92 */
93 public static final String OPEN_TYPE_FIELD = "openType";
94
95 /**
96 * The name of the <a href="Descriptor.html#originalType">{@code
97 * originalType}</a> field.
98 */
99 public static final String ORIGINAL_TYPE_FIELD = "originalType";
100
101 /**
102 * <p>Make a proxy for a Standard MBean in a local or remote
103 * MBean Server.</p>
104 *
105 * <p>If you have an MBean Server {@code mbs} containing an MBean
106 * with {@link ObjectName} {@code name}, and if the MBean's
107 * management interface is described by the Java interface
108 * {@code MyMBean}, you can construct a proxy for the MBean like
109 * this:</p>
110 *
111 * <pre>
112 * MyMBean proxy = JMX.newMBeanProxy(mbs, name, MyMBean.class);
113 * </pre>
114 *
115 * <p>Suppose, for example, {@code MyMBean} looks like this:</p>
116 *
117 * <pre>
118 * public interface MyMBean {
119 * public String getSomeAttribute();
120 * public void setSomeAttribute(String value);
121 * public void someOperation(String param1, int param2);
122 * }
123 * </pre>
124 *
125 * <p>Then you can execute:</p>
126 *
127 * <ul>
128 *
129 * <li>{@code proxy.getSomeAttribute()} which will result in a
130 * call to {@code mbs.}{@link MBeanServerConnection#getAttribute
131 * getAttribute}{@code (name, "SomeAttribute")}.
132 *
133 * <li>{@code proxy.setSomeAttribute("whatever")} which will result
134 * in a call to {@code mbs.}{@link MBeanServerConnection#setAttribute
135 * setAttribute}{@code (name, new Attribute("SomeAttribute", "whatever"))}.
136 *
137 * <li>{@code proxy.someOperation("param1", 2)} which will be
138 * translated into a call to {@code mbs.}{@link
139 * MBeanServerConnection#invoke invoke}{@code (name, "someOperation", <etc>)}.
140 *
141 * </ul>
142 *
143 * <p>The object returned by this method is a
144 * {@link Proxy} whose {@code InvocationHandler} is an
145 * {@link MBeanServerInvocationHandler}.</p>
146 *
147 * <p>This method is equivalent to {@link
148 * #newMBeanProxy(MBeanServerConnection, ObjectName, Class,
149 * boolean) newMBeanProxy(connection, objectName, interfaceClass,
150 * false)}.</p>
151 *
152 * @param connection the MBean server to forward to.
153 * @param objectName the name of the MBean within
154 * {@code connection} to forward to.
155 * @param interfaceClass the management interface that the MBean
156 * exports, which will also be implemented by the returned proxy.
157 *
158 * @param <T> allows the compiler to know that if the {@code
159 * interfaceClass} parameter is {@code MyMBean.class}, for
160 * example, then the return type is {@code MyMBean}.
161 *
162 * @return the new proxy instance.
163 *
164 * @throws IllegalArgumentException if {@code interfaceClass} is not
165 * a <a href="package-summary.html#mgIface">compliant MBean
166 * interface</a>
167 */
168 public static <T> T newMBeanProxy(MBeanServerConnection connection,
169 ObjectName objectName,
170 Class<T> interfaceClass) {
171 return newMBeanProxy(connection, objectName, interfaceClass, false);
172 }
173
174 /**
175 * <p>Make a proxy for a Standard MBean in a local or remote MBean
176 * Server that may also support the methods of {@link
177 * NotificationEmitter}.</p>
178 *
179 * <p>This method behaves the same as {@link
180 * #newMBeanProxy(MBeanServerConnection, ObjectName, Class)}, but
181 * additionally, if {@code notificationEmitter} is {@code
182 * true}, then the MBean is assumed to be a {@link
183 * NotificationBroadcaster} or {@link NotificationEmitter} and the
184 * returned proxy will implement {@link NotificationEmitter} as
185 * well as {@code interfaceClass}. A call to {@link
186 * NotificationBroadcaster#addNotificationListener} on the proxy
187 * will result in a call to {@link
188 * MBeanServerConnection#addNotificationListener(ObjectName,
189 * NotificationListener, NotificationFilter, Object)}, and
190 * likewise for the other methods of {@link
191 * NotificationBroadcaster} and {@link NotificationEmitter}.</p>
192 *
193 * @param connection the MBean server to forward to.
194 * @param objectName the name of the MBean within
195 * {@code connection} to forward to.
196 * @param interfaceClass the management interface that the MBean
197 * exports, which will also be implemented by the returned proxy.
198 * @param notificationEmitter make the returned proxy
199 * implement {@link NotificationEmitter} by forwarding its methods
200 * via {@code connection}.
201 *
202 * @param <T> allows the compiler to know that if the {@code
203 * interfaceClass} parameter is {@code MyMBean.class}, for
204 * example, then the return type is {@code MyMBean}.
205 *
206 * @return the new proxy instance.
207 *
208 * @throws IllegalArgumentException if {@code interfaceClass} is not
209 * a <a href="package-summary.html#mgIface">compliant MBean
210 * interface</a>
211 */
212 public static <T> T newMBeanProxy(MBeanServerConnection connection,
213 ObjectName objectName,
214 Class<T> interfaceClass,
215 boolean notificationEmitter) {
216 return createProxy(connection, objectName, interfaceClass, notificationEmitter, false);
217 }
218
219 /**
220 * Make a proxy for an MXBean in a local or remote MBean Server.
221 *
222 * <p>If you have an MBean Server {@code mbs} containing an
223 * MXBean with {@link ObjectName} {@code name}, and if the
224 * MXBean's management interface is described by the Java
225 * interface {@code MyMXBean}, you can construct a proxy for
226 * the MXBean like this:</p>
227 *
228 * <pre>
229 * MyMXBean proxy = JMX.newMXBeanProxy(mbs, name, MyMXBean.class);
230 * </pre>
231 *
232 * <p>Suppose, for example, {@code MyMXBean} looks like this:</p>
233 *
234 * <pre>
235 * public interface MyMXBean {
236 * public String getSimpleAttribute();
237 * public void setSimpleAttribute(String value);
238 * public {@link java.lang.management.MemoryUsage} getMappedAttribute();
239 * public void setMappedAttribute(MemoryUsage memoryUsage);
240 * public MemoryUsage someOperation(String param1, MemoryUsage param2);
241 * }
242 * </pre>
243 *
244 * <p>Then:</p>
245 *
246 * <ul>
247 *
248 * <li><p>{@code proxy.getSimpleAttribute()} will result in a
249 * call to {@code mbs.}{@link MBeanServerConnection#getAttribute
250 * getAttribute}{@code (name, "SimpleAttribute")}.</p>
251 *
252 * <li><p>{@code proxy.setSimpleAttribute("whatever")} will result
253 * in a call to {@code mbs.}{@link
254 * MBeanServerConnection#setAttribute setAttribute}<code>(name,
255 * new Attribute("SimpleAttribute", "whatever"))</code>.</p>
256 *
257 * <p>Because {@code String} is a <em>simple type</em>, in the
258 * sense of {@link javax.management.openmbean.SimpleType}, it
259 * is not changed in the context of an MXBean. The MXBean
260 * proxy behaves the same as a Standard MBean proxy (see
261 * {@link #newMBeanProxy(MBeanServerConnection, ObjectName,
262 * Class) newMBeanProxy}) for the attribute {@code
263 * SimpleAttribute}.</p>
264 *
265 * <li><p>{@code proxy.getMappedAttribute()} will result in a call
266 * to {@code mbs.getAttribute("MappedAttribute")}. The MXBean
267 * mapping rules mean that the actual type of the attribute {@code
268 * MappedAttribute} will be {@link
269 * javax.management.openmbean.CompositeData CompositeData} and
270 * that is what the {@code mbs.getAttribute} call will return.
271 * The proxy will then convert the {@code CompositeData} back into
272 * the expected type {@code MemoryUsage} using the MXBean mapping
273 * rules.</p>
274 *
275 * <li><p>Similarly, {@code proxy.setMappedAttribute(memoryUsage)}
276 * will convert the {@code MemoryUsage} argument into a {@code
277 * CompositeData} before calling {@code mbs.setAttribute}.</p>
278 *
279 * <li><p>{@code proxy.someOperation("whatever", memoryUsage)}
280 * will convert the {@code MemoryUsage} argument into a {@code
281 * CompositeData} and call {@code mbs.invoke}. The value returned
282 * by {@code mbs.invoke} will be also be a {@code CompositeData},
283 * and the proxy will convert this into the expected type {@code
284 * MemoryUsage} using the MXBean mapping rules.</p>
285 *
286 * </ul>
287 *
288 * <p>The object returned by this method is a
289 * {@link Proxy} whose {@code InvocationHandler} is an
290 * {@link MBeanServerInvocationHandler}.</p>
291 *
292 * <p>This method is equivalent to {@link
293 * #newMXBeanProxy(MBeanServerConnection, ObjectName, Class,
294 * boolean) newMXBeanProxy(connection, objectName, interfaceClass,
295 * false)}.</p>
296 *
297 * @param connection the MBean server to forward to.
298 * @param objectName the name of the MBean within
299 * {@code connection} to forward to.
300 * @param interfaceClass the MXBean interface,
301 * which will also be implemented by the returned proxy.
302 *
303 * @param <T> allows the compiler to know that if the {@code
304 * interfaceClass} parameter is {@code MyMXBean.class}, for
305 * example, then the return type is {@code MyMXBean}.
306 *
307 * @return the new proxy instance.
308 *
309 * @throws IllegalArgumentException if {@code interfaceClass} is not
310 * a {@link javax.management.MXBean compliant MXBean interface}
311 */
312 public static <T> T newMXBeanProxy(MBeanServerConnection connection,
313 ObjectName objectName,
314 Class<T> interfaceClass) {
315 return newMXBeanProxy(connection, objectName, interfaceClass, false);
316 }
317
318 /**
319 * <p>Make a proxy for an MXBean in a local or remote MBean
320 * Server that may also support the methods of {@link
321 * NotificationEmitter}.</p>
322 *
323 * <p>This method behaves the same as {@link
324 * #newMXBeanProxy(MBeanServerConnection, ObjectName, Class)}, but
325 * additionally, if {@code notificationEmitter} is {@code
326 * true}, then the MXBean is assumed to be a {@link
327 * NotificationBroadcaster} or {@link NotificationEmitter} and the
328 * returned proxy will implement {@link NotificationEmitter} as
329 * well as {@code interfaceClass}. A call to {@link
330 * NotificationBroadcaster#addNotificationListener} on the proxy
331 * will result in a call to {@link
332 * MBeanServerConnection#addNotificationListener(ObjectName,
333 * NotificationListener, NotificationFilter, Object)}, and
334 * likewise for the other methods of {@link
335 * NotificationBroadcaster} and {@link NotificationEmitter}.</p>
336 *
337 * @param connection the MBean server to forward to.
338 * @param objectName the name of the MBean within
339 * {@code connection} to forward to.
340 * @param interfaceClass the MXBean interface,
341 * which will also be implemented by the returned proxy.
342 * @param notificationEmitter make the returned proxy
343 * implement {@link NotificationEmitter} by forwarding its methods
344 * via {@code connection}.
345 *
346 * @param <T> allows the compiler to know that if the {@code
347 * interfaceClass} parameter is {@code MyMXBean.class}, for
348 * example, then the return type is {@code MyMXBean}.
349 *
350 * @return the new proxy instance.
351 *
352 * @throws IllegalArgumentException if {@code interfaceClass} is not
353 * a {@link javax.management.MXBean compliant MXBean interface}
354 */
355 public static <T> T newMXBeanProxy(MBeanServerConnection connection,
356 ObjectName objectName,
357 Class<T> interfaceClass,
358 boolean notificationEmitter) {
359 return createProxy(connection, objectName, interfaceClass, notificationEmitter, true);
360 }
361
362 /**
363 * <p>Test whether an interface is an MXBean interface.
364 * An interface is an MXBean interface if it is public,
365 * annotated {@link MXBean @MXBean} or {@code @MXBean(true)}
366 * or if it does not have an {@code @MXBean} annotation
367 * and its name ends with "{@code MXBean}".</p>
368 *
369 * @param interfaceClass The candidate interface.
370 *
371 * @return true if {@code interfaceClass} is a
372 * {@link javax.management.MXBean compliant MXBean interface}
373 *
374 * @throws NullPointerException if {@code interfaceClass} is null.
375 */
376 public static boolean isMXBeanInterface(Class<?> interfaceClass) {
377 if (!interfaceClass.isInterface())
378 return false;
379 if (!Modifier.isPublic(interfaceClass.getModifiers()) &&
380 !Introspector.ALLOW_NONPUBLIC_MBEAN) {
381 return false;
382 }
383 MXBean a = interfaceClass.getAnnotation(MXBean.class);
384 if (a != null)
385 return a.value();
386 return interfaceClass.getName().endsWith("MXBean");
387 // We don't bother excluding the case where the name is
388 // exactly the string "MXBean" since that would mean there
389 // was no package name, which is pretty unlikely in practice.
390 }
391
392 /**
393 * Centralised M(X)Bean proxy creation code
394 * @param connection {@linkplain MBeanServerConnection} to use
395 * @param objectName M(X)Bean object name
396 * @param interfaceClass M(X)Bean interface class
397 * @param notificationEmitter Is a notification emitter?
398 * @param isMXBean Is an MXBean?
399 * @return Returns an M(X)Bean proxy generated for the provided interface class
400 * @throws SecurityException
401 * @throws IllegalArgumentException
402 */
403 private static <T> T createProxy(MBeanServerConnection connection,
404 ObjectName objectName,
405 Class<T> interfaceClass,
406 boolean notificationEmitter,
407 boolean isMXBean) {
408
409 try {
410 if (isMXBean) {
411 // Check interface for MXBean compliance
412 Introspector.testComplianceMXBeanInterface(interfaceClass);
413 } else {
414 // Check interface for MBean compliance
415 Introspector.testComplianceMBeanInterface(interfaceClass);
416 }
417 } catch (NotCompliantMBeanException e) {
418 throw new IllegalArgumentException(e);
419 }
420
421 InvocationHandler handler = new MBeanServerInvocationHandler(
422 connection, objectName, isMXBean);
423 final Class<?>[] interfaces;
424 if (notificationEmitter) {
425 interfaces =
426 new Class<?>[] {interfaceClass, NotificationEmitter.class};
427 } else
428 interfaces = new Class<?>[] {interfaceClass};
429
430 Object proxy = Proxy.newProxyInstance(
431 interfaceClass.getClassLoader(),
432 interfaces,
433 handler);
434 return interfaceClass.cast(proxy);
435 }
436 }
437