/* | 
|
 * Copyright (c) 2005, 2013, 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 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;  | 
|
}  |