--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/java.management/share/classes/javax/management/StandardMBean.java Tue Sep 12 19:03:39 2017 +0200
@@ -0,0 +1,1234 @@
+/*
+ * Copyright (c) 2002, 2017, 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 static com.sun.jmx.defaults.JmxProperties.MISC_LOGGER;
+import com.sun.jmx.mbeanserver.DescriptorCache;
+import com.sun.jmx.mbeanserver.Introspector;
+import com.sun.jmx.mbeanserver.MBeanSupport;
+import com.sun.jmx.mbeanserver.MXBeanSupport;
+import com.sun.jmx.mbeanserver.StandardMBeanSupport;
+import com.sun.jmx.mbeanserver.Util;
+
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.WeakHashMap;
+import java.lang.System.Logger.Level;
+import javax.management.openmbean.OpenMBeanAttributeInfo;
+import javax.management.openmbean.OpenMBeanAttributeInfoSupport;
+import javax.management.openmbean.OpenMBeanConstructorInfo;
+import javax.management.openmbean.OpenMBeanConstructorInfoSupport;
+import javax.management.openmbean.OpenMBeanOperationInfo;
+import javax.management.openmbean.OpenMBeanOperationInfoSupport;
+import javax.management.openmbean.OpenMBeanParameterInfo;
+import javax.management.openmbean.OpenMBeanParameterInfoSupport;
+
+/**
+ * <p>An MBean whose management interface is determined by reflection
+ * on a Java interface.</p>
+ *
+ * <p>This class brings more flexibility to the notion of Management
+ * Interface in the use of Standard MBeans. Straightforward use of
+ * the patterns for Standard MBeans described in the JMX Specification
+ * means that there is a fixed relationship between the implementation
+ * class of an MBean and its management interface (i.e., if the
+ * implementation class is Thing, the management interface must be
+ * ThingMBean). This class makes it possible to keep the convenience
+ * of specifying the management interface with a Java interface,
+ * without requiring that there be any naming relationship between the
+ * implementation and interface classes.</p>
+ *
+ * <p>By making a DynamicMBean out of an MBean, this class makes
+ * it possible to select any interface implemented by the MBean as its
+ * management interface, provided that it complies with JMX patterns
+ * (i.e., attributes defined by getter/setter etc...).</p>
+ *
+ * <p> This class also provides hooks that make it possible to supply
+ * custom descriptions and names for the {@link MBeanInfo} returned by
+ * the DynamicMBean interface.</p>
+ *
+ * <p>Using this class, an MBean can be created with any
+ * implementation class name <i>Impl</i> and with a management
+ * interface defined (as for current Standard MBeans) by any interface
+ * <i>Intf</i>, in one of two general ways:</p>
+ *
+ * <ul>
+ *
+ * <li>Using the public constructor
+ * {@link #StandardMBean(java.lang.Object, java.lang.Class, boolean)
+ * StandardMBean(impl,interface)}:
+ * <pre>
+ * MBeanServer mbs;
+ * ...
+ * Impl impl = new Impl(...);
+ * StandardMBean mbean = new StandardMBean(impl, Intf.class, false);
+ * mbs.registerMBean(mbean, objectName);
+ * </pre></li>
+ *
+ * <li>Subclassing StandardMBean:
+ * <pre>
+ * public class Impl extends StandardMBean implements Intf {
+ * public Impl() {
+ * super(Intf.class, false);
+ * }
+ * // implement methods of Intf
+ * }
+ *
+ * [...]
+ *
+ * MBeanServer mbs;
+ * ....
+ * Impl impl = new Impl();
+ * mbs.registerMBean(impl, objectName);
+ * </pre></li>
+ *
+ * </ul>
+ *
+ * <p>In either case, the class <i>Impl</i> must implement the
+ * interface <i>Intf</i>.</p>
+ *
+ * <p>Standard MBeans based on the naming relationship between
+ * implementation and interface classes are of course still
+ * available.</p>
+ *
+ * <p>This class may also be used to construct MXBeans. The usage
+ * is exactly the same as for Standard MBeans except that in the
+ * examples above, the {@code false} parameter to the constructor or
+ * {@code super(...)} invocation is instead {@code true}.</p>
+ *
+ * @since 1.5
+ */
+public class StandardMBean implements DynamicMBean, MBeanRegistration {
+
+ private final static DescriptorCache descriptors =
+ DescriptorCache.getInstance(JMX.proof);
+
+ /**
+ * The DynamicMBean that wraps the MXBean or Standard MBean implementation.
+ **/
+ private volatile MBeanSupport<?> mbean;
+
+ /**
+ * The cached MBeanInfo.
+ **/
+ private volatile MBeanInfo cachedMBeanInfo;
+
+ /**
+ * Make a DynamicMBean out of <var>implementation</var>, using the
+ * specified <var>mbeanInterface</var> class.
+ * @param implementation The implementation of this MBean.
+ * If <code>null</code>, and null implementation is allowed,
+ * then the implementation is assumed to be <var>this</var>.
+ * @param mbeanInterface The Management Interface exported by this
+ * MBean's implementation. If <code>null</code>, then this
+ * object will use standard JMX design pattern to determine
+ * the management interface associated with the given
+ * implementation.
+ * @param nullImplementationAllowed <code>true</code> if a null
+ * implementation is allowed. If null implementation is allowed,
+ * and a null implementation is passed, then the implementation
+ * is assumed to be <var>this</var>.
+ * @exception IllegalArgumentException if the given
+ * <var>implementation</var> is null, and null is not allowed.
+ **/
+ private <T> void construct(T implementation, Class<T> mbeanInterface,
+ boolean nullImplementationAllowed,
+ boolean isMXBean)
+ throws NotCompliantMBeanException {
+ if (implementation == null) {
+ // Have to use (T)this rather than mbeanInterface.cast(this)
+ // because mbeanInterface might be null.
+ if (nullImplementationAllowed)
+ implementation = Util.<T>cast(this);
+ else throw new IllegalArgumentException("implementation is null");
+ }
+ if (isMXBean) {
+ if (mbeanInterface == null) {
+ mbeanInterface = Util.cast(Introspector.getMXBeanInterface(
+ implementation.getClass()));
+ }
+ this.mbean = new MXBeanSupport(implementation, mbeanInterface);
+ } else {
+ if (mbeanInterface == null) {
+ mbeanInterface = Util.cast(Introspector.getStandardMBeanInterface(
+ implementation.getClass()));
+ }
+ this.mbean =
+ new StandardMBeanSupport(implementation, mbeanInterface);
+ }
+ }
+
+ /**
+ * <p>Make a DynamicMBean out of the object
+ * <var>implementation</var>, using the specified
+ * <var>mbeanInterface</var> class.</p>
+ *
+ * @param implementation The implementation of this MBean.
+ * @param mbeanInterface The Management Interface exported by this
+ * MBean's implementation. If <code>null</code>, then this
+ * object will use standard JMX design pattern to determine
+ * the management interface associated with the given
+ * implementation.
+ * @param <T> Allows the compiler to check
+ * that {@code implementation} does indeed implement the class
+ * described by {@code mbeanInterface}. The compiler can only
+ * check this if {@code mbeanInterface} is a class literal such
+ * as {@code MyMBean.class}.
+ *
+ * @exception IllegalArgumentException if the given
+ * <var>implementation</var> is null.
+ * @exception NotCompliantMBeanException if the <var>mbeanInterface</var>
+ * does not follow JMX design patterns for Management Interfaces, or
+ * if the given <var>implementation</var> does not implement the
+ * specified interface.
+ **/
+ public <T> StandardMBean(T implementation, Class<T> mbeanInterface)
+ throws NotCompliantMBeanException {
+ construct(implementation, mbeanInterface, false, false);
+ }
+
+ /**
+ * <p>Make a DynamicMBean out of <var>this</var>, using the specified
+ * <var>mbeanInterface</var> class.</p>
+ *
+ * <p>Calls {@link #StandardMBean(java.lang.Object, java.lang.Class)
+ * this(this,mbeanInterface)}.
+ * This constructor is reserved to subclasses.</p>
+ *
+ * @param mbeanInterface The Management Interface exported by this
+ * MBean.
+ *
+ * @exception NotCompliantMBeanException if the <var>mbeanInterface</var>
+ * does not follow JMX design patterns for Management Interfaces, or
+ * if <var>this</var> does not implement the specified interface.
+ **/
+ protected StandardMBean(Class<?> mbeanInterface)
+ throws NotCompliantMBeanException {
+ construct(null, mbeanInterface, true, false);
+ }
+
+ /**
+ * <p>Make a DynamicMBean out of the object
+ * <var>implementation</var>, using the specified
+ * <var>mbeanInterface</var> class, and choosing whether the
+ * resultant MBean is an MXBean. This constructor can be used
+ * to make either Standard MBeans or MXBeans. Unlike the
+ * constructor {@link #StandardMBean(Object, Class)}, it
+ * does not throw NotCompliantMBeanException.</p>
+ *
+ * @param implementation The implementation of this MBean.
+ * @param mbeanInterface The Management Interface exported by this
+ * MBean's implementation. If <code>null</code>, then this
+ * object will use standard JMX design pattern to determine
+ * the management interface associated with the given
+ * implementation.
+ * @param isMXBean If true, the {@code mbeanInterface} parameter
+ * names an MXBean interface and the resultant MBean is an MXBean.
+ * @param <T> Allows the compiler to check
+ * that {@code implementation} does indeed implement the class
+ * described by {@code mbeanInterface}. The compiler can only
+ * check this if {@code mbeanInterface} is a class literal such
+ * as {@code MyMBean.class}.
+ *
+ * @exception IllegalArgumentException if the given
+ * <var>implementation</var> is null, or if the <var>mbeanInterface</var>
+ * does not follow JMX design patterns for Management Interfaces, or
+ * if the given <var>implementation</var> does not implement the
+ * specified interface.
+ *
+ * @since 1.6
+ **/
+ public <T> StandardMBean(T implementation, Class<T> mbeanInterface,
+ boolean isMXBean) {
+ try {
+ construct(implementation, mbeanInterface, false, isMXBean);
+ } catch (NotCompliantMBeanException e) {
+ throw new IllegalArgumentException(e);
+ }
+ }
+
+ /**
+ * <p>Make a DynamicMBean out of <var>this</var>, using the specified
+ * <var>mbeanInterface</var> class, and choosing whether the resulting
+ * MBean is an MXBean. This constructor can be used
+ * to make either Standard MBeans or MXBeans. Unlike the
+ * constructor {@link #StandardMBean(Object, Class)}, it
+ * does not throw NotCompliantMBeanException.</p>
+ *
+ * <p>Calls {@link #StandardMBean(java.lang.Object, java.lang.Class, boolean)
+ * this(this, mbeanInterface, isMXBean)}.
+ * This constructor is reserved to subclasses.</p>
+ *
+ * @param mbeanInterface The Management Interface exported by this
+ * MBean.
+ * @param isMXBean If true, the {@code mbeanInterface} parameter
+ * names an MXBean interface and the resultant MBean is an MXBean.
+ *
+ * @exception IllegalArgumentException if the <var>mbeanInterface</var>
+ * does not follow JMX design patterns for Management Interfaces, or
+ * if <var>this</var> does not implement the specified interface.
+ *
+ * @since 1.6
+ **/
+ protected StandardMBean(Class<?> mbeanInterface, boolean isMXBean) {
+ try {
+ construct(null, mbeanInterface, true, isMXBean);
+ } catch (NotCompliantMBeanException e) {
+ throw new IllegalArgumentException(e);
+ }
+ }
+
+ /**
+ * <p>Replace the implementation object wrapped in this object.</p>
+ *
+ * @param implementation The new implementation of this Standard MBean
+ * (or MXBean). The <code>implementation</code> object must implement
+ * the Standard MBean (or MXBean) interface that was supplied when this
+ * <code>StandardMBean</code> was constructed.
+ *
+ * @exception IllegalArgumentException if the given
+ * <var>implementation</var> is null.
+ *
+ * @exception NotCompliantMBeanException if the given
+ * <var>implementation</var> does not implement the
+ * Standard MBean (or MXBean) interface that was
+ * supplied at construction.
+ *
+ * @see #getImplementation
+ **/
+ public void setImplementation(Object implementation)
+ throws NotCompliantMBeanException {
+
+ if (implementation == null)
+ throw new IllegalArgumentException("implementation is null");
+
+ if (isMXBean()) {
+ this.mbean = new MXBeanSupport(implementation,
+ Util.<Class<Object>>cast(getMBeanInterface()));
+ } else {
+ this.mbean = new StandardMBeanSupport(implementation,
+ Util.<Class<Object>>cast(getMBeanInterface()));
+ }
+ }
+
+ /**
+ * Get the implementation of this Standard MBean (or MXBean).
+ * @return The implementation of this Standard MBean (or MXBean).
+ *
+ * @see #setImplementation
+ **/
+ public Object getImplementation() {
+ return mbean.getResource();
+ }
+
+ /**
+ * Get the Management Interface of this Standard MBean (or MXBean).
+ * @return The management interface of this Standard MBean (or MXBean).
+ **/
+ public final Class<?> getMBeanInterface() {
+ return mbean.getMBeanInterface();
+ }
+
+ /**
+ * Get the class of the implementation of this Standard MBean (or MXBean).
+ * @return The class of the implementation of this Standard MBean (or MXBean).
+ **/
+ public Class<?> getImplementationClass() {
+ return mbean.getResource().getClass();
+ }
+
+ // ------------------------------------------------------------------
+ // From the DynamicMBean interface.
+ // ------------------------------------------------------------------
+ public Object getAttribute(String attribute)
+ throws AttributeNotFoundException,
+ MBeanException,
+ ReflectionException {
+ return mbean.getAttribute(attribute);
+ }
+
+ // ------------------------------------------------------------------
+ // From the DynamicMBean interface.
+ // ------------------------------------------------------------------
+ public void setAttribute(Attribute attribute)
+ throws AttributeNotFoundException,
+ InvalidAttributeValueException,
+ MBeanException,
+ ReflectionException {
+ mbean.setAttribute(attribute);
+ }
+
+ // ------------------------------------------------------------------
+ // From the DynamicMBean interface.
+ // ------------------------------------------------------------------
+ public AttributeList getAttributes(String[] attributes) {
+ return mbean.getAttributes(attributes);
+ }
+
+ // ------------------------------------------------------------------
+ // From the DynamicMBean interface.
+ // ------------------------------------------------------------------
+ public AttributeList setAttributes(AttributeList attributes) {
+ return mbean.setAttributes(attributes);
+ }
+
+ // ------------------------------------------------------------------
+ // From the DynamicMBean interface.
+ // ------------------------------------------------------------------
+ public Object invoke(String actionName, Object params[], String signature[])
+ throws MBeanException, ReflectionException {
+ return mbean.invoke(actionName, params, signature);
+ }
+
+ /**
+ * Get the {@link MBeanInfo} for this MBean.
+ * <p>
+ * This method implements
+ * {@link javax.management.DynamicMBean#getMBeanInfo()
+ * DynamicMBean.getMBeanInfo()}.
+ * <p>
+ * This method first calls {@link #getCachedMBeanInfo()} in order to
+ * retrieve the cached MBeanInfo for this MBean, if any. If the
+ * MBeanInfo returned by {@link #getCachedMBeanInfo()} is not null,
+ * then it is returned.<br>
+ * Otherwise, this method builds a default MBeanInfo for this MBean,
+ * using the Management Interface specified for this MBean.
+ * <p>
+ * While building the MBeanInfo, this method calls the customization
+ * hooks that make it possible for subclasses to supply their custom
+ * descriptions, parameter names, etc...<br>
+ * Finally, it calls {@link #cacheMBeanInfo(javax.management.MBeanInfo)
+ * cacheMBeanInfo()} in order to cache the new MBeanInfo.
+ * @return The cached MBeanInfo for that MBean, if not null, or a
+ * newly built MBeanInfo if none was cached.
+ **/
+ public MBeanInfo getMBeanInfo() {
+ try {
+ final MBeanInfo cached = getCachedMBeanInfo();
+ if (cached != null) return cached;
+ } catch (RuntimeException x) {
+ if (MISC_LOGGER.isLoggable(Level.DEBUG)) {
+ MISC_LOGGER.log(Level.DEBUG,
+ "Failed to get cached MBeanInfo", x);
+ }
+ }
+
+ if (MISC_LOGGER.isLoggable(Level.TRACE)) {
+ MISC_LOGGER.log(Level.TRACE,
+ "Building MBeanInfo for " +
+ getImplementationClass().getName());
+ }
+
+ MBeanSupport<?> msupport = mbean;
+ final MBeanInfo bi = msupport.getMBeanInfo();
+ final Object impl = msupport.getResource();
+
+ final boolean immutableInfo = immutableInfo(this.getClass());
+
+ final String cname = getClassName(bi);
+ final String text = getDescription(bi);
+ final MBeanConstructorInfo[] ctors = getConstructors(bi,impl);
+ final MBeanAttributeInfo[] attrs = getAttributes(bi);
+ final MBeanOperationInfo[] ops = getOperations(bi);
+ final MBeanNotificationInfo[] ntfs = getNotifications(bi);
+ final Descriptor desc = getDescriptor(bi, immutableInfo);
+
+ final MBeanInfo nmbi = new MBeanInfo(
+ cname, text, attrs, ctors, ops, ntfs, desc);
+ try {
+ cacheMBeanInfo(nmbi);
+ } catch (RuntimeException x) {
+ if (MISC_LOGGER.isLoggable(Level.DEBUG)) {
+ MISC_LOGGER.log(Level.DEBUG,
+ "Failed to cache MBeanInfo", x);
+ }
+ }
+
+ return nmbi;
+ }
+
+ /**
+ * Customization hook:
+ * Get the className that will be used in the MBeanInfo returned by
+ * this MBean.
+ * <br>
+ * Subclasses may redefine this method in order to supply their
+ * custom class name. The default implementation returns
+ * {@link MBeanInfo#getClassName() info.getClassName()}.
+ * @param info The default MBeanInfo derived by reflection.
+ * @return the class name for the new MBeanInfo.
+ **/
+ protected String getClassName(MBeanInfo info) {
+ if (info == null) return getImplementationClass().getName();
+ return info.getClassName();
+ }
+
+ /**
+ * Customization hook:
+ * Get the description that will be used in the MBeanInfo returned by
+ * this MBean.
+ * <br>
+ * Subclasses may redefine this method in order to supply their
+ * custom MBean description. The default implementation returns
+ * {@link MBeanInfo#getDescription() info.getDescription()}.
+ * @param info The default MBeanInfo derived by reflection.
+ * @return the description for the new MBeanInfo.
+ **/
+ protected String getDescription(MBeanInfo info) {
+ if (info == null) return null;
+ return info.getDescription();
+ }
+
+ /**
+ * <p>Customization hook:
+ * Get the description that will be used in the MBeanFeatureInfo
+ * returned by this MBean.</p>
+ *
+ * <p>Subclasses may redefine this method in order to supply
+ * their custom description. The default implementation returns
+ * {@link MBeanFeatureInfo#getDescription()
+ * info.getDescription()}.</p>
+ *
+ * <p>This method is called by
+ * {@link #getDescription(MBeanAttributeInfo)},
+ * {@link #getDescription(MBeanOperationInfo)},
+ * {@link #getDescription(MBeanConstructorInfo)}.</p>
+ *
+ * @param info The default MBeanFeatureInfo derived by reflection.
+ * @return the description for the given MBeanFeatureInfo.
+ **/
+ protected String getDescription(MBeanFeatureInfo info) {
+ if (info == null) return null;
+ return info.getDescription();
+ }
+
+ /**
+ * Customization hook:
+ * Get the description that will be used in the MBeanAttributeInfo
+ * returned by this MBean.
+ *
+ * <p>Subclasses may redefine this method in order to supply their
+ * custom description. The default implementation returns {@link
+ * #getDescription(MBeanFeatureInfo)
+ * getDescription((MBeanFeatureInfo) info)}.
+ * @param info The default MBeanAttributeInfo derived by reflection.
+ * @return the description for the given MBeanAttributeInfo.
+ **/
+ protected String getDescription(MBeanAttributeInfo info) {
+ return getDescription((MBeanFeatureInfo)info);
+ }
+
+ /**
+ * Customization hook:
+ * Get the description that will be used in the MBeanConstructorInfo
+ * returned by this MBean.
+ * <br>
+ * Subclasses may redefine this method in order to supply their
+ * custom description.
+ * The default implementation returns {@link
+ * #getDescription(MBeanFeatureInfo)
+ * getDescription((MBeanFeatureInfo) info)}.
+ * @param info The default MBeanConstructorInfo derived by reflection.
+ * @return the description for the given MBeanConstructorInfo.
+ **/
+ protected String getDescription(MBeanConstructorInfo info) {
+ return getDescription((MBeanFeatureInfo)info);
+ }
+
+ /**
+ * Customization hook:
+ * Get the description that will be used for the <var>sequence</var>
+ * MBeanParameterInfo of the MBeanConstructorInfo returned by this MBean.
+ * <br>
+ * Subclasses may redefine this method in order to supply their
+ * custom description. The default implementation returns
+ * {@link MBeanParameterInfo#getDescription() param.getDescription()}.
+ *
+ * @param ctor The default MBeanConstructorInfo derived by reflection.
+ * @param param The default MBeanParameterInfo derived by reflection.
+ * @param sequence The sequence number of the parameter considered
+ * ("0" for the first parameter, "1" for the second parameter,
+ * etc...).
+ * @return the description for the given MBeanParameterInfo.
+ **/
+ protected String getDescription(MBeanConstructorInfo ctor,
+ MBeanParameterInfo param,
+ int sequence) {
+ if (param == null) return null;
+ return param.getDescription();
+ }
+
+ /**
+ * Customization hook:
+ * Get the name that will be used for the <var>sequence</var>
+ * MBeanParameterInfo of the MBeanConstructorInfo returned by this MBean.
+ * <br>
+ * Subclasses may redefine this method in order to supply their
+ * custom parameter name. The default implementation returns
+ * {@link MBeanParameterInfo#getName() param.getName()}.
+ *
+ * @param ctor The default MBeanConstructorInfo derived by reflection.
+ * @param param The default MBeanParameterInfo derived by reflection.
+ * @param sequence The sequence number of the parameter considered
+ * ("0" for the first parameter, "1" for the second parameter,
+ * etc...).
+ * @return the name for the given MBeanParameterInfo.
+ **/
+ protected String getParameterName(MBeanConstructorInfo ctor,
+ MBeanParameterInfo param,
+ int sequence) {
+ if (param == null) return null;
+ return param.getName();
+ }
+
+ /**
+ * Customization hook:
+ * Get the description that will be used in the MBeanOperationInfo
+ * returned by this MBean.
+ * <br>
+ * Subclasses may redefine this method in order to supply their
+ * custom description. The default implementation returns
+ * {@link #getDescription(MBeanFeatureInfo)
+ * getDescription((MBeanFeatureInfo) info)}.
+ * @param info The default MBeanOperationInfo derived by reflection.
+ * @return the description for the given MBeanOperationInfo.
+ **/
+ protected String getDescription(MBeanOperationInfo info) {
+ return getDescription((MBeanFeatureInfo)info);
+ }
+
+ /**
+ * Customization hook:
+ * Get the <var>impact</var> flag of the operation that will be used in
+ * the MBeanOperationInfo returned by this MBean.
+ * <br>
+ * Subclasses may redefine this method in order to supply their
+ * custom impact flag. The default implementation returns
+ * {@link MBeanOperationInfo#getImpact() info.getImpact()}.
+ * @param info The default MBeanOperationInfo derived by reflection.
+ * @return the impact flag for the given MBeanOperationInfo.
+ **/
+ protected int getImpact(MBeanOperationInfo info) {
+ if (info == null) return MBeanOperationInfo.UNKNOWN;
+ return info.getImpact();
+ }
+
+ /**
+ * Customization hook:
+ * Get the name that will be used for the <var>sequence</var>
+ * MBeanParameterInfo of the MBeanOperationInfo returned by this MBean.
+ * <br>
+ * Subclasses may redefine this method in order to supply their
+ * custom parameter name. The default implementation returns
+ * {@link MBeanParameterInfo#getName() param.getName()}.
+ *
+ * @param op The default MBeanOperationInfo derived by reflection.
+ * @param param The default MBeanParameterInfo derived by reflection.
+ * @param sequence The sequence number of the parameter considered
+ * ("0" for the first parameter, "1" for the second parameter,
+ * etc...).
+ * @return the name to use for the given MBeanParameterInfo.
+ **/
+ protected String getParameterName(MBeanOperationInfo op,
+ MBeanParameterInfo param,
+ int sequence) {
+ if (param == null) return null;
+ return param.getName();
+ }
+
+ /**
+ * Customization hook:
+ * Get the description that will be used for the <var>sequence</var>
+ * MBeanParameterInfo of the MBeanOperationInfo returned by this MBean.
+ * <br>
+ * Subclasses may redefine this method in order to supply their
+ * custom description. The default implementation returns
+ * {@link MBeanParameterInfo#getDescription() param.getDescription()}.
+ *
+ * @param op The default MBeanOperationInfo derived by reflection.
+ * @param param The default MBeanParameterInfo derived by reflection.
+ * @param sequence The sequence number of the parameter considered
+ * ("0" for the first parameter, "1" for the second parameter,
+ * etc...).
+ * @return the description for the given MBeanParameterInfo.
+ **/
+ protected String getDescription(MBeanOperationInfo op,
+ MBeanParameterInfo param,
+ int sequence) {
+ if (param == null) return null;
+ return param.getDescription();
+ }
+
+ /**
+ * Customization hook:
+ * Get the MBeanConstructorInfo[] that will be used in the MBeanInfo
+ * returned by this MBean.
+ * <br>
+ * By default, this method returns <code>null</code> if the wrapped
+ * implementation is not <var>this</var>. Indeed, if the wrapped
+ * implementation is not this object itself, it will not be possible
+ * to recreate a wrapped implementation by calling the implementation
+ * constructors through <code>MBeanServer.createMBean(...)</code>.<br>
+ * Otherwise, if the wrapped implementation is <var>this</var>,
+ * <var>ctors</var> is returned.
+ * <br>
+ * Subclasses may redefine this method in order to modify this
+ * behavior, if needed.
+ * @param ctors The default MBeanConstructorInfo[] derived by reflection.
+ * @param impl The wrapped implementation. If <code>null</code> is
+ * passed, the wrapped implementation is ignored and
+ * <var>ctors</var> is returned.
+ * @return the MBeanConstructorInfo[] for the new MBeanInfo.
+ **/
+ protected MBeanConstructorInfo[]
+ getConstructors(MBeanConstructorInfo[] ctors, Object impl) {
+ if (ctors == null) return null;
+ if (impl != null && impl != this) return null;
+ return ctors;
+ }
+
+ /**
+ * Customization hook:
+ * Get the MBeanNotificationInfo[] that will be used in the MBeanInfo
+ * returned by this MBean.
+ * <br>
+ * Subclasses may redefine this method in order to supply their
+ * custom notifications.
+ * @param info The default MBeanInfo derived by reflection.
+ * @return the MBeanNotificationInfo[] for the new MBeanInfo.
+ **/
+ MBeanNotificationInfo[] getNotifications(MBeanInfo info) {
+ return null;
+ }
+
+ /**
+ * <p>Get the Descriptor that will be used in the MBeanInfo
+ * returned by this MBean.</p>
+ *
+ * <p>Subclasses may redefine this method in order to supply
+ * their custom descriptor.</p>
+ *
+ * <p>The default implementation of this method returns a Descriptor
+ * that contains at least the field {@code interfaceClassName}, with
+ * value {@link #getMBeanInterface()}.getName(). It may also contain
+ * the field {@code immutableInfo}, with a value that is the string
+ * {@code "true"} if the implementation can determine that the
+ * {@code MBeanInfo} returned by {@link #getMBeanInfo()} will always
+ * be the same. It may contain other fields: fields defined by the
+ * JMX specification must have appropriate values, and other fields
+ * must follow the conventions for non-standard field names.</p>
+ *
+ * @param info The default MBeanInfo derived by reflection.
+ * @return the Descriptor for the new MBeanInfo.
+ */
+ Descriptor getDescriptor(MBeanInfo info, boolean immutableInfo) {
+ ImmutableDescriptor desc;
+ if (info == null ||
+ info.getDescriptor() == null ||
+ info.getDescriptor().getFieldNames().length == 0) {
+ final String interfaceClassNameS =
+ "interfaceClassName=" + getMBeanInterface().getName();
+ final String immutableInfoS =
+ "immutableInfo=" + immutableInfo;
+ desc = new ImmutableDescriptor(interfaceClassNameS, immutableInfoS);
+ desc = descriptors.get(desc);
+ } else {
+ Descriptor d = info.getDescriptor();
+ Map<String,Object> fields = new HashMap<String,Object>();
+ for (String fieldName : d.getFieldNames()) {
+ if (fieldName.equals("immutableInfo")) {
+ // Replace immutableInfo as the underlying MBean/MXBean
+ // could already implement NotificationBroadcaster and
+ // return immutableInfo=true in its MBeanInfo.
+ fields.put(fieldName, Boolean.toString(immutableInfo));
+ } else {
+ fields.put(fieldName, d.getFieldValue(fieldName));
+ }
+ }
+ desc = new ImmutableDescriptor(fields);
+ }
+ return desc;
+ }
+
+ /**
+ * Customization hook:
+ * Return the MBeanInfo cached for this object.
+ *
+ * <p>Subclasses may redefine this method in order to implement their
+ * own caching policy. The default implementation stores one
+ * {@link MBeanInfo} object per instance.
+ *
+ * @return The cached MBeanInfo, or null if no MBeanInfo is cached.
+ *
+ * @see #cacheMBeanInfo(MBeanInfo)
+ **/
+ protected MBeanInfo getCachedMBeanInfo() {
+ return cachedMBeanInfo;
+ }
+
+ /**
+ * Customization hook:
+ * cache the MBeanInfo built for this object.
+ *
+ * <p>Subclasses may redefine this method in order to implement
+ * their own caching policy. The default implementation stores
+ * <code>info</code> in this instance. A subclass can define
+ * other policies, such as not saving <code>info</code> (so it is
+ * reconstructed every time {@link #getMBeanInfo()} is called) or
+ * sharing a unique {@link MBeanInfo} object when several
+ * <code>StandardMBean</code> instances have equal {@link
+ * MBeanInfo} values.
+ *
+ * @param info the new <code>MBeanInfo</code> to cache. Any
+ * previously cached value is discarded. This parameter may be
+ * null, in which case there is no new cached value.
+ **/
+ protected void cacheMBeanInfo(MBeanInfo info) {
+ cachedMBeanInfo = info;
+ }
+
+ private boolean isMXBean() {
+ return mbean.isMXBean();
+ }
+
+ private static <T> boolean identicalArrays(T[] a, T[] b) {
+ if (a == b)
+ return true;
+ if (a == null || b == null || a.length != b.length)
+ return false;
+ for (int i = 0; i < a.length; i++) {
+ if (a[i] != b[i])
+ return false;
+ }
+ return true;
+ }
+
+ private static <T> boolean equal(T a, T b) {
+ if (a == b)
+ return true;
+ if (a == null || b == null)
+ return false;
+ return a.equals(b);
+ }
+
+ private static MBeanParameterInfo
+ customize(MBeanParameterInfo pi,
+ String name,
+ String description) {
+ if (equal(name, pi.getName()) &&
+ equal(description, pi.getDescription()))
+ return pi;
+ else if (pi instanceof OpenMBeanParameterInfo) {
+ OpenMBeanParameterInfo opi = (OpenMBeanParameterInfo) pi;
+ return new OpenMBeanParameterInfoSupport(name,
+ description,
+ opi.getOpenType(),
+ pi.getDescriptor());
+ } else {
+ return new MBeanParameterInfo(name,
+ pi.getType(),
+ description,
+ pi.getDescriptor());
+ }
+ }
+
+ private static MBeanConstructorInfo
+ customize(MBeanConstructorInfo ci,
+ String description,
+ MBeanParameterInfo[] signature) {
+ if (equal(description, ci.getDescription()) &&
+ identicalArrays(signature, ci.getSignature()))
+ return ci;
+ if (ci instanceof OpenMBeanConstructorInfo) {
+ OpenMBeanParameterInfo[] oparams =
+ paramsToOpenParams(signature);
+ return new OpenMBeanConstructorInfoSupport(ci.getName(),
+ description,
+ oparams,
+ ci.getDescriptor());
+ } else {
+ return new MBeanConstructorInfo(ci.getName(),
+ description,
+ signature,
+ ci.getDescriptor());
+ }
+ }
+
+ private static MBeanOperationInfo
+ customize(MBeanOperationInfo oi,
+ String description,
+ MBeanParameterInfo[] signature,
+ int impact) {
+ if (equal(description, oi.getDescription()) &&
+ identicalArrays(signature, oi.getSignature()) &&
+ impact == oi.getImpact())
+ return oi;
+ if (oi instanceof OpenMBeanOperationInfo) {
+ OpenMBeanOperationInfo ooi = (OpenMBeanOperationInfo) oi;
+ OpenMBeanParameterInfo[] oparams =
+ paramsToOpenParams(signature);
+ return new OpenMBeanOperationInfoSupport(oi.getName(),
+ description,
+ oparams,
+ ooi.getReturnOpenType(),
+ impact,
+ oi.getDescriptor());
+ } else {
+ return new MBeanOperationInfo(oi.getName(),
+ description,
+ signature,
+ oi.getReturnType(),
+ impact,
+ oi.getDescriptor());
+ }
+ }
+
+ private static MBeanAttributeInfo
+ customize(MBeanAttributeInfo ai,
+ String description) {
+ if (equal(description, ai.getDescription()))
+ return ai;
+ if (ai instanceof OpenMBeanAttributeInfo) {
+ OpenMBeanAttributeInfo oai = (OpenMBeanAttributeInfo) ai;
+ return new OpenMBeanAttributeInfoSupport(ai.getName(),
+ description,
+ oai.getOpenType(),
+ ai.isReadable(),
+ ai.isWritable(),
+ ai.isIs(),
+ ai.getDescriptor());
+ } else {
+ return new MBeanAttributeInfo(ai.getName(),
+ ai.getType(),
+ description,
+ ai.isReadable(),
+ ai.isWritable(),
+ ai.isIs(),
+ ai.getDescriptor());
+ }
+ }
+
+ private static OpenMBeanParameterInfo[]
+ paramsToOpenParams(MBeanParameterInfo[] params) {
+ if (params instanceof OpenMBeanParameterInfo[])
+ return (OpenMBeanParameterInfo[]) params;
+ OpenMBeanParameterInfo[] oparams =
+ new OpenMBeanParameterInfoSupport[params.length];
+ System.arraycopy(params, 0, oparams, 0, params.length);
+ return oparams;
+ }
+
+ // ------------------------------------------------------------------
+ // Build the custom MBeanConstructorInfo[]
+ // ------------------------------------------------------------------
+ private MBeanConstructorInfo[]
+ getConstructors(MBeanInfo info, Object impl) {
+ final MBeanConstructorInfo[] ctors =
+ getConstructors(info.getConstructors(), impl);
+ if (ctors == null)
+ return null;
+ final int ctorlen = ctors.length;
+ final MBeanConstructorInfo[] nctors = new MBeanConstructorInfo[ctorlen];
+ for (int i=0; i<ctorlen; i++) {
+ final MBeanConstructorInfo c = ctors[i];
+ final MBeanParameterInfo[] params = c.getSignature();
+ final MBeanParameterInfo[] nps;
+ if (params != null) {
+ final int plen = params.length;
+ nps = new MBeanParameterInfo[plen];
+ for (int ii=0;ii<plen;ii++) {
+ MBeanParameterInfo p = params[ii];
+ nps[ii] = customize(p,
+ getParameterName(c,p,ii),
+ getDescription(c,p,ii));
+ }
+ } else {
+ nps = null;
+ }
+ nctors[i] =
+ customize(c, getDescription(c), nps);
+ }
+ return nctors;
+ }
+
+ // ------------------------------------------------------------------
+ // Build the custom MBeanOperationInfo[]
+ // ------------------------------------------------------------------
+ private MBeanOperationInfo[] getOperations(MBeanInfo info) {
+ final MBeanOperationInfo[] ops = info.getOperations();
+ if (ops == null)
+ return null;
+ final int oplen = ops.length;
+ final MBeanOperationInfo[] nops = new MBeanOperationInfo[oplen];
+ for (int i=0; i<oplen; i++) {
+ final MBeanOperationInfo o = ops[i];
+ final MBeanParameterInfo[] params = o.getSignature();
+ final MBeanParameterInfo[] nps;
+ if (params != null) {
+ final int plen = params.length;
+ nps = new MBeanParameterInfo[plen];
+ for (int ii=0;ii<plen;ii++) {
+ MBeanParameterInfo p = params[ii];
+ nps[ii] = customize(p,
+ getParameterName(o,p,ii),
+ getDescription(o,p,ii));
+ }
+ } else {
+ nps = null;
+ }
+ nops[i] = customize(o, getDescription(o), nps, getImpact(o));
+ }
+ return nops;
+ }
+
+ // ------------------------------------------------------------------
+ // Build the custom MBeanAttributeInfo[]
+ // ------------------------------------------------------------------
+ private MBeanAttributeInfo[] getAttributes(MBeanInfo info) {
+ final MBeanAttributeInfo[] atts = info.getAttributes();
+ if (atts == null)
+ return null; // should not happen
+ final MBeanAttributeInfo[] natts;
+ final int attlen = atts.length;
+ natts = new MBeanAttributeInfo[attlen];
+ for (int i=0; i<attlen; i++) {
+ final MBeanAttributeInfo a = atts[i];
+ natts[i] = customize(a, getDescription(a));
+ }
+ return natts;
+ }
+
+ /**
+ * <p>Allows the MBean to perform any operations it needs before
+ * being registered in the MBean server. If the name of the MBean
+ * is not specified, the MBean can provide a name for its
+ * registration. If any exception is raised, the MBean will not be
+ * registered in the MBean server.</p>
+ *
+ * <p>The default implementation of this method returns the {@code name}
+ * parameter. It does nothing else for
+ * Standard MBeans. For MXBeans, it records the {@code MBeanServer}
+ * and {@code ObjectName} parameters so they can be used to translate
+ * inter-MXBean references.</p>
+ *
+ * <p>It is good practice for a subclass that overrides this method
+ * to call the overridden method via {@code super.preRegister(...)}.
+ * This is necessary if this object is an MXBean that is referenced
+ * by attributes or operations in other MXBeans.</p>
+ *
+ * @param server The MBean server in which the MBean will be registered.
+ *
+ * @param name The object name of the MBean. This name is null if
+ * the name parameter to one of the <code>createMBean</code> or
+ * <code>registerMBean</code> methods in the {@link MBeanServer}
+ * interface is null. In that case, this method must return a
+ * non-null ObjectName for the new MBean.
+ *
+ * @return The name under which the MBean is to be registered.
+ * This value must not be null. If the <code>name</code>
+ * parameter is not null, it will usually but not necessarily be
+ * the returned value.
+ *
+ * @throws IllegalArgumentException if this is an MXBean and
+ * {@code name} is null.
+ *
+ * @throws InstanceAlreadyExistsException if this is an MXBean and
+ * it has already been registered under another name (in this
+ * MBean Server or another).
+ *
+ * @throws Exception no other checked exceptions are thrown by
+ * this method but {@code Exception} is declared so that subclasses
+ * can override the method and throw their own exceptions.
+ *
+ * @since 1.6
+ */
+ public ObjectName preRegister(MBeanServer server, ObjectName name)
+ throws Exception {
+ mbean.register(server, name);
+ return name;
+ }
+
+ /**
+ * <p>Allows the MBean to perform any operations needed after having been
+ * registered in the MBean server or after the registration has failed.</p>
+ *
+ * <p>The default implementation of this method does nothing for
+ * Standard MBeans. For MXBeans, it undoes any work done by
+ * {@link #preRegister preRegister} if registration fails.</p>
+ *
+ * <p>It is good practice for a subclass that overrides this method
+ * to call the overridden method via {@code super.postRegister(...)}.
+ * This is necessary if this object is an MXBean that is referenced
+ * by attributes or operations in other MXBeans.</p>
+ *
+ * @param registrationDone Indicates whether or not the MBean has
+ * been successfully registered in the MBean server. The value
+ * false means that the registration phase has failed.
+ *
+ * @since 1.6
+ */
+ public void postRegister(Boolean registrationDone) {
+ if (!registrationDone)
+ mbean.unregister();
+ }
+
+ /**
+ * <p>Allows the MBean to perform any operations it needs before
+ * being unregistered by the MBean server.</p>
+ *
+ * <p>The default implementation of this method does nothing.</p>
+ *
+ * <p>It is good practice for a subclass that overrides this method
+ * to call the overridden method via {@code super.preDeregister(...)}.</p>
+ *
+ * @throws Exception no checked exceptions are throw by this method
+ * but {@code Exception} is declared so that subclasses can override
+ * this method and throw their own exceptions.
+ *
+ * @since 1.6
+ */
+ public void preDeregister() throws Exception {
+ }
+
+ /**
+ * <p>Allows the MBean to perform any operations needed after having been
+ * unregistered in the MBean server.</p>
+ *
+ * <p>The default implementation of this method does nothing for
+ * Standard MBeans. For MXBeans, it removes any information that
+ * was recorded by the {@link #preRegister preRegister} method.</p>
+ *
+ * <p>It is good practice for a subclass that overrides this method
+ * to call the overridden method via {@code super.postRegister(...)}.
+ * This is necessary if this object is an MXBean that is referenced
+ * by attributes or operations in other MXBeans.</p>
+ *
+ * @since 1.6
+ */
+ public void postDeregister() {
+ mbean.unregister();
+ }
+
+ //
+ // MBeanInfo immutability
+ //
+
+ /**
+ * Cached results of previous calls to immutableInfo. This is
+ * a WeakHashMap so that we don't prevent a class from being
+ * garbage collected just because we know whether its MBeanInfo
+ * is immutable.
+ */
+ private static final Map<Class<?>, Boolean> mbeanInfoSafeMap =
+ new WeakHashMap<Class<?>, Boolean>();
+
+ /**
+ * Return true if {@code subclass} is known to preserve the immutability
+ * of the {@code MBeanInfo}. The {@code subclass} is considered to have
+ * an immutable {@code MBeanInfo} if it does not override any of the
+ * getMBeanInfo, getCachedMBeanInfo, cacheMBeanInfo and getNotificationInfo
+ * methods.
+ */
+ static boolean immutableInfo(Class<? extends StandardMBean> subclass) {
+ if (subclass == StandardMBean.class ||
+ subclass == StandardEmitterMBean.class)
+ return true;
+ synchronized (mbeanInfoSafeMap) {
+ Boolean safe = mbeanInfoSafeMap.get(subclass);
+ if (safe == null) {
+ try {
+ MBeanInfoSafeAction action =
+ new MBeanInfoSafeAction(subclass);
+ safe = AccessController.doPrivileged(action);
+ } catch (Exception e) { // e.g. SecurityException
+ /* We don't know, so we assume it isn't. */
+ safe = false;
+ }
+ mbeanInfoSafeMap.put(subclass, safe);
+ }
+ return safe;
+ }
+ }
+
+ static boolean overrides(Class<?> subclass, Class<?> superclass,
+ String name, Class<?>... params) {
+ for (Class<?> c = subclass; c != superclass; c = c.getSuperclass()) {
+ try {
+ c.getDeclaredMethod(name, params);
+ return true;
+ } catch (NoSuchMethodException e) {
+ // OK: this class doesn't override it
+ }
+ }
+ return false;
+ }
+
+ private static class MBeanInfoSafeAction
+ implements PrivilegedAction<Boolean> {
+
+ private final Class<?> subclass;
+
+ MBeanInfoSafeAction(Class<?> subclass) {
+ this.subclass = subclass;
+ }
+
+ public Boolean run() {
+ // Check for "void cacheMBeanInfo(MBeanInfo)" method.
+ //
+ if (overrides(subclass, StandardMBean.class,
+ "cacheMBeanInfo", MBeanInfo.class))
+ return false;
+
+ // Check for "MBeanInfo getCachedMBeanInfo()" method.
+ //
+ if (overrides(subclass, StandardMBean.class,
+ "getCachedMBeanInfo", (Class<?>[]) null))
+ return false;
+
+ // Check for "MBeanInfo getMBeanInfo()" method.
+ //
+ if (overrides(subclass, StandardMBean.class,
+ "getMBeanInfo", (Class<?>[]) null))
+ return false;
+
+ // Check for "MBeanNotificationInfo[] getNotificationInfo()"
+ // method.
+ //
+ // This method is taken into account for the MBeanInfo
+ // immutability checks if and only if the given subclass is
+ // StandardEmitterMBean itself or can be assigned to
+ // StandardEmitterMBean.
+ //
+ if (StandardEmitterMBean.class.isAssignableFrom(subclass))
+ if (overrides(subclass, StandardEmitterMBean.class,
+ "getNotificationInfo", (Class<?>[]) null))
+ return false;
+ return true;
+ }
+ }
+}