/* * Copyright (c) 2005, 2006, 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 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; /** * 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> mbeanInterface) throws NotCompliantMBeanException { if (mbeanInterface == null) throw new NotCompliantMBeanException("Null MBean interface"); if (!mbeanInterface.isInstance(resource)) { final String msg = "Resource class " + resource.getClass().getName() + " is not an instance of " + mbeanInterface.getName(); throw new NotCompliantMBeanException(msg); } this.resource = resource; MBeanIntrospector<M> introspector = getMBeanIntrospector(); this.perInterface = introspector.getPerInterface(mbeanInterface); 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) return ((MBeanRegistration) resource).preRegister(server, name); else 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; }