/* * 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.IOException; 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.namespace.JMXNamespaces; 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 JMX() {} /** * The name of the {@code * defaultValue} field. */ public static final String DEFAULT_VALUE_FIELD = "defaultValue"; /** * The name of the {@code * descriptionResourceBundleBaseName} field. */ public static final String DESCRIPTION_RESOURCE_BUNDLE_BASE_NAME_FIELD = "descriptionResourceBundleBaseName"; /** * The name of the {@code * descriptionResourceKey} field. */ public static final String DESCRIPTION_RESOURCE_KEY_FIELD = "descriptionResourceKey"; /** * The name of the {@code * exceptions} field. */ public static final String EXCEPTIONS_FIELD = "exceptions"; /** * The name of the {@code * immutableInfo} field. */ public static final String IMMUTABLE_INFO_FIELD = "immutableInfo"; /** * The name of the {@code * interfaceClassName} field. */ public static final String INTERFACE_CLASS_NAME_FIELD = "interfaceClassName"; /** * The name of the {@code * legalValues} field. */ public static final String LEGAL_VALUES_FIELD = "legalValues"; /** * The name of the {@code locale} * field. */ public static final String LOCALE_FIELD = "locale"; /** * The name of the {@code * maxValue} field. */ public static final String MAX_VALUE_FIELD = "maxValue"; /** * The name of the {@code * minValue} field. */ public static final String MIN_VALUE_FIELD = "minValue"; /** * The name of the {@code * mxbean} field. */ public static final String MXBEAN_FIELD = "mxbean"; /** * The name of the * {@code * mxbeanMappingFactoryClass} field. */ public static final String MXBEAN_MAPPING_FACTORY_CLASS_FIELD = "mxbeanMappingFactoryClass"; /** * The name of the {@code * openType} field. */ public static final String OPEN_TYPE_FIELD = "openType"; /** * The name of the {@code * originalType} field. */ public static final String ORIGINAL_TYPE_FIELD = "originalType"; /** * The name of the {@code * setExceptions} field. */ public static final String SET_EXCEPTIONS_FIELD = "setExceptions"; /** *
Options to apply to an MBean proxy or to an instance of {@link * StandardMBean}.
* *For example, to specify the "wrapped object visible" option for a * {@code StandardMBean}, you might write this:
* ** StandardMBean.Options opts = new StandardMBean.Options(); * opts.setWrappedObjectVisible(true); * StandardMBean mbean = new StandardMBean(impl, intf, opts); ** * @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; /** *
Construct an {@code MBeanOptions} object where all options have * their default values.
*/ public MBeanOptions() {} @Override public MBeanOptions clone() { try { return (MBeanOptions) super.clone(); } catch (CloneNotSupportedException e) { throw new AssertionError(e); } } /** *True if this is an MXBean proxy or a StandardMBean instance * that is an MXBean. The default value is false.
* *This method is equivalent to {@link #getMXBeanMappingFactory() * this.getMXBeanMappingFactory()}{@code != null}.
* * @return true if this is an MXBean proxy or a StandardMBean instance * that is an MXBean. */ public boolean isMXBean() { return (this.mappingFactory != null); } /** *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.
* * @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; } /** *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.
* *Examples:
** 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); ** * @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
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.
* @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(); } } /** *Options to apply to an MBean proxy.
* * @see #newMBeanProxy */ public static class ProxyOptions extends MBeanOptions { private static final long serialVersionUID = 7238804866098386559L; private boolean notificationEmitter; /** *Construct a {@code ProxyOptions} object where all options have * their default values.
*/ public ProxyOptions() {} @Override public ProxyOptions clone() { return (ProxyOptions) super.clone(); } /** *Defines whether the returned proxy should * implement {@link NotificationEmitter}. The default value is false.
* * @return true if this proxy will be a NotificationEmitter. * * @see JMX#newMBeanProxy(MBeanServerConnection, ObjectName, Class, * MBeanOptions) */ public boolean isNotificationEmitter() { return this.notificationEmitter; } /** *Set the {@link #isNotificationEmitter NotificationEmitter} option to * the given value.
* @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); } } /** *Make a proxy for a Standard MBean in a local or remote * MBean Server.
* *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:
* ** MyMBean proxy = JMX.newMBeanProxy(mbs, name, MyMBean.class); ** *
Suppose, for example, {@code MyMBean} looks like this:
* ** public interface MyMBean { * public String getSomeAttribute(); * public void setSomeAttribute(String value); * public void someOperation(String param1, int param2); * } ** *
Then you can execute:
* *The object returned by this method is a * {@link Proxy} whose {@code InvocationHandler} is an * {@link MBeanServerInvocationHandler}.
* *This method is equivalent to {@link * #newMBeanProxy(MBeanServerConnection, ObjectName, Class, * boolean) newMBeanProxy(connection, objectName, interfaceClass, * false)}.
* * @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. * * @paramMake a proxy for a Standard MBean in a local or remote MBean * Server that may also support the methods of {@link * NotificationEmitter}.
* *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}.
* *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.
* * @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}. * @paramMake a proxy for an MXBean in a local or remote * MBean Server.
* *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:
* ** MyMXBean proxy = JMX.newMXBeanProxy(mbs, name, MyMXBean.class); ** *
Suppose, for example, {@code MyMXBean} looks like this:
* ** 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); * } ** *
Then:
* *{@code proxy.getSimpleAttribute()} will result in a * call to {@code mbs.}{@link MBeanServerConnection#getAttribute * getAttribute}{@code (name, "SimpleAttribute")}.
* *{@code proxy.setSimpleAttribute("whatever")} will result
* in a call to {@code mbs.}{@link
* MBeanServerConnection#setAttribute setAttribute}(name,
* new Attribute("SimpleAttribute", "whatever"))
.
* *
Because {@code String} is a simple type, 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}.
* *{@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.
* *Similarly, {@code proxy.setMappedAttribute(memoryUsage)} * will convert the {@code MemoryUsage} argument into a {@code * CompositeData} before calling {@code mbs.setAttribute}.
* *{@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.
* *This method is equivalent to {@link * #newMXBeanProxy(MBeanServerConnection, ObjectName, Class, * boolean) newMXBeanProxy(connection, objectName, interfaceClass, * false)}.
* * @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. * * @paramMake a proxy for an MXBean in a local or remote MBean * Server that may also support the methods of {@link * NotificationEmitter}.
* *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}.
* *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.
* * @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}. * @paramMake 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.
* *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}.
* *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...
* ** FooMBean proxy = * JMX.newMBeanProxy(connection, objectName, FooMBean.class, opts); ** *
...then you can get the {@code MBeanServerInvocationHandler} like * this...
* ** MBeanServerInvocationHandler mbsih = (MBeanServerInvocationHandler) * {@link Proxy#getInvocationHandler(Object) * Proxy.getInvocationHandler}(proxy); ** *
...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()}.
* * @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. * @paramTest 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}".
* * @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. } /** *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 resource * injection to obtain an instance of {@link SendNotification} * through which it can send notifications.
* * @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 { for (int i = 0; i < 2; i++) { if (mbean instanceof NotificationBroadcaster || MBeanInjector.injectsSendNotification(mbean)) return true; if (mbean instanceof DynamicWrapperMBean) mbean = ((DynamicWrapperMBean) mbean).getWrappedObject(); else break; } return false; } /** *Return the version of the JMX specification that a (possibly remote) * MBean Server is using. The JMX specification described in this * documentation is version 2.0. The earlier versions that might be * reported by this method are 1.0, 1.1, 1.2, and 1.4. (There is no 1.3.) * All of these versions and all future versions can be compared using * {@link String#compareTo(String)}. So, for example, to tell if * {@code mbsc} is running at least version 2.0 you can write:
* ** String version = JMX.getSpecificationVersion(mbsc, null); * boolean atLeast2dot0 = (version.compareTo("2.0") >= 0); ** *
A remote MBean Server might be running an earlier version of the * JMX API, and in that case certain * features might not be available in it.
* *The version of the MBean Server {@code mbsc} is not necessarily * the version of all namespaces within that MBean Server, for example * if some of them use {@link javax.management.namespace.JMXRemoteNamespace * JMXRemoteNamespace}. To determine the version of the namespace * that a particular MBean is in, give its name as the {@code mbeanName} * parameter.
* * @param mbsc a connection to an MBean Server. * * @param mbeanName the name of an MBean within that MBean Server, or null. * If non-null, the namespace of this name, as determined by * {@link JMXNamespaces#getContainingNamespace * JMXNamespaces.getContainingNamespace}, is the one whose specification * version will be returned. * * @return the JMX specification version reported by that MBean Server. * * @throws IllegalArgumentException if {@code mbsc} is null, or if * {@code mbeanName} includes a wildcard character ({@code *} or {@code ?}) * in its namespace. * * @throws IOException if the version cannot be obtained, either because * there is a communication problem or because the remote MBean Server * does not have the appropriate {@linkplain * MBeanServerDelegateMBean#getSpecificationVersion() attribute}. * * @see Interoperability between * versions of the JMX specification * @see MBeanServerDelegateMBean#getSpecificationVersion */ public static String getSpecificationVersion( MBeanServerConnection mbsc, ObjectName mbeanName) throws IOException { if (mbsc == null) throw new IllegalArgumentException("Null MBeanServerConnection"); String namespace; if (mbeanName == null) namespace = ""; else namespace = JMXNamespaces.getContainingNamespace(mbeanName); if (namespace.contains("*") || namespace.contains("?")) { throw new IllegalArgumentException( "ObjectName contains namespace wildcard: " + mbeanName); } try { if (namespace.length() > 0) mbsc = JMXNamespaces.narrowToNamespace(mbsc, namespace); return (String) mbsc.getAttribute( MBeanServerDelegate.DELEGATE_NAME, "SpecificationVersion"); } catch (IOException e) { throw e; } catch (Exception e) { throw new IOException(e); } } }