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 
26 package com.sun.jmx.mbeanserver;
27 
28 
29 import javax.management.Attribute;
30 import javax.management.AttributeList;
31 import javax.management.AttributeNotFoundException;
32 import javax.management.InvalidAttributeValueException;
33 import javax.management.MBeanException;
34 import javax.management.MBeanInfo;
35 import javax.management.MBeanRegistration;
36 import javax.management.MBeanServer;
37 import javax.management.NotCompliantMBeanException;
38 import javax.management.ObjectName;
39 import javax.management.ReflectionException;
40 import com.sun.jmx.mbeanserver.MXBeanMappingFactory;
41 import 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  */
122 public abstract class MBeanSupport<M>
123         implements DynamicMBean2, MBeanRegistration {
124 
MBeanSupport(T resource, Class<T> mbeanInterfaceType)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. */
getMBeanIntrospector()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      */
getCookie()151     abstract Object getCookie();
152 
isMXBean()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
register(MBeanServer mbs, ObjectName name)160     public abstract void register(MBeanServer mbs, ObjectName name)
161             throws Exception;
unregister()162     public abstract void unregister();
163 
preRegister(MBeanServer server, ObjectName name)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 
preRegister2(MBeanServer server, ObjectName name)171     public final void preRegister2(MBeanServer server, ObjectName name)
172             throws Exception {
173         register(server, name);
174     }
175 
registerFailed()176     public final void registerFailed() {
177         unregister();
178     }
179 
postRegister(Boolean registrationDone)180     public final void postRegister(Boolean registrationDone) {
181         if (resource instanceof MBeanRegistration)
182             ((MBeanRegistration) resource).postRegister(registrationDone);
183     }
184 
preDeregister()185     public final void preDeregister() throws Exception {
186         if (resource instanceof MBeanRegistration)
187             ((MBeanRegistration) resource).preDeregister();
188     }
189 
postDeregister()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 
getAttribute(String attribute)202     public final Object getAttribute(String attribute)
203             throws AttributeNotFoundException,
204                    MBeanException,
205                    ReflectionException {
206         return perInterface.getAttribute(resource, attribute, getCookie());
207     }
208 
getAttributes(String[] attributes)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 
setAttribute(Attribute attribute)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 
setAttributes(AttributeList attributes)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 
invoke(String operation, Object[] params, String[] signature)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
getMBeanInfo()257     public MBeanInfo getMBeanInfo() {
258         return mbeanInfo;
259     }
260 
getClassName()261     public final String getClassName() {
262         return resource.getClass().getName();
263     }
264 
getResource()265     public final Object getResource() {
266         return resource;
267     }
268 
getMBeanInterface()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