/* * Copyright (c) 2002, 2007, 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.io.PrintWriter; import java.io.StringWriter; import java.security.AccessController; import java.security.PrivilegedAction; import java.util.HashMap; import java.util.Map; import java.util.WeakHashMap; import java.util.logging.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>Call {@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. 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. 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>Call {@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.FINEST)) { MISC_LOGGER.logp(Level.FINEST, MBeanServerFactory.class.getName(), "getMBeanInfo", "Failed to get cached MBeanInfo", x); } } if (MISC_LOGGER.isLoggable(Level.FINER)) { MISC_LOGGER.logp(Level.FINER, MBeanServerFactory.class.getName(), "getMBeanInfo", "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.FINEST)) { MISC_LOGGER.logp(Level.FINEST, MBeanServerFactory.class.getName(), "getMBeanInfo", "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 = null; 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.preDeegister(...)}.</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 only 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; } } }