| /* |
| * 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; |
| import javax.management.openmbean.MXBeanMappingFactory; |
| import javax.management.openmbean.MXBeanMappingFactoryClass; |
| |
| /** |
| * 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, |
| MXBeanMappingFactory mappingFactory) |
| throws NotCompliantMBeanException { |
| super(resource, mxbeanInterface, mappingFactory); |
| } |
| |
| @Override |
| MBeanIntrospector<ConvertingMethod> |
| getMBeanIntrospector(MXBeanMappingFactory mappingFactory) { |
| return MXBeanIntrospector.getInstance(mappingFactory); |
| } |
| |
| @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.Plain.lookupFor(server); |
| this.mxbeanLookup.addReference(name, getWrappedObject()); |
| this.objectName = name; |
| } |
| } |
| |
| @Override |
| public void unregister() { |
| synchronized (lock) { |
| if (mxbeanLookup != null) { |
| if (mxbeanLookup.removeReference(objectName, getWrappedObject())) |
| objectName = null; |
| } |
| // XXX: need to revisit the whole register/unregister logic in |
| // the face of wrapping. The mxbeanLookup!=null test is a hack. |
| // If you wrap an MXBean in a MyWrapperMBean and register it, |
| // the lookup table should contain the wrapped object. But that |
| // implies that MyWrapperMBean calls register, which today it |
| // can't within the public API. |
| } |
| } |
| private final Object lock = new Object(); // for mxbeanLookup and objectName |
| |
| private MXBeanLookup.Plain mxbeanLookup; |
| private ObjectName objectName; |
| } |