/* | 
|
 * Copyright (c) 2005, 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 com.sun.jmx.mbeanserver;  | 
|
import static com.sun.jmx.mbeanserver.Util.*;  | 
|
import java.util.Iterator;  | 
|
import java.util.Set;  | 
|
import javax.management.InstanceAlreadyExistsException;  | 
|
import javax.management.JMX;  | 
|
import javax.management.MBeanServer;  | 
|
import javax.management.NotCompliantMBeanException;  | 
|
import javax.management.ObjectName;  | 
|
/** | 
|
 * Base class for MXBeans. | 
|
 * | 
|
 * @since 1.6 | 
|
*/  | 
|
public class MXBeanSupport extends MBeanSupport<ConvertingMethod> {  | 
|
    /** | 
|
       <p>Construct an MXBean that wraps the given resource using the | 
|
       given MXBean interface.</p> | 
|
 | 
|
       @param resource the underlying resource for the new MXBean. | 
|
 | 
|
       @param mxbeanInterface the interface to be used to determine | 
|
       the MXBean's management interface. | 
|
 | 
|
       @param <T> a type parameter that allows the compiler to check | 
|
       that {@code resource} implements {@code mxbeanInterface}, | 
|
       provided that {@code mxbeanInterface} is a class constant like | 
|
       {@code SomeMXBean.class}. | 
|
 | 
|
       @throws IllegalArgumentException if {@code resource} is null or | 
|
       if it does not implement the class {@code mxbeanInterface} or if | 
|
       that class is not a valid MXBean interface. | 
|
*/  | 
|
public <T> MXBeanSupport(T resource, Class<T> mxbeanInterface)  | 
|
throws NotCompliantMBeanException {  | 
|
super(resource, mxbeanInterface);  | 
|
}  | 
|
@Override  | 
|
MBeanIntrospector<ConvertingMethod> getMBeanIntrospector() {  | 
|
return MXBeanIntrospector.getInstance();  | 
|
}  | 
|
@Override  | 
|
Object getCookie() {  | 
|
return mxbeanLookup;  | 
|
}  | 
|
static <T> Class<? super T> findMXBeanInterface(Class<T> resourceClass) {  | 
|
if (resourceClass == null)  | 
|
throw new IllegalArgumentException("Null resource class");  | 
|
final Set<Class<?>> intfs = transitiveInterfaces(resourceClass);  | 
|
final Set<Class<?>> candidates = newSet();  | 
|
for (Class<?> intf : intfs) {  | 
|
if (JMX.isMXBeanInterface(intf))  | 
|
candidates.add(intf);  | 
|
}  | 
|
reduce:  | 
|
while (candidates.size() > 1) {  | 
|
for (Class<?> intf : candidates) {  | 
|
for (Iterator<Class<?>> it = candidates.iterator(); it.hasNext();  | 
|
                    ) { | 
|
final Class<?> intf2 = it.next();  | 
|
if (intf != intf2 && intf2.isAssignableFrom(intf)) {  | 
|
it.remove();  | 
|
continue reduce;  | 
|
}  | 
|
}  | 
|
}  | 
|
final String msg =  | 
|
"Class " + resourceClass.getName() + " implements more than " +  | 
|
"one MXBean interface: " + candidates;  | 
|
throw new IllegalArgumentException(msg);  | 
|
}  | 
|
if (candidates.iterator().hasNext()) {  | 
|
return Util.cast(candidates.iterator().next());  | 
|
        } else { | 
|
final String msg =  | 
|
"Class " + resourceClass.getName() +  | 
|
                " is not a JMX compliant MXBean"; | 
|
throw new IllegalArgumentException(msg);  | 
|
}  | 
|
}  | 
|
    /* Return all interfaces inherited by this class, directly or | 
|
     * indirectly through the parent class and interfaces. | 
|
*/  | 
|
private static Set<Class<?>> transitiveInterfaces(Class<?> c) {  | 
|
Set<Class<?>> set = newSet();  | 
|
transitiveInterfaces(c, set);  | 
|
return set;  | 
|
}  | 
|
private static void transitiveInterfaces(Class<?> c, Set<Class<?>> intfs) {  | 
|
if (c == null)  | 
|
return;  | 
|
if (c.isInterface())  | 
|
intfs.add(c);  | 
|
transitiveInterfaces(c.getSuperclass(), intfs);  | 
|
for (Class<?> sup : c.getInterfaces())  | 
|
transitiveInterfaces(sup, intfs);  | 
|
}  | 
|
    /* | 
|
     * The sequence of events for tracking inter-MXBean references is | 
|
     * relatively complicated.  We use the magical preRegister2 method | 
|
     * which the MBeanServer knows about.  The steps during registration | 
|
     * are: | 
|
     * (1) Call user preRegister, if any.  If exception, abandon. | 
|
     * (2) Call preRegister2 and hence this register method.  If exception, | 
|
     * call postRegister(false) and abandon. | 
|
     * (3) Try to register the MBean.  If exception, call registerFailed() | 
|
     * which will call the unregister method.  (Also call postRegister(false).) | 
|
     * (4) If we get this far, we can call postRegister(true). | 
|
     * | 
|
     * When we are wrapped in an instance of javax.management.StandardMBean, | 
|
     * things are simpler.  That class calls this method from its preRegister, | 
|
     * and propagates any exception.  There is no user preRegister in this case. | 
|
     * If this method succeeds but registration subsequently fails, | 
|
     * StandardMBean calls unregister from its postRegister(false) method. | 
|
*/  | 
|
@Override  | 
|
public void register(MBeanServer server, ObjectName name)  | 
|
throws InstanceAlreadyExistsException {  | 
|
if (name == null)  | 
|
throw new IllegalArgumentException("Null object name");  | 
|
// eventually we could have some logic to supply a default name  | 
|
        synchronized (lock) { | 
|
this.mxbeanLookup = MXBeanLookup.lookupFor(server);  | 
|
this.mxbeanLookup.addReference(name, getResource());  | 
|
this.objectName = name;  | 
|
}  | 
|
}  | 
|
@Override  | 
|
    public void unregister() { | 
|
        synchronized (lock) { | 
|
            if (mxbeanLookup != null) { | 
|
if (mxbeanLookup.removeReference(objectName, getResource()))  | 
|
objectName = null;  | 
|
}  | 
|
}  | 
|
}  | 
|
private final Object lock = new Object(); // for mxbeanLookup and objectName  | 
|
private MXBeanLookup mxbeanLookup;  | 
|
private ObjectName objectName;  | 
|
}  |