| /* |
| * Copyright 2002-2007 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 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; |
| 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; |
| } |
| } |
| |
| } |