blob: 7f8cc411964c08d9d3f1bd8441d68326dfe8e99f [file] [log] [blame]
/*
* Copyright (c) 2005, 2006, 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;
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.
*/
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.
*/
public static <T> T newMBeanProxy(MBeanServerConnection connection,
ObjectName objectName,
Class<T> interfaceClass,
boolean notificationEmitter) {
return createProxy(connection, objectName, interfaceClass, notificationEmitter, false);
}
/**
* <p>Make a proxy for an MXBean in a local or remote
* MBean Server.</p>
*
* <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.
*/
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.
*/
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 annotated
* {@link MXBean &#64;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 an interface and
* meets the conditions described.
*
* @throws NullPointerException if {@code interfaceClass} is null.
*/
public static boolean isMXBeanInterface(Class<?> interfaceClass) {
if (!interfaceClass.isInterface())
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) {
if (System.getSecurityManager() != null) {
checkProxyInterface(interfaceClass);
}
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);
}
/**
* Checks for the M(X)Bean proxy interface being public and not restricted
* @param interfaceClass MBean proxy interface
* @throws SecurityException when the proxy interface comes from a restricted
* package or is not public
*/
private static void checkProxyInterface(Class<?> interfaceClass) {
if (!Modifier.isPublic(interfaceClass.getModifiers())) {
throw new SecurityException("mbean proxy interface non-public");
}
ReflectUtil.checkPackageAccess(interfaceClass);
}
}