/* |
|
* Copyright (c) 2002, 2008, Oracle and/or its affiliates. All rights reserved. |
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. |
|
* |
|
* This code is free software; you can redistribute it and/or modify it |
|
* under the terms of the GNU General Public License version 2 only, as |
|
* published by the Free Software Foundation. Oracle designates this |
|
* particular file as subject to the "Classpath" exception as provided |
|
* by Oracle in the LICENSE file that accompanied this code. |
|
* |
|
* This code is distributed in the hope that it will be useful, but WITHOUT |
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
|
* version 2 for more details (a copy is included in the LICENSE file that |
|
* accompanied this code). |
|
* |
|
* You should have received a copy of the GNU General Public License version |
|
* 2 along with this work; if not, write to the Free Software Foundation, |
|
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. |
|
* |
|
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA |
|
* or visit www.oracle.com if you need additional information or have any |
|
* questions. |
|
*/ |
|
package javax.management; |
|
import static com.sun.jmx.defaults.JmxProperties.MISC_LOGGER; |
|
import com.sun.jmx.mbeanserver.DescriptorCache; |
|
import com.sun.jmx.mbeanserver.Introspector; |
|
import com.sun.jmx.mbeanserver.MBeanSupport; |
|
import com.sun.jmx.mbeanserver.MXBeanSupport; |
|
import com.sun.jmx.mbeanserver.StandardMBeanSupport; |
|
import com.sun.jmx.mbeanserver.Util; |
|
import java.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; |
|
} |
|
} |
|
} |