blob: 977c74a3230fb7139f57eb13b5622992923d4a36 [file] [log] [blame]
/*
* Copyright 2005-2006 Sun Microsystems, Inc. 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. Sun designates this
* particular file as subject to the "Classpath" exception as provided
* by Sun 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
* CA 95054 USA or visit www.sun.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 Class<?> findMXBeanInterface(Class<?> resourceClass)
throws IllegalArgumentException {
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 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) {
if (this.objectName != null) {
final String msg =
"MXBean already registered with name " + this.objectName;
throw new InstanceAlreadyExistsException(msg);
}
this.mxbeanLookup = MXBeanLookup.lookupFor(server);
this.mxbeanLookup.addReference(name, getResource());
this.objectName = name;
}
}
@Override
public void unregister() {
synchronized (lock) {
if (mxbeanLookup.removeReference(objectName, getResource()))
objectName = null;
}
}
private Object lock = new Object(); // for mxbeanLookup and objectName
private MXBeanLookup mxbeanLookup;
private ObjectName objectName;
}