1/*
2 * Copyright (c) 2005, 2013, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation.  Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26package com.sun.jmx.mbeanserver;
27
28
29import javax.management.Attribute;
30import javax.management.AttributeList;
31import javax.management.AttributeNotFoundException;
32import javax.management.InvalidAttributeValueException;
33import javax.management.MBeanException;
34import javax.management.MBeanInfo;
35import javax.management.MBeanRegistration;
36import javax.management.MBeanServer;
37import javax.management.NotCompliantMBeanException;
38import javax.management.ObjectName;
39import javax.management.ReflectionException;
40import com.sun.jmx.mbeanserver.MXBeanMappingFactory;
41import sun.reflect.misc.ReflectUtil;
42
43/**
44 * Base class for MBeans.  There is one instance of this class for
45 * every Standard MBean and every MXBean.  We try to limit the amount
46 * of information per instance so we can handle very large numbers of
47 * MBeans comfortably.
48 *
49 * @param <M> either Method or ConvertingMethod, for Standard MBeans
50 * and MXBeans respectively.
51 *
52 * @since 1.6
53 */
54/*
55 * We maintain a couple of caches to increase sharing between
56 * different MBeans of the same type and also to reduce creation time
57 * for the second and subsequent instances of the same type.
58 *
59 * The first cache maps from an MBean interface to a PerInterface
60 * object containing information parsed out of the interface.  The
61 * interface is either a Standard MBean interface or an MXBean
62 * interface, and there is one cache for each case.
63 *
64 * The PerInterface includes an MBeanInfo.  This contains the
65 * attributes and operations parsed out of the interface's methods,
66 * plus a basic Descriptor for the interface containing at least the
67 * interfaceClassName field and any fields derived from annotations on
68 * the interface.  This MBeanInfo can never be the MBeanInfo for any
69 * actual MBean, because an MBeanInfo's getClassName() is the name of
70 * a concrete class and we don't know what the class will be.
71 * Furthermore a real MBeanInfo may need to add constructors and/or
72 * notifications to the MBeanInfo.
73 *
74 * The PerInterface also contains an MBeanDispatcher which is able to
75 * route getAttribute, setAttribute, and invoke to the appropriate
76 * method of the interface, including doing any necessary translation
77 * of parameters and return values for MXBeans.
78 *
79 * The PerInterface also contains the original Class for the interface.
80 *
81 * We need to be careful about references.  When there are no MBeans
82 * with a given interface, there must not be any strong references to
83 * the interface Class.  Otherwise it could never be garbage collected,
84 * and neither could its ClassLoader or any other classes loaded by
85 * its ClassLoader.  Therefore the cache must wrap the PerInterface
86 * in a WeakReference.  Each instance of MBeanSupport has a strong
87 * reference to its PerInterface, which prevents PerInterface instances
88 * from being garbage-collected prematurely.
89 *
90 * The second cache maps from a concrete class and an MBean interface
91 * that that class implements to the MBeanInfo for that class and
92 * interface.  (The ability to specify an interface separately comes
93 * from the class StandardMBean.  MBeans registered directly in the
94 * MBean Server will always have the same interface here.)
95 *
96 * The MBeanInfo in this second cache will be the MBeanInfo from the
97 * PerInterface cache for the given itnerface, but with the
98 * getClassName() having the concrete class's name, and the public
99 * constructors based on the concrete class's constructors.  This
100 * MBeanInfo can be shared between all instances of the concrete class
101 * specifying the same interface, except instances that are
102 * NotificationBroadcasters.  NotificationBroadcasters supply the
103 * MBeanNotificationInfo[] in the MBeanInfo based on the instance
104 * method NotificationBroadcaster.getNotificationInfo(), so two
105 * instances of the same concrete class do not necessarily have the
106 * same MBeanNotificationInfo[].  Currently we do not try to detect
107 * when they do, although it would probably be worthwhile doing that
108 * since it is a very common case.
109 *
110 * Standard MBeans additionally have the property that
111 * getNotificationInfo() must in principle be called every time
112 * getMBeanInfo() is called for the MBean, since the returned array is
113 * allowed to change over time.  We attempt to reduce the cost of
114 * doing this by detecting when the Standard MBean is a subclass of
115 * NotificationBroadcasterSupport that does not override
116 * getNotificationInfo(), meaning that the MBeanNotificationInfo[] is
117 * the one that was supplied to the constructor.  MXBeans do not have
118 * this problem because their getNotificationInfo() method is called
119 * only once.
120 *
121 */
122public abstract class MBeanSupport<M>
123        implements DynamicMBean2, MBeanRegistration {
124
125    <T> MBeanSupport(T resource, Class<T> mbeanInterfaceType)
126            throws NotCompliantMBeanException {
127        if (mbeanInterfaceType == null)
128            throw new NotCompliantMBeanException("Null MBean interface");
129        if (!mbeanInterfaceType.isInstance(resource)) {
130            final String msg =
131                "Resource class " + resource.getClass().getName() +
132                " is not an instance of " + mbeanInterfaceType.getName();
133            throw new NotCompliantMBeanException(msg);
134        }
135        ReflectUtil.checkPackageAccess(mbeanInterfaceType);
136        this.resource = resource;
137        MBeanIntrospector<M> introspector = getMBeanIntrospector();
138        this.perInterface = introspector.getPerInterface(mbeanInterfaceType);
139        this.mbeanInfo = introspector.getMBeanInfo(resource, perInterface);
140    }
141
142    /** Return the appropriate introspector for this type of MBean. */
143    abstract MBeanIntrospector<M> getMBeanIntrospector();
144
145    /**
146     * Return a cookie for this MBean.  This cookie will be passed to
147     * MBean method invocations where it can supply additional information
148     * to the invocation.  For example, with MXBeans it can be used to
149     * supply the MXBeanLookup context for resolving inter-MXBean references.
150     */
151    abstract Object getCookie();
152
153    public final boolean isMXBean() {
154        return perInterface.isMXBean();
155    }
156
157    // Methods that javax.management.StandardMBean should call from its
158    // preRegister and postRegister, given that it is not supposed to
159    // call the contained object's preRegister etc methods even if it has them
160    public abstract void register(MBeanServer mbs, ObjectName name)
161            throws Exception;
162    public abstract void unregister();
163
164    public final ObjectName preRegister(MBeanServer server, ObjectName name)
165            throws Exception {
166        if (resource instanceof MBeanRegistration)
167            name = ((MBeanRegistration) resource).preRegister(server, name);
168        return name;
169    }
170
171    public final void preRegister2(MBeanServer server, ObjectName name)
172            throws Exception {
173        register(server, name);
174    }
175
176    public final void registerFailed() {
177        unregister();
178    }
179
180    public final void postRegister(Boolean registrationDone) {
181        if (resource instanceof MBeanRegistration)
182            ((MBeanRegistration) resource).postRegister(registrationDone);
183    }
184
185    public final void preDeregister() throws Exception {
186        if (resource instanceof MBeanRegistration)
187            ((MBeanRegistration) resource).preDeregister();
188    }
189
190    public final void postDeregister() {
191        // Undo any work from registration.  We do this in postDeregister
192        // not preDeregister, because if the user preDeregister throws an
193        // exception then the MBean is not unregistered.
194        try {
195            unregister();
196        } finally {
197            if (resource instanceof MBeanRegistration)
198                ((MBeanRegistration) resource).postDeregister();
199        }
200    }
201
202    public final Object getAttribute(String attribute)
203            throws AttributeNotFoundException,
204                   MBeanException,
205                   ReflectionException {
206        return perInterface.getAttribute(resource, attribute, getCookie());
207    }
208
209    public final AttributeList getAttributes(String[] attributes) {
210        final AttributeList result = new AttributeList(attributes.length);
211        for (String attrName : attributes) {
212            try {
213                final Object attrValue = getAttribute(attrName);
214                result.add(new Attribute(attrName, attrValue));
215            } catch (Exception e) {
216                // OK: attribute is not included in returned list, per spec
217                // XXX: log the exception
218            }
219        }
220        return result;
221    }
222
223    public final void setAttribute(Attribute attribute)
224            throws AttributeNotFoundException,
225                   InvalidAttributeValueException,
226                   MBeanException,
227                   ReflectionException {
228        final String name = attribute.getName();
229        final Object value = attribute.getValue();
230        perInterface.setAttribute(resource, name, value, getCookie());
231    }
232
233    public final AttributeList setAttributes(AttributeList attributes) {
234        final AttributeList result = new AttributeList(attributes.size());
235        for (Object attrObj : attributes) {
236            // We can't use AttributeList.asList because it has side-effects
237            Attribute attr = (Attribute) attrObj;
238            try {
239                setAttribute(attr);
240                result.add(new Attribute(attr.getName(), attr.getValue()));
241            } catch (Exception e) {
242                // OK: attribute is not included in returned list, per spec
243                // XXX: log the exception
244            }
245        }
246        return result;
247    }
248
249    public final Object invoke(String operation, Object[] params,
250                         String[] signature)
251            throws MBeanException, ReflectionException {
252        return perInterface.invoke(resource, operation, params, signature,
253                                   getCookie());
254    }
255
256    // Overridden by StandardMBeanSupport
257    public MBeanInfo getMBeanInfo() {
258        return mbeanInfo;
259    }
260
261    public final String getClassName() {
262        return resource.getClass().getName();
263    }
264
265    public final Object getResource() {
266        return resource;
267    }
268
269    public final Class<?> getMBeanInterface() {
270        return perInterface.getMBeanInterface();
271    }
272
273    private final MBeanInfo mbeanInfo;
274    private final Object resource;
275    private final PerInterface<M> perInterface;
276}
277