/*
* Copyright (c) 2005, 2013, Oracle and/or its affiliates. All rights reserved.
* ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*/
package com.sun.jmx.mbeanserver;
import javax.management.
Attribute;
import javax.management.
AttributeList;
import javax.management.
AttributeNotFoundException;
import javax.management.
InvalidAttributeValueException;
import javax.management.
MBeanException;
import javax.management.
MBeanInfo;
import javax.management.
MBeanRegistration;
import javax.management.
MBeanServer;
import javax.management.
NotCompliantMBeanException;
import javax.management.
ObjectName;
import javax.management.
ReflectionException;
import com.sun.jmx.mbeanserver.
MXBeanMappingFactory;
import sun.reflect.misc.
ReflectUtil;
/**
* Base class for MBeans. There is one instance of this class for
* every Standard MBean and every MXBean. We try to limit the amount
* of information per instance so we can handle very large numbers of
* MBeans comfortably.
*
* @param <M> either Method or ConvertingMethod, for Standard MBeans
* and MXBeans respectively.
*
* @since 1.6
*/
/*
* We maintain a couple of caches to increase sharing between
* different MBeans of the same type and also to reduce creation time
* for the second and subsequent instances of the same type.
*
* The first cache maps from an MBean interface to a PerInterface
* object containing information parsed out of the interface. The
* interface is either a Standard MBean interface or an MXBean
* interface, and there is one cache for each case.
*
* The PerInterface includes an MBeanInfo. This contains the
* attributes and operations parsed out of the interface's methods,
* plus a basic Descriptor for the interface containing at least the
* interfaceClassName field and any fields derived from annotations on
* the interface. This MBeanInfo can never be the MBeanInfo for any
* actual MBean, because an MBeanInfo's getClassName() is the name of
* a concrete class and we don't know what the class will be.
* Furthermore a real MBeanInfo may need to add constructors and/or
* notifications to the MBeanInfo.
*
* The PerInterface also contains an MBeanDispatcher which is able to
* route getAttribute, setAttribute, and invoke to the appropriate
* method of the interface, including doing any necessary translation
* of parameters and return values for MXBeans.
*
* The PerInterface also contains the original Class for the interface.
*
* We need to be careful about references. When there are no MBeans
* with a given interface, there must not be any strong references to
* the interface Class. Otherwise it could never be garbage collected,
* and neither could its ClassLoader or any other classes loaded by
* its ClassLoader. Therefore the cache must wrap the PerInterface
* in a WeakReference. Each instance of MBeanSupport has a strong
* reference to its PerInterface, which prevents PerInterface instances
* from being garbage-collected prematurely.
*
* The second cache maps from a concrete class and an MBean interface
* that that class implements to the MBeanInfo for that class and
* interface. (The ability to specify an interface separately comes
* from the class StandardMBean. MBeans registered directly in the
* MBean Server will always have the same interface here.)
*
* The MBeanInfo in this second cache will be the MBeanInfo from the
* PerInterface cache for the given itnerface, but with the
* getClassName() having the concrete class's name, and the public
* constructors based on the concrete class's constructors. This
* MBeanInfo can be shared between all instances of the concrete class
* specifying the same interface, except instances that are
* NotificationBroadcasters. NotificationBroadcasters supply the
* MBeanNotificationInfo[] in the MBeanInfo based on the instance
* method NotificationBroadcaster.getNotificationInfo(), so two
* instances of the same concrete class do not necessarily have the
* same MBeanNotificationInfo[]. Currently we do not try to detect
* when they do, although it would probably be worthwhile doing that
* since it is a very common case.
*
* Standard MBeans additionally have the property that
* getNotificationInfo() must in principle be called every time
* getMBeanInfo() is called for the MBean, since the returned array is
* allowed to change over time. We attempt to reduce the cost of
* doing this by detecting when the Standard MBean is a subclass of
* NotificationBroadcasterSupport that does not override
* getNotificationInfo(), meaning that the MBeanNotificationInfo[] is
* the one that was supplied to the constructor. MXBeans do not have
* this problem because their getNotificationInfo() method is called
* only once.
*
*/
public abstract class
MBeanSupport<M>
implements
DynamicMBean2,
MBeanRegistration {
<T>
MBeanSupport(T
resource,
Class<T>
mbeanInterfaceType)
throws
NotCompliantMBeanException {
if (
mbeanInterfaceType == null)
throw new
NotCompliantMBeanException("Null MBean interface");
if (!
mbeanInterfaceType.
isInstance(
resource)) {
final
String msg =
"Resource class " +
resource.
getClass().
getName() +
" is not an instance of " +
mbeanInterfaceType.
getName();
throw new
NotCompliantMBeanException(
msg);
}
ReflectUtil.
checkPackageAccess(
mbeanInterfaceType);
this.
resource =
resource;
MBeanIntrospector<M>
introspector =
getMBeanIntrospector();
this.
perInterface =
introspector.
getPerInterface(
mbeanInterfaceType);
this.
mbeanInfo =
introspector.
getMBeanInfo(
resource,
perInterface);
}
/** Return the appropriate introspector for this type of MBean. */
abstract
MBeanIntrospector<M>
getMBeanIntrospector();
/**
* Return a cookie for this MBean. This cookie will be passed to
* MBean method invocations where it can supply additional information
* to the invocation. For example, with MXBeans it can be used to
* supply the MXBeanLookup context for resolving inter-MXBean references.
*/
abstract
Object getCookie();
public final boolean
isMXBean() {
return
perInterface.
isMXBean();
}
// Methods that javax.management.StandardMBean should call from its
// preRegister and postRegister, given that it is not supposed to
// call the contained object's preRegister etc methods even if it has them
public abstract void
register(
MBeanServer mbs,
ObjectName name)
throws
Exception;
public abstract void
unregister();
public final
ObjectName preRegister(
MBeanServer server,
ObjectName name)
throws
Exception {
if (
resource instanceof
MBeanRegistration)
name = ((
MBeanRegistration)
resource).
preRegister(
server,
name);
return
name;
}
public final void
preRegister2(
MBeanServer server,
ObjectName name)
throws
Exception {
register(
server,
name);
}
public final void
registerFailed() {
unregister();
}
public final void
postRegister(
Boolean registrationDone) {
if (
resource instanceof
MBeanRegistration)
((
MBeanRegistration)
resource).
postRegister(
registrationDone);
}
public final void
preDeregister() throws
Exception {
if (
resource instanceof
MBeanRegistration)
((
MBeanRegistration)
resource).
preDeregister();
}
public final void
postDeregister() {
// Undo any work from registration. We do this in postDeregister
// not preDeregister, because if the user preDeregister throws an
// exception then the MBean is not unregistered.
try {
unregister();
} finally {
if (
resource instanceof
MBeanRegistration)
((
MBeanRegistration)
resource).
postDeregister();
}
}
public final
Object getAttribute(
String attribute)
throws
AttributeNotFoundException,
MBeanException,
ReflectionException {
return
perInterface.
getAttribute(
resource,
attribute,
getCookie());
}
public final
AttributeList getAttributes(
String[]
attributes) {
final
AttributeList result = new
AttributeList(
attributes.length);
for (
String attrName :
attributes) {
try {
final
Object attrValue =
getAttribute(
attrName);
result.
add(new
Attribute(
attrName,
attrValue));
} catch (
Exception e) {
// OK: attribute is not included in returned list, per spec
// XXX: log the exception
}
}
return
result;
}
public final void
setAttribute(
Attribute attribute)
throws
AttributeNotFoundException,
InvalidAttributeValueException,
MBeanException,
ReflectionException {
final
String name =
attribute.
getName();
final
Object value =
attribute.
getValue();
perInterface.
setAttribute(
resource,
name,
value,
getCookie());
}
public final
AttributeList setAttributes(
AttributeList attributes) {
final
AttributeList result = new
AttributeList(
attributes.
size());
for (
Object attrObj :
attributes) {
// We can't use AttributeList.asList because it has side-effects
Attribute attr = (
Attribute)
attrObj;
try {
setAttribute(
attr);
result.
add(new
Attribute(
attr.
getName(),
attr.
getValue()));
} catch (
Exception e) {
// OK: attribute is not included in returned list, per spec
// XXX: log the exception
}
}
return
result;
}
public final
Object invoke(
String operation,
Object[]
params,
String[]
signature)
throws
MBeanException,
ReflectionException {
return
perInterface.
invoke(
resource,
operation,
params,
signature,
getCookie());
}
// Overridden by StandardMBeanSupport
public
MBeanInfo getMBeanInfo() {
return
mbeanInfo;
}
public final
String getClassName() {
return
resource.
getClass().
getName();
}
public final
Object getResource() {
return
resource;
}
public final
Class<?>
getMBeanInterface() {
return
perInterface.
getMBeanInterface();
}
private final
MBeanInfo mbeanInfo;
private final
Object resource;
private final
PerInterface<M>
perInterface;
}