/* * @(#)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:
* ** MBeanServer mbs; * ... * Impl impl = new Impl(...); * StandardMBean mbean = new StandardMBean(impl, Intf.class); * mbs.registerMBean(mbean, objectName); *
* public class Impl extends StandardMBean implements Intf { * public Impl() { * super(Intf.class); * } * // implement methods of Intf * } * * [...] * * MBeanServer mbs; * .... * Impl impl = new Impl(); * mbs.registerMBean(impl, objectName); *
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. * Ifnull
, 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. Ifnull
, 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. * Theimplementation
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