| /* | |
| * Copyright (c) 2005, 2025, Oracle and/or its affiliates. All rights reserved. | |
| * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. | |
| * | |
| * This code is free software; you can redistribute it and/or modify it | |
| * under the terms of the GNU General Public License version 2 only, as | |
| * published by the Free Software Foundation. Oracle designates this | |
| * particular file as subject to the "Classpath" exception as provided | |
| * by Oracle in the LICENSE file that accompanied this code. | |
| * | |
| * This code is distributed in the hope that it will be useful, but WITHOUT | |
| * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
| * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | |
| * version 2 for more details (a copy is included in the LICENSE file that | |
| * accompanied this code). | |
| * | |
| * You should have received a copy of the GNU General Public License version | |
| * 2 along with this work; if not, write to the Free Software Foundation, | |
| * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. | |
| * | |
| * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA | |
| * or visit www.oracle.com if you need additional information or have any | |
| * questions. | |
| */ | |
| package javax.management; | |
| import com.sun.jmx.mbeanserver.Introspector; | |
| import java.lang.reflect.InvocationHandler; | |
| import java.lang.reflect.Modifier; | |
| import java.lang.reflect.Proxy; | |
| /** | |
| * Static methods from the JMX API. There are no instances of this class. | |
| * | |
| * @since 1.6 | |
| */ | |
| public class JMX { | |
| /* Code within this package can prove that by providing this instance of | |
| * this class. | |
| */ | |
| static final JMX proof = new JMX(); | |
| private JMX() {} | |
| /** | |
| * The name of the <a href="Descriptor.html#defaultValue">{@code | |
| * defaultValue}</a> field. | |
| */ | |
| public static final String DEFAULT_VALUE_FIELD = "defaultValue"; | |
| /** | |
| * The name of the <a href="Descriptor.html#immutableInfo">{@code | |
| * immutableInfo}</a> field. | |
| */ | |
| public static final String IMMUTABLE_INFO_FIELD = "immutableInfo"; | |
| /** | |
| * The name of the <a href="Descriptor.html#interfaceClassName">{@code | |
| * interfaceClassName}</a> field. | |
| */ | |
| public static final String INTERFACE_CLASS_NAME_FIELD = "interfaceClassName"; | |
| /** | |
| * The name of the <a href="Descriptor.html#legalValues">{@code | |
| * legalValues}</a> field. | |
| */ | |
| public static final String LEGAL_VALUES_FIELD = "legalValues"; | |
| /** | |
| * The name of the <a href="Descriptor.html#maxValue">{@code | |
| * maxValue}</a> field. | |
| */ | |
| public static final String MAX_VALUE_FIELD = "maxValue"; | |
| /** | |
| * The name of the <a href="Descriptor.html#minValue">{@code | |
| * minValue}</a> field. | |
| */ | |
| public static final String MIN_VALUE_FIELD = "minValue"; | |
| /** | |
| * The name of the <a href="Descriptor.html#mxbean">{@code | |
| * mxbean}</a> field. | |
| */ | |
| public static final String MXBEAN_FIELD = "mxbean"; | |
| /** | |
| * The name of the <a href="Descriptor.html#openType">{@code | |
| * openType}</a> field. | |
| */ | |
| public static final String OPEN_TYPE_FIELD = "openType"; | |
| /** | |
| * The name of the <a href="Descriptor.html#originalType">{@code | |
| * originalType}</a> field. | |
| */ | |
| public static final String ORIGINAL_TYPE_FIELD = "originalType"; | |
| /** | |
| * <p>Make a proxy for a Standard MBean in a local or remote | |
| * MBean Server.</p> | |
| * | |
| * <p>If you have an MBean Server {@code mbs} containing an MBean | |
| * with {@link ObjectName} {@code name}, and if the MBean's | |
| * management interface is described by the Java interface | |
| * {@code MyMBean}, you can construct a proxy for the MBean like | |
| * this:</p> | |
| * | |
| * <pre> | |
| * MyMBean proxy = JMX.newMBeanProxy(mbs, name, MyMBean.class); | |
| * </pre> | |
| * | |
| * <p>Suppose, for example, {@code MyMBean} looks like this:</p> | |
| * | |
| * <pre> | |
| * public interface MyMBean { | |
| * public String getSomeAttribute(); | |
| * public void setSomeAttribute(String value); | |
| * public void someOperation(String param1, int param2); | |
| * } | |
| * </pre> | |
| * | |
| * <p>Then you can execute:</p> | |
| * | |
| * <ul> | |
| * | |
| * <li>{@code proxy.getSomeAttribute()} which will result in a | |
| * call to {@code mbs.}{@link MBeanServerConnection#getAttribute | |
| * getAttribute}{@code (name, "SomeAttribute")}. | |
| * | |
| * <li>{@code proxy.setSomeAttribute("whatever")} which will result | |
| * in a call to {@code mbs.}{@link MBeanServerConnection#setAttribute | |
| * setAttribute}{@code (name, new Attribute("SomeAttribute", "whatever"))}. | |
| * | |
| * <li>{@code proxy.someOperation("param1", 2)} which will be | |
| * translated into a call to {@code mbs.}{@link | |
| * MBeanServerConnection#invoke invoke}{@code (name, "someOperation", <etc>)}. | |
| * | |
| * </ul> | |
| * | |
| * <p>The object returned by this method is a | |
| * {@link Proxy} whose {@code InvocationHandler} is an | |
| * {@link MBeanServerInvocationHandler}.</p> | |
| * | |
| * <p>This method is equivalent to {@link | |
| * #newMBeanProxy(MBeanServerConnection, ObjectName, Class, | |
| * boolean) newMBeanProxy(connection, objectName, interfaceClass, | |
| * false)}.</p> | |
| * | |
| * @param connection the MBean server to forward to. | |
| * @param objectName the name of the MBean within | |
| * {@code connection} to forward to. | |
| * @param interfaceClass the management interface that the MBean | |
| * exports, which will also be implemented by the returned proxy. | |
| * | |
| * @param <T> allows the compiler to know that if the {@code | |
| * interfaceClass} parameter is {@code MyMBean.class}, for | |
| * example, then the return type is {@code MyMBean}. | |
| * | |
| * @return the new proxy instance. | |
| * | |
| * @throws IllegalArgumentException if {@code interfaceClass} is not | |
| * a <a href="package-summary.html#mgIface">compliant MBean | |
| * interface</a> | |
| */ | |
| public static <T> T newMBeanProxy(MBeanServerConnection connection, | |
| ObjectName objectName, | |
| Class<T> interfaceClass) { | |
| return newMBeanProxy(connection, objectName, interfaceClass, false); | |
| } | |
| /** | |
| * <p>Make a proxy for a Standard MBean in a local or remote MBean | |
| * Server that may also support the methods of {@link | |
| * NotificationEmitter}.</p> | |
| * | |
| * <p>This method behaves the same as {@link | |
| * #newMBeanProxy(MBeanServerConnection, ObjectName, Class)}, but | |
| * additionally, if {@code notificationEmitter} is {@code | |
| * true}, then the MBean is assumed to be a {@link | |
| * NotificationBroadcaster} or {@link NotificationEmitter} and the | |
| * returned proxy will implement {@link NotificationEmitter} as | |
| * well as {@code interfaceClass}. A call to {@link | |
| * NotificationBroadcaster#addNotificationListener} on the proxy | |
| * will result in a call to {@link | |
| * MBeanServerConnection#addNotificationListener(ObjectName, | |
| * NotificationListener, NotificationFilter, Object)}, and | |
| * likewise for the other methods of {@link | |
| * NotificationBroadcaster} and {@link NotificationEmitter}.</p> | |
| * | |
| * @param connection the MBean server to forward to. | |
| * @param objectName the name of the MBean within | |
| * {@code connection} to forward to. | |
| * @param interfaceClass the management interface that the MBean | |
| * exports, which will also be implemented by the returned proxy. | |
| * @param notificationEmitter make the returned proxy | |
| * implement {@link NotificationEmitter} by forwarding its methods | |
| * via {@code connection}. | |
| * | |
| * @param <T> allows the compiler to know that if the {@code | |
| * interfaceClass} parameter is {@code MyMBean.class}, for | |
| * example, then the return type is {@code MyMBean}. | |
| * | |
| * @return the new proxy instance. | |
| * | |
| * @throws IllegalArgumentException if {@code interfaceClass} is not | |
| * a <a href="package-summary.html#mgIface">compliant MBean | |
| * interface</a> | |
| */ | |
| public static <T> T newMBeanProxy(MBeanServerConnection connection, | |
| ObjectName objectName, | |
| Class<T> interfaceClass, | |
| boolean notificationEmitter) { | |
| return createProxy(connection, objectName, interfaceClass, notificationEmitter, false); | |
| } | |
| /** | |
| * Make a proxy for an MXBean in a local or remote MBean Server. | |
| * | |
| * <p>If you have an MBean Server {@code mbs} containing an | |
| * MXBean with {@link ObjectName} {@code name}, and if the | |
| * MXBean's management interface is described by the Java | |
| * interface {@code MyMXBean}, you can construct a proxy for | |
| * the MXBean like this:</p> | |
| * | |
| * <pre> | |
| * MyMXBean proxy = JMX.newMXBeanProxy(mbs, name, MyMXBean.class); | |
| * </pre> | |
| * | |
| * <p>Suppose, for example, {@code MyMXBean} looks like this:</p> | |
| * | |
| * <pre> | |
| * public interface MyMXBean { | |
| * public String getSimpleAttribute(); | |
| * public void setSimpleAttribute(String value); | |
| * public {@link java.lang.management.MemoryUsage} getMappedAttribute(); | |
| * public void setMappedAttribute(MemoryUsage memoryUsage); | |
| * public MemoryUsage someOperation(String param1, MemoryUsage param2); | |
| * } | |
| * </pre> | |
| * | |
| * <p>Then:</p> | |
| * | |
| * <ul> | |
| * | |
| * <li><p>{@code proxy.getSimpleAttribute()} will result in a | |
| * call to {@code mbs.}{@link MBeanServerConnection#getAttribute | |
| * getAttribute}{@code (name, "SimpleAttribute")}.</p> | |
| * | |
| * <li><p>{@code proxy.setSimpleAttribute("whatever")} will result | |
| * in a call to {@code mbs.}{@link | |
| * MBeanServerConnection#setAttribute setAttribute}<code>(name, | |
| * new Attribute("SimpleAttribute", "whatever"))</code>.</p> | |
| * | |
| * <p>Because {@code String} is a <em>simple type</em>, in the | |
| * sense of {@link javax.management.openmbean.SimpleType}, it | |
| * is not changed in the context of an MXBean. The MXBean | |
| * proxy behaves the same as a Standard MBean proxy (see | |
| * {@link #newMBeanProxy(MBeanServerConnection, ObjectName, | |
| * Class) newMBeanProxy}) for the attribute {@code | |
| * SimpleAttribute}.</p> | |
| * | |
| * <li><p>{@code proxy.getMappedAttribute()} will result in a call | |
| * to {@code mbs.getAttribute("MappedAttribute")}. The MXBean | |
| * mapping rules mean that the actual type of the attribute {@code | |
| * MappedAttribute} will be {@link | |
| * javax.management.openmbean.CompositeData CompositeData} and | |
| * that is what the {@code mbs.getAttribute} call will return. | |
| * The proxy will then convert the {@code CompositeData} back into | |
| * the expected type {@code MemoryUsage} using the MXBean mapping | |
| * rules.</p> | |
| * | |
| * <li><p>Similarly, {@code proxy.setMappedAttribute(memoryUsage)} | |
| * will convert the {@code MemoryUsage} argument into a {@code | |
| * CompositeData} before calling {@code mbs.setAttribute}.</p> | |
| * | |
| * <li><p>{@code proxy.someOperation("whatever", memoryUsage)} | |
| * will convert the {@code MemoryUsage} argument into a {@code | |
| * CompositeData} and call {@code mbs.invoke}. The value returned | |
| * by {@code mbs.invoke} will be also be a {@code CompositeData}, | |
| * and the proxy will convert this into the expected type {@code | |
| * MemoryUsage} using the MXBean mapping rules.</p> | |
| * | |
| * </ul> | |
| * | |
| * <p>The object returned by this method is a | |
| * {@link Proxy} whose {@code InvocationHandler} is an | |
| * {@link MBeanServerInvocationHandler}.</p> | |
| * | |
| * <p>This method is equivalent to {@link | |
| * #newMXBeanProxy(MBeanServerConnection, ObjectName, Class, | |
| * boolean) newMXBeanProxy(connection, objectName, interfaceClass, | |
| * false)}.</p> | |
| * | |
| * @param connection the MBean server to forward to. | |
| * @param objectName the name of the MBean within | |
| * {@code connection} to forward to. | |
| * @param interfaceClass the MXBean interface, | |
| * which will also be implemented by the returned proxy. | |
| * | |
| * @param <T> allows the compiler to know that if the {@code | |
| * interfaceClass} parameter is {@code MyMXBean.class}, for | |
| * example, then the return type is {@code MyMXBean}. | |
| * | |
| * @return the new proxy instance. | |
| * | |
| * @throws IllegalArgumentException if {@code interfaceClass} is not | |
| * a {@link javax.management.MXBean compliant MXBean interface} | |
| */ | |
| public static <T> T newMXBeanProxy(MBeanServerConnection connection, | |
| ObjectName objectName, | |
| Class<T> interfaceClass) { | |
| return newMXBeanProxy(connection, objectName, interfaceClass, false); | |
| } | |
| /** | |
| * <p>Make a proxy for an MXBean in a local or remote MBean | |
| * Server that may also support the methods of {@link | |
| * NotificationEmitter}.</p> | |
| * | |
| * <p>This method behaves the same as {@link | |
| * #newMXBeanProxy(MBeanServerConnection, ObjectName, Class)}, but | |
| * additionally, if {@code notificationEmitter} is {@code | |
| * true}, then the MXBean is assumed to be a {@link | |
| * NotificationBroadcaster} or {@link NotificationEmitter} and the | |
| * returned proxy will implement {@link NotificationEmitter} as | |
| * well as {@code interfaceClass}. A call to {@link | |
| * NotificationBroadcaster#addNotificationListener} on the proxy | |
| * will result in a call to {@link | |
| * MBeanServerConnection#addNotificationListener(ObjectName, | |
| * NotificationListener, NotificationFilter, Object)}, and | |
| * likewise for the other methods of {@link | |
| * NotificationBroadcaster} and {@link NotificationEmitter}.</p> | |
| * | |
| * @param connection the MBean server to forward to. | |
| * @param objectName the name of the MBean within | |
| * {@code connection} to forward to. | |
| * @param interfaceClass the MXBean interface, | |
| * which will also be implemented by the returned proxy. | |
| * @param notificationEmitter make the returned proxy | |
| * implement {@link NotificationEmitter} by forwarding its methods | |
| * via {@code connection}. | |
| * | |
| * @param <T> allows the compiler to know that if the {@code | |
| * interfaceClass} parameter is {@code MyMXBean.class}, for | |
| * example, then the return type is {@code MyMXBean}. | |
| * | |
| * @return the new proxy instance. | |
| * | |
| * @throws IllegalArgumentException if {@code interfaceClass} is not | |
| * a {@link javax.management.MXBean compliant MXBean interface} | |
| */ | |
| public static <T> T newMXBeanProxy(MBeanServerConnection connection, | |
| ObjectName objectName, | |
| Class<T> interfaceClass, | |
| boolean notificationEmitter) { | |
| return createProxy(connection, objectName, interfaceClass, notificationEmitter, true); | |
| } | |
| /** | |
| * <p>Test whether an interface is an MXBean interface. | |
| * An interface is an MXBean interface if it is public, | |
| * annotated {@link MXBean @MXBean} or {@code @MXBean(true)} | |
| * or if it does not have an {@code @MXBean} annotation | |
| * and its name ends with "{@code MXBean}".</p> | |
| * | |
| * @param interfaceClass The candidate interface. | |
| * | |
| * @return true if {@code interfaceClass} is a | |
| * {@link javax.management.MXBean compliant MXBean interface} | |
| * | |
| * @throws NullPointerException if {@code interfaceClass} is null. | |
| */ | |
| public static boolean isMXBeanInterface(Class<?> interfaceClass) { | |
| if (!interfaceClass.isInterface()) | |
| return false; | |
| if (!Modifier.isPublic(interfaceClass.getModifiers())) { | |
| return false; | |
| } | |
| MXBean a = interfaceClass.getAnnotation(MXBean.class); | |
| if (a != null) | |
| return a.value(); | |
| return interfaceClass.getName().endsWith("MXBean"); | |
| // We don't bother excluding the case where the name is | |
| // exactly the string "MXBean" since that would mean there | |
| // was no package name, which is pretty unlikely in practice. | |
| } | |
| /** | |
| * Centralised M(X)Bean proxy creation code | |
| * @param connection {@linkplain MBeanServerConnection} to use | |
| * @param objectName M(X)Bean object name | |
| * @param interfaceClass M(X)Bean interface class | |
| * @param notificationEmitter Is a notification emitter? | |
| * @param isMXBean Is an MXBean? | |
| * @return Returns an M(X)Bean proxy generated for the provided interface class | |
| * @throws SecurityException | |
| * @throws IllegalArgumentException | |
| */ | |
| private static <T> T createProxy(MBeanServerConnection connection, | |
| ObjectName objectName, | |
| Class<T> interfaceClass, | |
| boolean notificationEmitter, | |
| boolean isMXBean) { | |
| try { | |
| if (isMXBean) { | |
| // Check interface for MXBean compliance | |
| Introspector.testComplianceMXBeanInterface(interfaceClass); | |
| } else { | |
| // Check interface for MBean compliance | |
| Introspector.testComplianceMBeanInterface(interfaceClass); | |
| } | |
| } catch (NotCompliantMBeanException e) { | |
| throw new IllegalArgumentException(e); | |
| } | |
| InvocationHandler handler = new MBeanServerInvocationHandler( | |
| connection, objectName, isMXBean); | |
| final Class<?>[] interfaces; | |
| if (notificationEmitter) { | |
| interfaces = | |
| new Class<?>[] {interfaceClass, NotificationEmitter.class}; | |
| } else | |
| interfaces = new Class<?>[] {interfaceClass}; | |
| Object proxy = Proxy.newProxyInstance( | |
| interfaceClass.getClassLoader(), | |
| interfaces, | |
| handler); | |
| return interfaceClass.cast(proxy); | |
| } | |
| } |