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
26package javax.management;
27
28import com.sun.jmx.mbeanserver.Introspector;
29import java.lang.reflect.InvocationHandler;
30import java.lang.reflect.Modifier;
31import java.lang.reflect.Proxy;
32import 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 */
39public 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 &#64;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