/* * @(#)StandardMBean.java 1.23 05/05/27 * * Copyright 2005 Sun Microsystems, Inc. All rights reserved. * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms. */ package javax.management; import com.sun.jmx.mbeanserver.StandardMBeanMetaDataImpl; import com.sun.jmx.trace.Trace; import java.io.PrintWriter; import java.io.StringWriter; import java.lang.reflect.UndeclaredThrowableException; /** *

An MBean whose management interface is determined by reflection * on a Java interface.

* *

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.

* *

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...).

* *

This class also provides hooks that make it possible to supply * custom descriptions and names for the {@link MBeanInfo} returned by * the DynamicMBean interface.

* *

Using this class, an MBean can be created with any * implementation class name Impl and with a management * interface defined (as for current Standard MBeans) by any interface * Intf, in one of two general ways:

* * * *

In either case, the class Impl must implement the * interface Intf.

* *

Standard MBeans based on the naming relationship between * implementation and interface classes are of course still * available.

* * @since 1.5 * @since.unbundled JMX 1.2 */ public class StandardMBean implements DynamicMBean { /** The name of this class to be used for tracing */ private final static String dbgTag = "StandardMBean"; /** * The management interface. **/ private Class mbeanInterface; /** * The implementation. **/ private Object implementation; /** * The MetaData object used for invoking reflection. **/ private final StandardMBeanMetaDataImpl meta; /** * The cached MBeanInfo. **/ private MBeanInfo cachedMBeanInfo; /** * Make a DynamicMBean out of implementation, using the * specified mbeanInterface class. * @param implementation The implementation of this MBean. * If null, and null implementation is allowed, * then the implementation is assumed to be this. * @param mbeanInterface The Management Interface exported by this * MBean's implementation. If null, then this * object will use standard JMX design pattern to determine * the management interface associated with the given * implementation. * @param nullImplementationAllowed true if a null * implementation is allowed. If null implementation is allowed, * and a null implementation is passed, then the implementation * is assumed to be this. * @exception IllegalArgumentException if the given * implementation is null, and null is not allowed. * @exception NotCompliantMBeanException if the mbeanInterface * does not follow JMX design patterns for Management Interfaces, or * if the given implementation does not implement the * specified interface. **/ private StandardMBean(Object implementation, Class mbeanInterface, boolean nullImplementationAllowed) throws NotCompliantMBeanException { if (implementation == null) { if (nullImplementationAllowed) implementation = this; else throw new IllegalArgumentException("implementation is null"); } this.meta = new StandardMBeanMetaDataImpl(this); setImplementation(implementation,mbeanInterface); } /** *

Make a DynamicMBean out of the object * implementation, using the specified * mbeanInterface class.

* * @param implementation The implementation of this MBean. * @param mbeanInterface The Management Interface exported by this * MBean's implementation. If null, then this * object will use standard JMX design pattern to determine * the management interface associated with the given * implementation. * * @exception IllegalArgumentException if the given * implementation is null. * @exception NotCompliantMBeanException if the mbeanInterface * does not follow JMX design patterns for Management Interfaces, or * if the given implementation does not implement the * specified interface. **/ public StandardMBean(Object implementation,Class mbeanInterface) throws NotCompliantMBeanException { this(implementation,mbeanInterface,false); } /** *

Make a DynamicMBean out of this, using the specified * mbeanInterface class.

* *

Call {@link #StandardMBean(java.lang.Object, java.lang.Class) * this(this,mbeanInterface)}. * This constructor is reserved to subclasses.

* * @param mbeanInterface The Management Interface exported by this * MBean. * * @exception NotCompliantMBeanException if the mbeanInterface * does not follow JMX design patterns for Management Interfaces, or * if this does not implement the specified interface. **/ protected StandardMBean(Class mbeanInterface) throws NotCompliantMBeanException { this(null,mbeanInterface,true); } /** *

Replace the implementation object wrapped in this * object.

* * @param implementation The new implementation of this MBean. * The implementation object must implement the MBean * interface that was supplied when this * StandardMBean was constructed. * * @exception IllegalArgumentException if the given * implementation is null. * * @exception NotCompliantMBeanException if the given * implementation does not implement the MBean * interface that was supplied at construction. * * @see #getImplementation **/ public synchronized void setImplementation(Object implementation) throws NotCompliantMBeanException { setImplementation(implementation, getMBeanInterface()); } /** * Replace the implementation and management interface wrapped in * this object. * @param implementation The new implementation of this MBean. * @param mbeanInterface The Management Interface exported by this * MBean's implementation. If null, then this * object will use standard JMX design patterns to determine * the management interface associated with the given * implementation. * @exception IllegalArgumentException if the given * implementation is null. * @exception NotCompliantMBeanException if the mbeanInterface * does not follow JMX design patterns for Management Interfaces, or * if the given implementation does not implement the * specified interface. **/ private synchronized void setImplementation(Object implementation, Class mbeanInterface) throws NotCompliantMBeanException { if (implementation == null) throw new IllegalArgumentException("implementation is null"); // test compliance this.meta.testCompliance(implementation.getClass(),mbeanInterface); // flush the cache... cacheMBeanInfo(null); this.implementation = implementation; this.mbeanInterface = mbeanInterface; if (this.mbeanInterface == null) this.mbeanInterface = meta.getStandardMBeanInterface(implementation.getClass()); } /** * Get the implementation of this MBean. * @return The implementation of this MBean. * * @see #setImplementation **/ public synchronized Object getImplementation() { return implementation; } /** * Get the Management Interface of this MBean. * @return The management interface of this MBean. **/ public final synchronized Class getMBeanInterface() { return mbeanInterface; } /** * Get the class of the implementation of this MBean. * @return The class of the implementation of this MBean. **/ public synchronized Class getImplementationClass() { if (implementation == null) return null; return implementation.getClass(); } // ------------------------------------------------------------------ // From the DynamicMBean interface. // ------------------------------------------------------------------ public Object getAttribute(String attribute) throws AttributeNotFoundException, MBeanException, ReflectionException { return meta.getAttribute(getImplementation(),attribute); } // ------------------------------------------------------------------ // From the DynamicMBean interface. // ------------------------------------------------------------------ public void setAttribute(Attribute attribute) throws AttributeNotFoundException, InvalidAttributeValueException, MBeanException, ReflectionException { meta.setAttribute(getImplementation(),attribute); } // ------------------------------------------------------------------ // From the DynamicMBean interface. // ------------------------------------------------------------------ public AttributeList getAttributes(String[] attributes) { try { return meta.getAttributes(getImplementation(),attributes); } catch (ReflectionException x) { final RuntimeException r = new UndeclaredThrowableException(x,x.getMessage()); throw new RuntimeOperationsException(r,x.getMessage()); } } // ------------------------------------------------------------------ // From the DynamicMBean interface. // ------------------------------------------------------------------ public AttributeList setAttributes(AttributeList attributes) { try { return meta.setAttributes(getImplementation(),attributes); } catch (ReflectionException x) { final RuntimeException r = new UndeclaredThrowableException(x,x.getMessage()); throw new RuntimeOperationsException(r,x.getMessage()); } } // ------------------------------------------------------------------ // From the DynamicMBean interface. // ------------------------------------------------------------------ public Object invoke(String actionName, Object params[], String signature[]) throws MBeanException, ReflectionException { return meta.invoke(getImplementation(),actionName,params,signature); } /** * Get the {@link MBeanInfo} for this MBean. *

* This method implements * {@link javax.management.DynamicMBean#getMBeanInfo() * DynamicMBean.getMBeanInfo()}. *

* 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.
* Otherwise, this method builds a default MBeanInfo for this MBean, * using the Management Interface specified for this MBean. *

* While building the MBeanInfo, this method calls the customization * hooks that make it possible for subclasses to supply their custom * descriptions, parameter names, etc...
* 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 (MBeanInfo)cached; } catch (RuntimeException x) { debug("getMBeanInfo","failed to get cached MBeanInfo: "+x); debugX("getMBeanInfo",x); } if (isTraceOn()) { trace("getMBeanInfo", "Building MBeanInfo for "+ getImplementationClass().getName()); } final MBeanInfo bi; final Object impl; try { synchronized (this) { impl = getImplementation(); bi = buildStandardMBeanInfo(); } } catch (NotCompliantMBeanException x) { final RuntimeException r = new UndeclaredThrowableException(x,x.getMessage()); throw new RuntimeOperationsException(r,x.getMessage()); } 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 MBeanInfo nmbi = new MBeanInfo(cname,text,attrs,ctors,ops,ntfs); try { cacheMBeanInfo(nmbi); } catch (RuntimeException x) { debug("cacheMBeanInfo","failed to cache MBeanInfo: "+x); debugX("cacheMBeanInfo",x); } return nmbi; } /** * Customization hook: * Get the className that will be used in the MBeanInfo returned by * this MBean. *
* 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. *
* 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(); } /** *

Customization hook: * Get the description that will be used in the MBeanFeatureInfo * returned by this MBean.

* *

Subclasses may redefine this method in order to supply * their custom description. The default implementation returns * {@link MBeanFeatureInfo#getDescription() * info.getDescription()}.

* *

This method is called by * {@link #getDescription(MBeanAttributeInfo)}, * {@link #getDescription(MBeanOperationInfo)}, * {@link #getDescription(MBeanConstructorInfo)}.

* * @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. * *

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. *
* 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 sequence * MBeanParameterInfo of the MBeanConstructorInfo returned by this MBean. *
* 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 sequence * MBeanParameterInfo of the MBeanConstructorInfo returned by this MBean. *
* 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. *
* 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 impact flag of the operation that will be used in * the MBeanOperationInfo returned by this MBean. *
* 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 sequence * MBeanParameterInfo of the MBeanOperationInfo returned by this MBean. *
* 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 sequence * MBeanParameterInfo of the MBeanOperationInfo returned by this MBean. *
* 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. *
* By default, this method returns null if the wrapped * implementation is not this. 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 MBeanServer.createMBean(...).
* Otherwise, if the wrapped implementation is this, * ctors is returned. *
* 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 null is * passed, the wrapped implementation is ignored and * ctors 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. *
* 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. **/ // Private because not needed - the StandardMBeanMetaDataImpl already // calls getNotificationInfo() on the implementation.... private MBeanNotificationInfo[] getNotifications(MBeanInfo info) { if (info == null) return null; return info.getNotifications(); } /** * Customization hook: * Return the MBeanInfo cached for this object. * *

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 synchronized MBeanInfo getCachedMBeanInfo() { return cachedMBeanInfo; } /** * Customization hook: * cache the MBeanInfo built for this object. * *

Subclasses may redefine this method in order to implement * their own caching policy. The default implementation stores * info in this instance. A subclass can define * other policies, such as not saving info (so it is * reconstructed every time {@link #getMBeanInfo()} is called) or * sharing a unique {@link MBeanInfo} object when several * StandardMBean instances have equal {@link * MBeanInfo} values. * * @param info the new MBeanInfo to cache. Any * previously cached value is discarded. This parameter may be * null, in which case there is no new cached value. **/ protected synchronized void cacheMBeanInfo(MBeanInfo info) { cachedMBeanInfo = info; } // ------------------------------------------------------------------ // Build the defaullt standard MBeanInfo. // ------------------------------------------------------------------ private synchronized MBeanInfo buildStandardMBeanInfo() throws NotCompliantMBeanException { return meta.buildMBeanInfo(getImplementationClass(), getMBeanInterface()); } // ------------------------------------------------------------------ // Build the custom MBeanConstructorInfo[] // ------------------------------------------------------------------ private MBeanConstructorInfo[] getConstructors(MBeanInfo info,Object impl) { final MBeanConstructorInfo[] ctors = getConstructors(info.getConstructors(),impl); final MBeanConstructorInfo[] nctors; if (ctors != null) { final int ctorlen = ctors.length; nctors = new MBeanConstructorInfo[ctorlen]; for (int i=0; i