| /* |
| * Copyright 2005-2006 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> |
| * |
| * @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>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) { |
| 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> |
| * |
| * @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)); |
| } |
| } |