/*
* Copyright (c) 2002, 2008, Oracle and/or its affiliates. All rights reserved.
* ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*/
package javax.management;
import static com.sun.jmx.defaults.
JmxProperties.
MISC_LOGGER;
import com.sun.jmx.mbeanserver.
DescriptorCache;
import com.sun.jmx.mbeanserver.
Introspector;
import com.sun.jmx.mbeanserver.
MBeanSupport;
import com.sun.jmx.mbeanserver.
MXBeanSupport;
import com.sun.jmx.mbeanserver.
StandardMBeanSupport;
import com.sun.jmx.mbeanserver.
Util;
import java.security.
AccessController;
import java.security.
PrivilegedAction;
import java.util.
HashMap;
import java.util.
Map;
import java.util.
WeakHashMap;
import java.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>Calls {@link #StandardMBean(java.lang.Object, java.lang.Class)
* this(this,mbeanInterface)}.
* This constructor is reserved to subclasses.</p>
*
* @param mbeanInterface The Management Interface exported by this
* MBean.
*
* @exception NotCompliantMBeanException if the <var>mbeanInterface</var>
* does not follow JMX design patterns for Management Interfaces, or
* if <var>this</var> does not implement the specified interface.
**/
protected
StandardMBean(
Class<?>
mbeanInterface)
throws
NotCompliantMBeanException {
construct(null,
mbeanInterface, true, false);
}
/**
* <p>Make a DynamicMBean out of the object
* <var>implementation</var>, using the specified
* <var>mbeanInterface</var> class, and choosing whether the
* resultant MBean is an MXBean. This constructor can be used
* to make either Standard MBeans or MXBeans. Unlike the
* constructor {@link #StandardMBean(Object, Class)}, it
* does not throw NotCompliantMBeanException.</p>
*
* @param implementation The implementation of this MBean.
* @param mbeanInterface The Management Interface exported by this
* MBean's implementation. If <code>null</code>, then this
* object will use standard JMX design pattern to determine
* the management interface associated with the given
* implementation.
* @param isMXBean If true, the {@code mbeanInterface} parameter
* names an MXBean interface and the resultant MBean is an MXBean.
* @param <T> Allows the compiler to check
* that {@code implementation} does indeed implement the class
* described by {@code mbeanInterface}. The compiler can only
* check this if {@code mbeanInterface} is a class literal such
* as {@code MyMBean.class}.
*
* @exception IllegalArgumentException if the given
* <var>implementation</var> is null, or if the <var>mbeanInterface</var>
* does not follow JMX design patterns for Management Interfaces, or
* if the given <var>implementation</var> does not implement the
* specified interface.
*
* @since 1.6
**/
public <T>
StandardMBean(T
implementation,
Class<T>
mbeanInterface,
boolean
isMXBean) {
try {
construct(
implementation,
mbeanInterface, false,
isMXBean);
} catch (
NotCompliantMBeanException e) {
throw new
IllegalArgumentException(
e);
}
}
/**
* <p>Make a DynamicMBean out of <var>this</var>, using the specified
* <var>mbeanInterface</var> class, and choosing whether the resulting
* MBean is an MXBean. This constructor can be used
* to make either Standard MBeans or MXBeans. Unlike the
* constructor {@link #StandardMBean(Object, Class)}, it
* does not throw NotCompliantMBeanException.</p>
*
* <p>Calls {@link #StandardMBean(java.lang.Object, java.lang.Class, boolean)
* this(this, mbeanInterface, isMXBean)}.
* This constructor is reserved to subclasses.</p>
*
* @param mbeanInterface The Management Interface exported by this
* MBean.
* @param isMXBean If true, the {@code mbeanInterface} parameter
* names an MXBean interface and the resultant MBean is an MXBean.
*
* @exception IllegalArgumentException if the <var>mbeanInterface</var>
* does not follow JMX design patterns for Management Interfaces, or
* if <var>this</var> does not implement the specified interface.
*
* @since 1.6
**/
protected
StandardMBean(
Class<?>
mbeanInterface, boolean
isMXBean) {
try {
construct(null,
mbeanInterface, true,
isMXBean);
} catch (
NotCompliantMBeanException e) {
throw new
IllegalArgumentException(
e);
}
}
/**
* <p>Replace the implementation object wrapped in this object.</p>
*
* @param implementation The new implementation of this Standard MBean
* (or MXBean). The <code>implementation</code> object must implement
* the Standard MBean (or MXBean) interface that was supplied when this
* <code>StandardMBean</code> was constructed.
*
* @exception IllegalArgumentException if the given
* <var>implementation</var> is null.
*
* @exception NotCompliantMBeanException if the given
* <var>implementation</var> does not implement the
* Standard MBean (or MXBean) interface that was
* supplied at construction.
*
* @see #getImplementation
**/
public void
setImplementation(
Object implementation)
throws
NotCompliantMBeanException {
if (
implementation == null)
throw new
IllegalArgumentException("implementation is null");
if (
isMXBean()) {
this.
mbean = new
MXBeanSupport(
implementation,
Util.<
Class<
Object>>
cast(
getMBeanInterface()));
} else {
this.
mbean = new
StandardMBeanSupport(
implementation,
Util.<
Class<
Object>>
cast(
getMBeanInterface()));
}
}
/**
* Get the implementation of this Standard MBean (or MXBean).
* @return The implementation of this Standard MBean (or MXBean).
*
* @see #setImplementation
**/
public
Object getImplementation() {
return
mbean.
getResource();
}
/**
* Get the Management Interface of this Standard MBean (or MXBean).
* @return The management interface of this Standard MBean (or MXBean).
**/
public final
Class<?>
getMBeanInterface() {
return
mbean.
getMBeanInterface();
}
/**
* Get the class of the implementation of this Standard MBean (or MXBean).
* @return The class of the implementation of this Standard MBean (or MXBean).
**/
public
Class<?>
getImplementationClass() {
return
mbean.
getResource().
getClass();
}
// ------------------------------------------------------------------
// From the DynamicMBean interface.
// ------------------------------------------------------------------
public
Object getAttribute(
String attribute)
throws
AttributeNotFoundException,
MBeanException,
ReflectionException {
return
mbean.
getAttribute(
attribute);
}
// ------------------------------------------------------------------
// From the DynamicMBean interface.
// ------------------------------------------------------------------
public void
setAttribute(
Attribute attribute)
throws
AttributeNotFoundException,
InvalidAttributeValueException,
MBeanException,
ReflectionException {
mbean.
setAttribute(
attribute);
}
// ------------------------------------------------------------------
// From the DynamicMBean interface.
// ------------------------------------------------------------------
public
AttributeList getAttributes(
String[]
attributes) {
return
mbean.
getAttributes(
attributes);
}
// ------------------------------------------------------------------
// From the DynamicMBean interface.
// ------------------------------------------------------------------
public
AttributeList setAttributes(
AttributeList attributes) {
return
mbean.
setAttributes(
attributes);
}
// ------------------------------------------------------------------
// From the DynamicMBean interface.
// ------------------------------------------------------------------
public
Object invoke(
String actionName,
Object params[],
String signature[])
throws
MBeanException,
ReflectionException {
return
mbean.
invoke(
actionName,
params,
signature);
}
/**
* Get the {@link MBeanInfo} for this MBean.
* <p>
* This method implements
* {@link javax.management.DynamicMBean#getMBeanInfo()
* DynamicMBean.getMBeanInfo()}.
* <p>
* This method first calls {@link #getCachedMBeanInfo()} in order to
* retrieve the cached MBeanInfo for this MBean, if any. If the
* MBeanInfo returned by {@link #getCachedMBeanInfo()} is not null,
* then it is returned.<br>
* Otherwise, this method builds a default MBeanInfo for this MBean,
* using the Management Interface specified for this MBean.
* <p>
* While building the MBeanInfo, this method calls the customization
* hooks that make it possible for subclasses to supply their custom
* descriptions, parameter names, etc...<br>
* Finally, it calls {@link #cacheMBeanInfo(javax.management.MBeanInfo)
* cacheMBeanInfo()} in order to cache the new MBeanInfo.
* @return The cached MBeanInfo for that MBean, if not null, or a
* newly built MBeanInfo if none was cached.
**/
public
MBeanInfo getMBeanInfo() {
try {
final
MBeanInfo cached =
getCachedMBeanInfo();
if (
cached != null) return
cached;
} catch (
RuntimeException x) {
if (
MISC_LOGGER.
isLoggable(
Level.
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.preDeregister(...)}.</p>
*
* @throws Exception no checked exceptions are throw by this method
* but {@code Exception} is declared so that subclasses can override
* this method and throw their own exceptions.
*
* @since 1.6
*/
public void
preDeregister() throws
Exception {
}
/**
* <p>Allows the MBean to perform any operations needed after having been
* unregistered in the MBean server.</p>
*
* <p>The default implementation of this method does nothing for
* Standard MBeans. For MXBeans, it removes any information that
* was recorded by the {@link #preRegister preRegister} method.</p>
*
* <p>It is good practice for a subclass that overrides this method
* to call the overridden method via {@code super.postRegister(...)}.
* This is necessary if this object is an MXBean that is referenced
* by attributes or operations in other MXBeans.</p>
*
* @since 1.6
*/
public void
postDeregister() {
mbean.
unregister();
}
//
// MBeanInfo immutability
//
/**
* Cached results of previous calls to immutableInfo. This is
* a WeakHashMap so that we don't prevent a class from being
* garbage collected just because we know whether its MBeanInfo
* is immutable.
*/
private static final
Map<
Class<?>,
Boolean>
mbeanInfoSafeMap =
new
WeakHashMap<
Class<?>,
Boolean>();
/**
* Return true if {@code subclass} is known to preserve the immutability
* of the {@code MBeanInfo}. The {@code subclass} is considered to have
* an immutable {@code MBeanInfo} if it does not override any of the
* getMBeanInfo, getCachedMBeanInfo, cacheMBeanInfo and getNotificationInfo
* methods.
*/
static boolean
immutableInfo(
Class<? extends
StandardMBean>
subclass) {
if (
subclass ==
StandardMBean.class ||
subclass ==
StandardEmitterMBean.class)
return true;
synchronized (
mbeanInfoSafeMap) {
Boolean safe =
mbeanInfoSafeMap.
get(
subclass);
if (
safe == null) {
try {
MBeanInfoSafeAction action =
new
MBeanInfoSafeAction(
subclass);
safe =
AccessController.
doPrivileged(
action);
} catch (
Exception e) { // e.g. SecurityException
/* We don't know, so we assume it isn't. */
safe = false;
}
mbeanInfoSafeMap.
put(
subclass,
safe);
}
return
safe;
}
}
static boolean
overrides(
Class<?>
subclass,
Class<?>
superclass,
String name,
Class<?>...
params) {
for (
Class<?>
c =
subclass;
c !=
superclass;
c =
c.
getSuperclass()) {
try {
c.
getDeclaredMethod(
name,
params);
return true;
} catch (
NoSuchMethodException e) {
// OK: this class doesn't override it
}
}
return false;
}
private static class
MBeanInfoSafeAction
implements
PrivilegedAction<
Boolean> {
private final
Class<?>
subclass;
MBeanInfoSafeAction(
Class<?>
subclass) {
this.
subclass =
subclass;
}
public
Boolean run() {
// Check for "void cacheMBeanInfo(MBeanInfo)" method.
//
if (
overrides(
subclass,
StandardMBean.class,
"cacheMBeanInfo",
MBeanInfo.class))
return false;
// Check for "MBeanInfo getCachedMBeanInfo()" method.
//
if (
overrides(
subclass,
StandardMBean.class,
"getCachedMBeanInfo", (
Class<?>[]) null))
return false;
// Check for "MBeanInfo getMBeanInfo()" method.
//
if (
overrides(
subclass,
StandardMBean.class,
"getMBeanInfo", (
Class<?>[]) null))
return false;
// Check for "MBeanNotificationInfo[] getNotificationInfo()"
// method.
//
// This method is taken into account for the MBeanInfo
// immutability checks if and only if the given subclass is
// StandardEmitterMBean itself or can be assigned to
// StandardEmitterMBean.
//
if (
StandardEmitterMBean.class.
isAssignableFrom(
subclass))
if (
overrides(
subclass,
StandardEmitterMBean.class,
"getNotificationInfo", (
Class<?>[]) null))
return false;
return true;
}
}
}