6450848: make it easier to get the ObjectName of a JMX Proxy
Summary: Rework proxy javadoc to explain how to do this.
Reviewed-by: sjiang
/*
* Copyright 2005-2008 Sun Microsystems, Inc. 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. Sun designates this
* particular file as subject to the "Classpath" exception as provided
* by Sun 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
* CA 95054 USA or visit www.sun.com if you need additional information or
* have any questions.
*/
package javax.management;
import com.sun.jmx.mbeanserver.Introspector;
import com.sun.jmx.mbeanserver.MBeanInjector;
import com.sun.jmx.remote.util.ClassLogger;
import java.beans.BeanInfo;
import java.beans.PropertyDescriptor;
import java.io.Serializable;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Map;
import java.util.TreeMap;
import javax.management.openmbean.MXBeanMappingFactory;
/**
* 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 static final ClassLogger logger =
new ClassLogger("javax.management.misc", "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#mxbeanMappingFactoryClass">{@code
* mxbeanMappingFactoryClass}</a> field.
*/
public static final String MXBEAN_MAPPING_FACTORY_CLASS_FIELD =
"mxbeanMappingFactoryClass";
/**
* 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>Options to apply to an MBean proxy or to an instance of {@link
* StandardMBean}.</p>
*
* <p>For example, to specify a custom {@link MXBeanMappingFactory}
* for a {@code StandardMBean}, you might write this:</p>
*
* <pre>
* MXBeanMappingFactory factory = new MyMXBeanMappingFactory();
* JMX.MBeanOptions opts = new JMX.MBeanOptions();
* opts.setMXBeanMappingFactory(factory);
* StandardMBean mbean = new StandardMBean(impl, intf, opts);
* </pre>
*
* @see javax.management.JMX.ProxyOptions
* @see javax.management.StandardMBean.Options
*/
public static class MBeanOptions implements Serializable, Cloneable {
private static final long serialVersionUID = -6380842449318177843L;
static final MBeanOptions MXBEAN = new MBeanOptions();
static {
MXBEAN.setMXBeanMappingFactory(MXBeanMappingFactory.DEFAULT);
}
private MXBeanMappingFactory mappingFactory;
/**
* <p>Construct an {@code MBeanOptions} object where all options have
* their default values.</p>
*/
public MBeanOptions() {}
@Override
public MBeanOptions clone() {
try {
return (MBeanOptions) super.clone();
} catch (CloneNotSupportedException e) {
throw new AssertionError(e);
}
}
/**
* <p>True if this is an MXBean proxy or a StandardMBean instance
* that is an MXBean. The default value is false.</p>
*
* <p>This method is equivalent to {@link #getMXBeanMappingFactory()
* this.getMXBeanMappingFactory()}{@code != null}.</p>
*
* @return true if this is an MXBean proxy or a StandardMBean instance
* that is an MXBean.
*/
public boolean isMXBean() {
return (this.mappingFactory != null);
}
/**
* <p>The mappings between Java types and Open Types to be used in
* an MXBean proxy or a StandardMBean instance that is an MXBean,
* or null if this instance is not for an MXBean.
* The default value is null.</p>
*
* @return the mappings to be used in this proxy or StandardMBean,
* or null if this instance is not for an MXBean.
*/
public MXBeanMappingFactory getMXBeanMappingFactory() {
return mappingFactory;
}
/**
* <p>Set the {@link #getMXBeanMappingFactory() MXBeanMappingFactory} to
* the given value. The value should be null if this instance is not
* for an MXBean. If this instance is for an MXBean, the value should
* usually be either a custom mapping factory, or
* {@link MXBeanMappingFactory#forInterface
* MXBeanMappingFactory.forInterface}{@code (mxbeanInterface)}
* which signifies
* that the {@linkplain MXBeanMappingFactory#DEFAULT default} mapping
* factory should be used unless an {@code @}{@link
* javax.management.openmbean.MXBeanMappingFactoryClass
* MXBeanMappingFactoryClass} annotation on {@code mxbeanInterface}
* specifies otherwise.</p>
*
* <p>Examples:</p>
* <pre>
* MBeanOptions opts = new MBeanOptions();
* opts.setMXBeanMappingFactory(myMappingFactory);
* MyMXBean proxy = JMX.newMBeanProxy(
* mbeanServerConnection, objectName, MyMXBean.class, opts);
*
* // ...or...
*
* MBeanOptions opts = new MBeanOptions();
* MXBeanMappingFactory defaultFactoryForMyMXBean =
* MXBeanMappingFactory.forInterface(MyMXBean.class);
* opts.setMXBeanMappingFactory(defaultFactoryForMyMXBean);
* MyMXBean proxy = JMX.newMBeanProxy(
* mbeanServerConnection, objectName, MyMXBean.class, opts);
* </pre>
*
* @param f the new value. If null, this instance is not for an
* MXBean.
*/
public void setMXBeanMappingFactory(MXBeanMappingFactory f) {
this.mappingFactory = f;
}
/* To maximise object sharing, classes in this package can replace
* a private MBeanOptions with no MXBeanMappingFactory with one
* of these shared instances. But they must be EXTREMELY careful
* never to give out the shared instances to user code, which could
* modify them.
*/
private static final MBeanOptions[] CANONICALS = {
new MBeanOptions(), MXBEAN,
};
// Overridden in local subclasses:
MBeanOptions[] canonicals() {
return CANONICALS;
}
// This is only used by the logic for canonical instances.
// Overridden in local subclasses:
boolean same(MBeanOptions opt) {
return (opt.mappingFactory == mappingFactory);
}
final MBeanOptions canonical() {
for (MBeanOptions opt : canonicals()) {
if (opt.getClass() == this.getClass() && same(opt))
return opt;
}
return this;
}
final MBeanOptions uncanonical() {
for (MBeanOptions opt : canonicals()) {
if (this == opt)
return clone();
}
return this;
}
private Map<String, Object> toMap() {
Map<String, Object> map = new TreeMap<String, Object>();
try {
BeanInfo bi = java.beans.Introspector.getBeanInfo(getClass());
PropertyDescriptor[] pds = bi.getPropertyDescriptors();
for (PropertyDescriptor pd : pds) {
String name = pd.getName();
if (name.equals("class"))
continue;
Method get = pd.getReadMethod();
if (get != null)
map.put(name, get.invoke(this));
}
} catch (Exception e) {
Throwable t = e;
if (t instanceof InvocationTargetException)
t = t.getCause();
map.put("Exception", t);
}
return map;
}
@Override
public String toString() {
return getClass().getSimpleName() + toMap();
// For example "MBeanOptions{MXBean=true, <etc>}".
}
/**
* <p>Indicates whether some other object is "equal to" this one. The
* result is true if and only if the other object is also an instance
* of MBeanOptions or a subclass, and has the same properties with
* the same values.</p>
* @return {@inheritDoc}
*/
@Override
public boolean equals(Object obj) {
if (obj == this)
return true;
if (obj == null || obj.getClass() != this.getClass())
return false;
return toMap().equals(((MBeanOptions) obj).toMap());
}
@Override
public int hashCode() {
return toMap().hashCode();
}
}
/**
* <p>Options to apply to an MBean proxy.</p>
*
* @see #newMBeanProxy
*/
public static class ProxyOptions extends MBeanOptions {
private static final long serialVersionUID = 7238804866098386559L;
private boolean notificationEmitter;
/**
* <p>Construct a {@code ProxyOptions} object where all options have
* their default values.</p>
*/
public ProxyOptions() {}
@Override
public ProxyOptions clone() {
return (ProxyOptions) super.clone();
}
/**
* <p>Defines whether the returned proxy should
* implement {@link NotificationEmitter}. The default value is false.</p>
*
* @return true if this proxy will be a NotificationEmitter.
*
* @see JMX#newMBeanProxy(MBeanServerConnection, ObjectName, Class,
* MBeanOptions)
*/
public boolean isNotificationEmitter() {
return this.notificationEmitter;
}
/**
* <p>Set the {@link #isNotificationEmitter NotificationEmitter} option to
* the given value.</p>
* @param emitter the new value.
*/
public void setNotificationEmitter(boolean emitter) {
this.notificationEmitter = emitter;
}
// Canonical objects for each of (MXBean,!MXBean) x (Emitter,!Emitter)
private static final ProxyOptions[] CANONICALS = {
new ProxyOptions(), new ProxyOptions(),
new ProxyOptions(), new ProxyOptions(),
};
static {
CANONICALS[1].setMXBeanMappingFactory(MXBeanMappingFactory.DEFAULT);
CANONICALS[2].setNotificationEmitter(true);
CANONICALS[3].setMXBeanMappingFactory(MXBeanMappingFactory.DEFAULT);
CANONICALS[3].setNotificationEmitter(true);
}
@Override
MBeanOptions[] canonicals() {
return CANONICALS;
}
@Override
boolean same(MBeanOptions opt) {
return (super.same(opt) && opt instanceof ProxyOptions &&
((ProxyOptions) opt).notificationEmitter == notificationEmitter);
}
}
/**
* <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>
*
* <p>This method is equivalent to {@link
* #newMBeanProxy(MBeanServerConnection, ObjectName, Class, JMX.MBeanOptions)
* newMBeanProxy(connection, objectName, interfaceClass, opts)}, where
* {@code opts} is a {@link JMX.ProxyOptions} representing the
* {@code notificationEmitter} parameter.</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) {
ProxyOptions opts = new ProxyOptions();
opts.setNotificationEmitter(notificationEmitter);
return newMBeanProxy(connection, objectName, interfaceClass, opts);
}
/**
* <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>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>
*
* <p>This method is equivalent to {@link
* #newMBeanProxy(MBeanServerConnection, ObjectName, Class, JMX.MBeanOptions)
* newMBeanProxy(connection, objectName, interfaceClass, opts)}, where
* {@code opts} is a {@link JMX.ProxyOptions} where the {@link
* JMX.ProxyOptions#getMXBeanMappingFactory() MXBeanMappingFactory}
* property is
* {@link MXBeanMappingFactory#forInterface(Class)
* MXBeanMappingFactory.forInterface(interfaceClass)} and the {@link
* JMX.ProxyOptions#isNotificationEmitter() notificationEmitter} property
* is equal to the {@code notificationEmitter} parameter.</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) {
ProxyOptions opts = new ProxyOptions();
MXBeanMappingFactory f = MXBeanMappingFactory.forInterface(interfaceClass);
opts.setMXBeanMappingFactory(f);
opts.setNotificationEmitter(notificationEmitter);
return newMBeanProxy(connection, objectName, interfaceClass, opts);
}
/**
* <p>Make a proxy for a Standard MBean or MXBean in a local or remote MBean
* Server that may also support the methods of {@link
* NotificationEmitter} and (for an MXBean) that may define custom MXBean
* type mappings.</p>
*
* <p>This method behaves the same as
* {@link #newMBeanProxy(MBeanServerConnection, ObjectName, Class)} or
* {@link #newMXBeanProxy(MBeanServerConnection, ObjectName, Class)},
* according as {@code opts.isMXBean()} is respectively false or true; but
* with the following changes based on {@code opts}.</p>
*
* <ul>
* <li>If {@code opts.isNotificationEmitter()} 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}.</li>
*
* <li>If {@code opts.getMXBeanMappingFactory()} is not null,
* then the mappings it defines will be applied to convert between
* arbitrary Java types and Open Types.</li>
* </ul>
*
* <p>The object returned by this method is a
* {@link Proxy} whose {@code InvocationHandler} is an
* {@link MBeanServerInvocationHandler}. This means that it is possible
* to retrieve the parameters that were used to produce the proxy. If the
* proxy was produced as follows...</p>
*
* <pre>
* FooMBean proxy =
* JMX.newMBeanProxy(connection, objectName, FooMBean.class, opts);
* </pre>
*
* <p>...then you can get the {@code MBeanServerInvocationHandler} like
* this...</p>
*
* <pre>
* MBeanServerInvocationHandler mbsih = (MBeanServerInvocationHandler)
* {@link Proxy#getInvocationHandler(Object)
* Proxy.getInvocationHandler}(proxy);
* </pre>
*
* <p>...and you can retrieve {@code connection}, {@code
* objectName}, and {@code opts} using the {@link
* MBeanServerInvocationHandler#getMBeanServerConnection()
* getMBeanServerConnection()}, {@link
* MBeanServerInvocationHandler#getObjectName() getObjectName()}, and
* {@link MBeanServerInvocationHandler#getMBeanOptions() getMBeanOptions()}
* methods on {@code mbsih}. You can retrieve {@code FooMBean.class}
* using {@code proxy.getClass().}{@link
* Class#getInterfaces() getInterfaces()}.</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 Standard MBean or MXBean interface,
* which will also be implemented by the returned proxy.
* @param opts the options to apply for this proxy. Can be null,
* in which case default options are applied.
* @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
* valid MXBean interface.
*/
public static <T> T newMBeanProxy(MBeanServerConnection connection,
ObjectName objectName,
Class<T> interfaceClass,
MBeanOptions opts) {
try {
return newMBeanProxy2(connection, objectName, interfaceClass, opts);
} catch (NotCompliantMBeanException e) {
throw new IllegalArgumentException(e);
}
}
private static <T> T newMBeanProxy2(MBeanServerConnection connection,
ObjectName objectName,
Class<T> interfaceClass,
MBeanOptions opts)
throws NotCompliantMBeanException {
if (opts == null)
opts = new MBeanOptions();
boolean notificationEmitter = opts instanceof ProxyOptions &&
((ProxyOptions) opts).isNotificationEmitter();
MXBeanMappingFactory mappingFactory = opts.getMXBeanMappingFactory();
if (mappingFactory != null) {
// Check interface for MXBean compliance
Introspector.testComplianceMXBeanInterface(interfaceClass,
mappingFactory);
}
InvocationHandler handler = new MBeanServerInvocationHandler(
connection, objectName, opts);
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);
}
/**
* <p>Test whether an interface is an MXBean interface.
* An interface is an MXBean interface if it is 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 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.
}
/**
* <p>Test if an MBean can emit notifications. An MBean can emit
* notifications if either it implements {@link NotificationBroadcaster}
* (perhaps through its child interface {@link NotificationEmitter}), or
* it uses <a href="MBeanRegistration.html#injection">resource
* injection</a> to obtain an instance of {@link SendNotification}
* through which it can send notifications.</p>
*
* @param mbean an MBean object.
* @return true if the given object is a valid MBean that can emit
* notifications; false if the object is a valid MBean but that
* cannot emit notifications.
* @throws NotCompliantMBeanException if the given object is not
* a valid MBean.
*/
public static boolean isNotificationSource(Object mbean)
throws NotCompliantMBeanException {
if (mbean instanceof NotificationBroadcaster)
return true;
Object resource = (mbean instanceof DynamicWrapperMBean) ?
((DynamicWrapperMBean) mbean).getWrappedObject() : mbean;
return (MBeanInjector.injectsSendNotification(resource));
}
}