You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
437 lines
17 KiB
437 lines
17 KiB
/*
|
|
* Copyright (c) 2005, 2013, Oracle and/or its affiliates. All rights reserved.
|
|
* ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*/
|
|
|
|
package javax.management;
|
|
|
|
import com.sun.jmx.mbeanserver.Introspector;
|
|
import java.lang.reflect.InvocationHandler;
|
|
import java.lang.reflect.Modifier;
|
|
import java.lang.reflect.Proxy;
|
|
import sun.reflect.misc.ReflectUtil;
|
|
|
|
/**
|
|
* 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()) &&
|
|
!Introspector.ALLOW_NONPUBLIC_MBEAN) {
|
|
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);
|
|
}
|
|
}
|