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