blob: 4fe37152c7f47813e4b50577e7c70a0138e55728 [file] [log] [blame]
J. Duke319a3b92007-12-01 00:00:00 +00001/*
2 * Copyright 2002-2007 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 javax.management;
27
28import static com.sun.jmx.defaults.JmxProperties.MISC_LOGGER;
29import com.sun.jmx.mbeanserver.DescriptorCache;
30import com.sun.jmx.mbeanserver.Introspector;
31import com.sun.jmx.mbeanserver.MBeanSupport;
32import com.sun.jmx.mbeanserver.MXBeanSupport;
33import com.sun.jmx.mbeanserver.StandardMBeanSupport;
34import com.sun.jmx.mbeanserver.Util;
35
36import java.io.PrintWriter;
37import java.io.StringWriter;
38import java.security.AccessController;
39import java.security.PrivilegedAction;
40import java.util.HashMap;
41import java.util.Map;
42import java.util.WeakHashMap;
43import java.util.logging.Level;
44import javax.management.openmbean.OpenMBeanAttributeInfo;
45import javax.management.openmbean.OpenMBeanAttributeInfoSupport;
46import javax.management.openmbean.OpenMBeanConstructorInfo;
47import javax.management.openmbean.OpenMBeanConstructorInfoSupport;
48import javax.management.openmbean.OpenMBeanOperationInfo;
49import javax.management.openmbean.OpenMBeanOperationInfoSupport;
50import javax.management.openmbean.OpenMBeanParameterInfo;
51import javax.management.openmbean.OpenMBeanParameterInfoSupport;
52
53/**
54 * <p>An MBean whose management interface is determined by reflection
55 * on a Java interface.</p>
56 *
57 * <p>This class brings more flexibility to the notion of Management
58 * Interface in the use of Standard MBeans. Straightforward use of
59 * the patterns for Standard MBeans described in the JMX Specification
60 * means that there is a fixed relationship between the implementation
61 * class of an MBean and its management interface (i.e., if the
62 * implementation class is Thing, the management interface must be
63 * ThingMBean). This class makes it possible to keep the convenience
64 * of specifying the management interface with a Java interface,
65 * without requiring that there be any naming relationship between the
66 * implementation and interface classes.</p>
67 *
68 * <p>By making a DynamicMBean out of an MBean, this class makes
69 * it possible to select any interface implemented by the MBean as its
70 * management interface, provided that it complies with JMX patterns
71 * (i.e., attributes defined by getter/setter etc...).</p>
72 *
73 * <p> This class also provides hooks that make it possible to supply
74 * custom descriptions and names for the {@link MBeanInfo} returned by
75 * the DynamicMBean interface.</p>
76 *
77 * <p>Using this class, an MBean can be created with any
78 * implementation class name <i>Impl</i> and with a management
79 * interface defined (as for current Standard MBeans) by any interface
80 * <i>Intf</i>, in one of two general ways:</p>
81 *
82 * <ul>
83 *
84 * <li>Using the public constructor
85 * {@link #StandardMBean(java.lang.Object, java.lang.Class, boolean)
86 * StandardMBean(impl,interface)}:
87 * <pre>
88 * MBeanServer mbs;
89 * ...
90 * Impl impl = new Impl(...);
91 * StandardMBean mbean = new StandardMBean(impl, Intf.class, false);
92 * mbs.registerMBean(mbean, objectName);
93 * </pre></li>
94 *
95 * <li>Subclassing StandardMBean:
96 * <pre>
97 * public class Impl extends StandardMBean implements Intf {
98 * public Impl() {
99 * super(Intf.class, false);
100 * }
101 * // implement methods of Intf
102 * }
103 *
104 * [...]
105 *
106 * MBeanServer mbs;
107 * ....
108 * Impl impl = new Impl();
109 * mbs.registerMBean(impl, objectName);
110 * </pre></li>
111 *
112 * </ul>
113 *
114 * <p>In either case, the class <i>Impl</i> must implement the
115 * interface <i>Intf</i>.</p>
116 *
117 * <p>Standard MBeans based on the naming relationship between
118 * implementation and interface classes are of course still
119 * available.</p>
120 *
121 * <p>This class may also be used to construct MXBeans. The usage
122 * is exactly the same as for Standard MBeans except that in the
123 * examples above, the {@code false} parameter to the constructor or
124 * {@code super(...)} invocation is instead {@code true}.</p>
125 *
126 * @since 1.5
127 */
128public class StandardMBean implements DynamicMBean, MBeanRegistration {
129
130 private final static DescriptorCache descriptors =
131 DescriptorCache.getInstance(JMX.proof);
132
133 /**
134 * The DynamicMBean that wraps the MXBean or Standard MBean implementation.
135 **/
136 private volatile MBeanSupport<?> mbean;
137
138 /**
139 * The cached MBeanInfo.
140 **/
141 private volatile MBeanInfo cachedMBeanInfo;
142
143 /**
144 * Make a DynamicMBean out of <var>implementation</var>, using the
145 * specified <var>mbeanInterface</var> class.
146 * @param implementation The implementation of this MBean.
147 * If <code>null</code>, and null implementation is allowed,
148 * then the implementation is assumed to be <var>this</var>.
149 * @param mbeanInterface The Management Interface exported by this
150 * MBean's implementation. If <code>null</code>, then this
151 * object will use standard JMX design pattern to determine
152 * the management interface associated with the given
153 * implementation.
154 * @param nullImplementationAllowed <code>true</code> if a null
155 * implementation is allowed. If null implementation is allowed,
156 * and a null implementation is passed, then the implementation
157 * is assumed to be <var>this</var>.
158 * @exception IllegalArgumentException if the given
159 * <var>implementation</var> is null, and null is not allowed.
160 **/
161 private <T> void construct(T implementation, Class<T> mbeanInterface,
162 boolean nullImplementationAllowed,
163 boolean isMXBean)
164 throws NotCompliantMBeanException {
165 if (implementation == null) {
166 // Have to use (T)this rather than mbeanInterface.cast(this)
167 // because mbeanInterface might be null.
168 if (nullImplementationAllowed)
169 implementation = Util.<T>cast(this);
170 else throw new IllegalArgumentException("implementation is null");
171 }
172 if (isMXBean) {
173 if (mbeanInterface == null) {
174 mbeanInterface = Util.cast(Introspector.getMXBeanInterface(
175 implementation.getClass()));
176 }
177 this.mbean = new MXBeanSupport(implementation, mbeanInterface);
178 } else {
179 if (mbeanInterface == null) {
180 mbeanInterface = Util.cast(Introspector.getStandardMBeanInterface(
181 implementation.getClass()));
182 }
183 this.mbean =
184 new StandardMBeanSupport(implementation, mbeanInterface);
185 }
186 }
187
188 /**
189 * <p>Make a DynamicMBean out of the object
190 * <var>implementation</var>, using the specified
191 * <var>mbeanInterface</var> class.</p>
192 *
193 * @param implementation The implementation of this MBean.
194 * @param mbeanInterface The Management Interface exported by this
195 * MBean's implementation. If <code>null</code>, then this
196 * object will use standard JMX design pattern to determine
197 * the management interface associated with the given
198 * implementation.
199 * @param <T> Allows the compiler to check
200 * that {@code implementation} does indeed implement the class
201 * described by {@code mbeanInterface}. The compiler can only
202 * check this if {@code mbeanInterface} is a class literal such
203 * as {@code MyMBean.class}.
204 *
205 * @exception IllegalArgumentException if the given
206 * <var>implementation</var> is null.
207 * @exception NotCompliantMBeanException if the <var>mbeanInterface</var>
208 * does not follow JMX design patterns for Management Interfaces, or
209 * if the given <var>implementation</var> does not implement the
210 * specified interface.
211 **/
212 public <T> StandardMBean(T implementation, Class<T> mbeanInterface)
213 throws NotCompliantMBeanException {
214 construct(implementation, mbeanInterface, false, false);
215 }
216
217 /**
218 * <p>Make a DynamicMBean out of <var>this</var>, using the specified
219 * <var>mbeanInterface</var> class.</p>
220 *
221 * <p>Call {@link #StandardMBean(java.lang.Object, java.lang.Class)
222 * this(this,mbeanInterface)}.
223 * This constructor is reserved to subclasses.</p>
224 *
225 * @param mbeanInterface The Management Interface exported by this
226 * MBean.
227 *
228 * @exception NotCompliantMBeanException if the <var>mbeanInterface</var>
229 * does not follow JMX design patterns for Management Interfaces, or
230 * if <var>this</var> does not implement the specified interface.
231 **/
232 protected StandardMBean(Class<?> mbeanInterface)
233 throws NotCompliantMBeanException {
234 construct(null, mbeanInterface, true, false);
235 }
236
237 /**
238 * <p>Make a DynamicMBean out of the object
239 * <var>implementation</var>, using the specified
240 * <var>mbeanInterface</var> class. This constructor can be used
241 * to make either Standard MBeans or MXBeans. Unlike the
242 * constructor {@link #StandardMBean(Object, Class)}, it
243 * does not throw NotCompliantMBeanException.</p>
244 *
245 * @param implementation The implementation of this MBean.
246 * @param mbeanInterface The Management Interface exported by this
247 * MBean's implementation. If <code>null</code>, then this
248 * object will use standard JMX design pattern to determine
249 * the management interface associated with the given
250 * implementation.
251 * @param isMXBean If true, the {@code mbeanInterface} parameter
252 * names an MXBean interface and the resultant MBean is an MXBean.
253 * @param <T> Allows the compiler to check
254 * that {@code implementation} does indeed implement the class
255 * described by {@code mbeanInterface}. The compiler can only
256 * check this if {@code mbeanInterface} is a class literal such
257 * as {@code MyMBean.class}.
258 *
259 * @exception IllegalArgumentException if the given
260 * <var>implementation</var> is null, or if the <var>mbeanInterface</var>
261 * does not follow JMX design patterns for Management Interfaces, or
262 * if the given <var>implementation</var> does not implement the
263 * specified interface.
264 *
265 * @since 1.6
266 **/
267 public <T> StandardMBean(T implementation, Class<T> mbeanInterface,
268 boolean isMXBean) {
269 try {
270 construct(implementation, mbeanInterface, false, isMXBean);
271 } catch (NotCompliantMBeanException e) {
272 throw new IllegalArgumentException(e);
273 }
274 }
275
276 /**
277 * <p>Make a DynamicMBean out of <var>this</var>, using the specified
278 * <var>mbeanInterface</var> class. This constructor can be used
279 * to make either Standard MBeans or MXBeans. Unlike the
280 * constructor {@link #StandardMBean(Object, Class)}, it
281 * does not throw NotCompliantMBeanException.</p>
282 *
283 * <p>Call {@link #StandardMBean(java.lang.Object, java.lang.Class, boolean)
284 * this(this, mbeanInterface, isMXBean)}.
285 * This constructor is reserved to subclasses.</p>
286 *
287 * @param mbeanInterface The Management Interface exported by this
288 * MBean.
289 * @param isMXBean If true, the {@code mbeanInterface} parameter
290 * names an MXBean interface and the resultant MBean is an MXBean.
291 *
292 * @exception IllegalArgumentException if the <var>mbeanInterface</var>
293 * does not follow JMX design patterns for Management Interfaces, or
294 * if <var>this</var> does not implement the specified interface.
295 *
296 * @since 1.6
297 **/
298 protected StandardMBean(Class<?> mbeanInterface, boolean isMXBean) {
299 try {
300 construct(null, mbeanInterface, true, isMXBean);
301 } catch (NotCompliantMBeanException e) {
302 throw new IllegalArgumentException(e);
303 }
304 }
305
306 /**
307 * <p>Replace the implementation object wrapped in this object.</p>
308 *
309 * @param implementation The new implementation of this Standard MBean
310 * (or MXBean). The <code>implementation</code> object must implement
311 * the Standard MBean (or MXBean) interface that was supplied when this
312 * <code>StandardMBean</code> was constructed.
313 *
314 * @exception IllegalArgumentException if the given
315 * <var>implementation</var> is null.
316 *
317 * @exception NotCompliantMBeanException if the given
318 * <var>implementation</var> does not implement the
319 * Standard MBean (or MXBean) interface that was
320 * supplied at construction.
321 *
322 * @see #getImplementation
323 **/
324 public void setImplementation(Object implementation)
325 throws NotCompliantMBeanException {
326
327 if (implementation == null)
328 throw new IllegalArgumentException("implementation is null");
329
330 if (isMXBean()) {
331 this.mbean = new MXBeanSupport(implementation,
332 Util.<Class<Object>>cast(getMBeanInterface()));
333 } else {
334 this.mbean = new StandardMBeanSupport(implementation,
335 Util.<Class<Object>>cast(getMBeanInterface()));
336 }
337 }
338
339 /**
340 * Get the implementation of this Standard MBean (or MXBean).
341 * @return The implementation of this Standard MBean (or MXBean).
342 *
343 * @see #setImplementation
344 **/
345 public Object getImplementation() {
346 return mbean.getResource();
347 }
348
349 /**
350 * Get the Management Interface of this Standard MBean (or MXBean).
351 * @return The management interface of this Standard MBean (or MXBean).
352 **/
353 public final Class<?> getMBeanInterface() {
354 return mbean.getMBeanInterface();
355 }
356
357 /**
358 * Get the class of the implementation of this Standard MBean (or MXBean).
359 * @return The class of the implementation of this Standard MBean (or MXBean).
360 **/
361 public Class<?> getImplementationClass() {
362 return mbean.getResource().getClass();
363 }
364
365 // ------------------------------------------------------------------
366 // From the DynamicMBean interface.
367 // ------------------------------------------------------------------
368 public Object getAttribute(String attribute)
369 throws AttributeNotFoundException,
370 MBeanException,
371 ReflectionException {
372 return mbean.getAttribute(attribute);
373 }
374
375 // ------------------------------------------------------------------
376 // From the DynamicMBean interface.
377 // ------------------------------------------------------------------
378 public void setAttribute(Attribute attribute)
379 throws AttributeNotFoundException,
380 InvalidAttributeValueException,
381 MBeanException,
382 ReflectionException {
383 mbean.setAttribute(attribute);
384 }
385
386 // ------------------------------------------------------------------
387 // From the DynamicMBean interface.
388 // ------------------------------------------------------------------
389 public AttributeList getAttributes(String[] attributes) {
390 return mbean.getAttributes(attributes);
391 }
392
393 // ------------------------------------------------------------------
394 // From the DynamicMBean interface.
395 // ------------------------------------------------------------------
396 public AttributeList setAttributes(AttributeList attributes) {
397 return mbean.setAttributes(attributes);
398 }
399
400 // ------------------------------------------------------------------
401 // From the DynamicMBean interface.
402 // ------------------------------------------------------------------
403 public Object invoke(String actionName, Object params[], String signature[])
404 throws MBeanException, ReflectionException {
405 return mbean.invoke(actionName, params, signature);
406 }
407
408 /**
409 * Get the {@link MBeanInfo} for this MBean.
410 * <p>
411 * This method implements
412 * {@link javax.management.DynamicMBean#getMBeanInfo()
413 * DynamicMBean.getMBeanInfo()}.
414 * <p>
415 * This method first calls {@link #getCachedMBeanInfo()} in order to
416 * retrieve the cached MBeanInfo for this MBean, if any. If the
417 * MBeanInfo returned by {@link #getCachedMBeanInfo()} is not null,
418 * then it is returned.<br>
419 * Otherwise, this method builds a default MBeanInfo for this MBean,
420 * using the Management Interface specified for this MBean.
421 * <p>
422 * While building the MBeanInfo, this method calls the customization
423 * hooks that make it possible for subclasses to supply their custom
424 * descriptions, parameter names, etc...<br>
425 * Finally, it calls {@link #cacheMBeanInfo(javax.management.MBeanInfo)
426 * cacheMBeanInfo()} in order to cache the new MBeanInfo.
427 * @return The cached MBeanInfo for that MBean, if not null, or a
428 * newly built MBeanInfo if none was cached.
429 **/
430 public MBeanInfo getMBeanInfo() {
431 try {
432 final MBeanInfo cached = getCachedMBeanInfo();
433 if (cached != null) return cached;
434 } catch (RuntimeException x) {
435 if (MISC_LOGGER.isLoggable(Level.FINEST)) {
436 MISC_LOGGER.logp(Level.FINEST,
437 MBeanServerFactory.class.getName(), "getMBeanInfo",
438 "Failed to get cached MBeanInfo", x);
439 }
440 }
441
442 if (MISC_LOGGER.isLoggable(Level.FINER)) {
443 MISC_LOGGER.logp(Level.FINER,
444 MBeanServerFactory.class.getName(), "getMBeanInfo",
445 "Building MBeanInfo for " +
446 getImplementationClass().getName());
447 }
448
449 MBeanSupport msupport = mbean;
450 final MBeanInfo bi = msupport.getMBeanInfo();
451 final Object impl = msupport.getResource();
452
453 final boolean immutableInfo = immutableInfo(this.getClass());
454
455 final String cname = getClassName(bi);
456 final String text = getDescription(bi);
457 final MBeanConstructorInfo[] ctors = getConstructors(bi,impl);
458 final MBeanAttributeInfo[] attrs = getAttributes(bi);
459 final MBeanOperationInfo[] ops = getOperations(bi);
460 final MBeanNotificationInfo[] ntfs = getNotifications(bi);
461 final Descriptor desc = getDescriptor(bi, immutableInfo);
462
463 final MBeanInfo nmbi = new MBeanInfo(
464 cname, text, attrs, ctors, ops, ntfs, desc);
465 try {
466 cacheMBeanInfo(nmbi);
467 } catch (RuntimeException x) {
468 if (MISC_LOGGER.isLoggable(Level.FINEST)) {
469 MISC_LOGGER.logp(Level.FINEST,
470 MBeanServerFactory.class.getName(), "getMBeanInfo",
471 "Failed to cache MBeanInfo", x);
472 }
473 }
474
475 return nmbi;
476 }
477
478 /**
479 * Customization hook:
480 * Get the className that will be used in the MBeanInfo returned by
481 * this MBean.
482 * <br>
483 * Subclasses may redefine this method in order to supply their
484 * custom class name. The default implementation returns
485 * {@link MBeanInfo#getClassName() info.getClassName()}.
486 * @param info The default MBeanInfo derived by reflection.
487 * @return the class name for the new MBeanInfo.
488 **/
489 protected String getClassName(MBeanInfo info) {
490 if (info == null) return getImplementationClass().getName();
491 return info.getClassName();
492 }
493
494 /**
495 * Customization hook:
496 * Get the description that will be used in the MBeanInfo returned by
497 * this MBean.
498 * <br>
499 * Subclasses may redefine this method in order to supply their
500 * custom MBean description. The default implementation returns
501 * {@link MBeanInfo#getDescription() info.getDescription()}.
502 * @param info The default MBeanInfo derived by reflection.
503 * @return the description for the new MBeanInfo.
504 **/
505 protected String getDescription(MBeanInfo info) {
506 if (info == null) return null;
507 return info.getDescription();
508 }
509
510 /**
511 * <p>Customization hook:
512 * Get the description that will be used in the MBeanFeatureInfo
513 * returned by this MBean.</p>
514 *
515 * <p>Subclasses may redefine this method in order to supply
516 * their custom description. The default implementation returns
517 * {@link MBeanFeatureInfo#getDescription()
518 * info.getDescription()}.</p>
519 *
520 * <p>This method is called by
521 * {@link #getDescription(MBeanAttributeInfo)},
522 * {@link #getDescription(MBeanOperationInfo)},
523 * {@link #getDescription(MBeanConstructorInfo)}.</p>
524 *
525 * @param info The default MBeanFeatureInfo derived by reflection.
526 * @return the description for the given MBeanFeatureInfo.
527 **/
528 protected String getDescription(MBeanFeatureInfo info) {
529 if (info == null) return null;
530 return info.getDescription();
531 }
532
533 /**
534 * Customization hook:
535 * Get the description that will be used in the MBeanAttributeInfo
536 * returned by this MBean.
537 *
538 * <p>Subclasses may redefine this method in order to supply their
539 * custom description. The default implementation returns {@link
540 * #getDescription(MBeanFeatureInfo)
541 * getDescription((MBeanFeatureInfo) info)}.
542 * @param info The default MBeanAttributeInfo derived by reflection.
543 * @return the description for the given MBeanAttributeInfo.
544 **/
545 protected String getDescription(MBeanAttributeInfo info) {
546 return getDescription((MBeanFeatureInfo)info);
547 }
548
549 /**
550 * Customization hook:
551 * Get the description that will be used in the MBeanConstructorInfo
552 * returned by this MBean.
553 * <br>
554 * Subclasses may redefine this method in order to supply their
555 * custom description.
556 * The default implementation returns {@link
557 * #getDescription(MBeanFeatureInfo)
558 * getDescription((MBeanFeatureInfo) info)}.
559 * @param info The default MBeanConstructorInfo derived by reflection.
560 * @return the description for the given MBeanConstructorInfo.
561 **/
562 protected String getDescription(MBeanConstructorInfo info) {
563 return getDescription((MBeanFeatureInfo)info);
564 }
565
566 /**
567 * Customization hook:
568 * Get the description that will be used for the <var>sequence</var>
569 * MBeanParameterInfo of the MBeanConstructorInfo returned by this MBean.
570 * <br>
571 * Subclasses may redefine this method in order to supply their
572 * custom description. The default implementation returns
573 * {@link MBeanParameterInfo#getDescription() param.getDescription()}.
574 *
575 * @param ctor The default MBeanConstructorInfo derived by reflection.
576 * @param param The default MBeanParameterInfo derived by reflection.
577 * @param sequence The sequence number of the parameter considered
578 * ("0" for the first parameter, "1" for the second parameter,
579 * etc...).
580 * @return the description for the given MBeanParameterInfo.
581 **/
582 protected String getDescription(MBeanConstructorInfo ctor,
583 MBeanParameterInfo param,
584 int sequence) {
585 if (param == null) return null;
586 return param.getDescription();
587 }
588
589 /**
590 * Customization hook:
591 * Get the name that will be used for the <var>sequence</var>
592 * MBeanParameterInfo of the MBeanConstructorInfo returned by this MBean.
593 * <br>
594 * Subclasses may redefine this method in order to supply their
595 * custom parameter name. The default implementation returns
596 * {@link MBeanParameterInfo#getName() param.getName()}.
597 *
598 * @param ctor The default MBeanConstructorInfo derived by reflection.
599 * @param param The default MBeanParameterInfo derived by reflection.
600 * @param sequence The sequence number of the parameter considered
601 * ("0" for the first parameter, "1" for the second parameter,
602 * etc...).
603 * @return the name for the given MBeanParameterInfo.
604 **/
605 protected String getParameterName(MBeanConstructorInfo ctor,
606 MBeanParameterInfo param,
607 int sequence) {
608 if (param == null) return null;
609 return param.getName();
610 }
611
612 /**
613 * Customization hook:
614 * Get the description that will be used in the MBeanOperationInfo
615 * returned by this MBean.
616 * <br>
617 * Subclasses may redefine this method in order to supply their
618 * custom description. The default implementation returns
619 * {@link #getDescription(MBeanFeatureInfo)
620 * getDescription((MBeanFeatureInfo) info)}.
621 * @param info The default MBeanOperationInfo derived by reflection.
622 * @return the description for the given MBeanOperationInfo.
623 **/
624 protected String getDescription(MBeanOperationInfo info) {
625 return getDescription((MBeanFeatureInfo)info);
626 }
627
628 /**
629 * Customization hook:
630 * Get the <var>impact</var> flag of the operation that will be used in
631 * the MBeanOperationInfo returned by this MBean.
632 * <br>
633 * Subclasses may redefine this method in order to supply their
634 * custom impact flag. The default implementation returns
635 * {@link MBeanOperationInfo#getImpact() info.getImpact()}.
636 * @param info The default MBeanOperationInfo derived by reflection.
637 * @return the impact flag for the given MBeanOperationInfo.
638 **/
639 protected int getImpact(MBeanOperationInfo info) {
640 if (info == null) return MBeanOperationInfo.UNKNOWN;
641 return info.getImpact();
642 }
643
644 /**
645 * Customization hook:
646 * Get the name that will be used for the <var>sequence</var>
647 * MBeanParameterInfo of the MBeanOperationInfo returned by this MBean.
648 * <br>
649 * Subclasses may redefine this method in order to supply their
650 * custom parameter name. The default implementation returns
651 * {@link MBeanParameterInfo#getName() param.getName()}.
652 *
653 * @param op The default MBeanOperationInfo derived by reflection.
654 * @param param The default MBeanParameterInfo derived by reflection.
655 * @param sequence The sequence number of the parameter considered
656 * ("0" for the first parameter, "1" for the second parameter,
657 * etc...).
658 * @return the name to use for the given MBeanParameterInfo.
659 **/
660 protected String getParameterName(MBeanOperationInfo op,
661 MBeanParameterInfo param,
662 int sequence) {
663 if (param == null) return null;
664 return param.getName();
665 }
666
667 /**
668 * Customization hook:
669 * Get the description that will be used for the <var>sequence</var>
670 * MBeanParameterInfo of the MBeanOperationInfo returned by this MBean.
671 * <br>
672 * Subclasses may redefine this method in order to supply their
673 * custom description. The default implementation returns
674 * {@link MBeanParameterInfo#getDescription() param.getDescription()}.
675 *
676 * @param op The default MBeanOperationInfo derived by reflection.
677 * @param param The default MBeanParameterInfo derived by reflection.
678 * @param sequence The sequence number of the parameter considered
679 * ("0" for the first parameter, "1" for the second parameter,
680 * etc...).
681 * @return the description for the given MBeanParameterInfo.
682 **/
683 protected String getDescription(MBeanOperationInfo op,
684 MBeanParameterInfo param,
685 int sequence) {
686 if (param == null) return null;
687 return param.getDescription();
688 }
689
690 /**
691 * Customization hook:
692 * Get the MBeanConstructorInfo[] that will be used in the MBeanInfo
693 * returned by this MBean.
694 * <br>
695 * By default, this method returns <code>null</code> if the wrapped
696 * implementation is not <var>this</var>. Indeed, if the wrapped
697 * implementation is not this object itself, it will not be possible
698 * to recreate a wrapped implementation by calling the implementation
699 * constructors through <code>MBeanServer.createMBean(...)</code>.<br>
700 * Otherwise, if the wrapped implementation is <var>this</var>,
701 * <var>ctors</var> is returned.
702 * <br>
703 * Subclasses may redefine this method in order to modify this
704 * behavior, if needed.
705 * @param ctors The default MBeanConstructorInfo[] derived by reflection.
706 * @param impl The wrapped implementation. If <code>null</code> is
707 * passed, the wrapped implementation is ignored and
708 * <var>ctors</var> is returned.
709 * @return the MBeanConstructorInfo[] for the new MBeanInfo.
710 **/
711 protected MBeanConstructorInfo[]
712 getConstructors(MBeanConstructorInfo[] ctors, Object impl) {
713 if (ctors == null) return null;
714 if (impl != null && impl != this) return null;
715 return ctors;
716 }
717
718 /**
719 * Customization hook:
720 * Get the MBeanNotificationInfo[] that will be used in the MBeanInfo
721 * returned by this MBean.
722 * <br>
723 * Subclasses may redefine this method in order to supply their
724 * custom notifications.
725 * @param info The default MBeanInfo derived by reflection.
726 * @return the MBeanNotificationInfo[] for the new MBeanInfo.
727 **/
728 MBeanNotificationInfo[] getNotifications(MBeanInfo info) {
729 return null;
730 }
731
732 /**
733 * <p>Get the Descriptor that will be used in the MBeanInfo
734 * returned by this MBean.</p>
735 *
736 * <p>Subclasses may redefine this method in order to supply
737 * their custom descriptor.</p>
738 *
739 * <p>The default implementation of this method returns a Descriptor
740 * that contains at least the field {@code interfaceClassName}, with
741 * value {@link #getMBeanInterface()}.getName(). It may also contain
742 * the field {@code immutableInfo}, with a value that is the string
743 * {@code "true"} if the implementation can determine that the
744 * {@code MBeanInfo} returned by {@link #getMBeanInfo()} will always
745 * be the same. It may contain other fields: fields defined by the
746 * JMX specification must have appropriate values, and other fields
747 * must follow the conventions for non-standard field names.</p>
748 *
749 * @param info The default MBeanInfo derived by reflection.
750 * @return the Descriptor for the new MBeanInfo.
751 */
752 Descriptor getDescriptor(MBeanInfo info, boolean immutableInfo) {
753 ImmutableDescriptor desc = null;
754 if (info == null ||
755 info.getDescriptor() == null ||
756 info.getDescriptor().getFieldNames().length == 0) {
757 final String interfaceClassNameS =
758 "interfaceClassName=" + getMBeanInterface().getName();
759 final String immutableInfoS =
760 "immutableInfo=" + immutableInfo;
761 desc = new ImmutableDescriptor(interfaceClassNameS, immutableInfoS);
762 desc = descriptors.get(desc);
763 } else {
764 Descriptor d = info.getDescriptor();
765 Map<String,Object> fields = new HashMap<String,Object>();
766 for (String fieldName : d.getFieldNames()) {
767 if (fieldName.equals("immutableInfo")) {
768 // Replace immutableInfo as the underlying MBean/MXBean
769 // could already implement NotificationBroadcaster and
770 // return immutableInfo=true in its MBeanInfo.
771 fields.put(fieldName, Boolean.toString(immutableInfo));
772 } else {
773 fields.put(fieldName, d.getFieldValue(fieldName));
774 }
775 }
776 desc = new ImmutableDescriptor(fields);
777 }
778 return desc;
779 }
780
781 /**
782 * Customization hook:
783 * Return the MBeanInfo cached for this object.
784 *
785 * <p>Subclasses may redefine this method in order to implement their
786 * own caching policy. The default implementation stores one
787 * {@link MBeanInfo} object per instance.
788 *
789 * @return The cached MBeanInfo, or null if no MBeanInfo is cached.
790 *
791 * @see #cacheMBeanInfo(MBeanInfo)
792 **/
793 protected MBeanInfo getCachedMBeanInfo() {
794 return cachedMBeanInfo;
795 }
796
797 /**
798 * Customization hook:
799 * cache the MBeanInfo built for this object.
800 *
801 * <p>Subclasses may redefine this method in order to implement
802 * their own caching policy. The default implementation stores
803 * <code>info</code> in this instance. A subclass can define
804 * other policies, such as not saving <code>info</code> (so it is
805 * reconstructed every time {@link #getMBeanInfo()} is called) or
806 * sharing a unique {@link MBeanInfo} object when several
807 * <code>StandardMBean</code> instances have equal {@link
808 * MBeanInfo} values.
809 *
810 * @param info the new <code>MBeanInfo</code> to cache. Any
811 * previously cached value is discarded. This parameter may be
812 * null, in which case there is no new cached value.
813 **/
814 protected void cacheMBeanInfo(MBeanInfo info) {
815 cachedMBeanInfo = info;
816 }
817
818 private boolean isMXBean() {
819 return mbean.isMXBean();
820 }
821
822 private static <T> boolean identicalArrays(T[] a, T[] b) {
823 if (a == b)
824 return true;
825 if (a == null || b == null || a.length != b.length)
826 return false;
827 for (int i = 0; i < a.length; i++) {
828 if (a[i] != b[i])
829 return false;
830 }
831 return true;
832 }
833
834 private static <T> boolean equal(T a, T b) {
835 if (a == b)
836 return true;
837 if (a == null || b == null)
838 return false;
839 return a.equals(b);
840 }
841
842 private static MBeanParameterInfo
843 customize(MBeanParameterInfo pi,
844 String name,
845 String description) {
846 if (equal(name, pi.getName()) &&
847 equal(description, pi.getDescription()))
848 return pi;
849 else if (pi instanceof OpenMBeanParameterInfo) {
850 OpenMBeanParameterInfo opi = (OpenMBeanParameterInfo) pi;
851 return new OpenMBeanParameterInfoSupport(name,
852 description,
853 opi.getOpenType(),
854 pi.getDescriptor());
855 } else {
856 return new MBeanParameterInfo(name,
857 pi.getType(),
858 description,
859 pi.getDescriptor());
860 }
861 }
862
863 private static MBeanConstructorInfo
864 customize(MBeanConstructorInfo ci,
865 String description,
866 MBeanParameterInfo[] signature) {
867 if (equal(description, ci.getDescription()) &&
868 identicalArrays(signature, ci.getSignature()))
869 return ci;
870 if (ci instanceof OpenMBeanConstructorInfo) {
871 OpenMBeanParameterInfo[] oparams =
872 paramsToOpenParams(signature);
873 return new OpenMBeanConstructorInfoSupport(ci.getName(),
874 description,
875 oparams,
876 ci.getDescriptor());
877 } else {
878 return new MBeanConstructorInfo(ci.getName(),
879 description,
880 signature,
881 ci.getDescriptor());
882 }
883 }
884
885 private static MBeanOperationInfo
886 customize(MBeanOperationInfo oi,
887 String description,
888 MBeanParameterInfo[] signature,
889 int impact) {
890 if (equal(description, oi.getDescription()) &&
891 identicalArrays(signature, oi.getSignature()) &&
892 impact == oi.getImpact())
893 return oi;
894 if (oi instanceof OpenMBeanOperationInfo) {
895 OpenMBeanOperationInfo ooi = (OpenMBeanOperationInfo) oi;
896 OpenMBeanParameterInfo[] oparams =
897 paramsToOpenParams(signature);
898 return new OpenMBeanOperationInfoSupport(oi.getName(),
899 description,
900 oparams,
901 ooi.getReturnOpenType(),
902 impact,
903 oi.getDescriptor());
904 } else {
905 return new MBeanOperationInfo(oi.getName(),
906 description,
907 signature,
908 oi.getReturnType(),
909 impact,
910 oi.getDescriptor());
911 }
912 }
913
914 private static MBeanAttributeInfo
915 customize(MBeanAttributeInfo ai,
916 String description) {
917 if (equal(description, ai.getDescription()))
918 return ai;
919 if (ai instanceof OpenMBeanAttributeInfo) {
920 OpenMBeanAttributeInfo oai = (OpenMBeanAttributeInfo) ai;
921 return new OpenMBeanAttributeInfoSupport(ai.getName(),
922 description,
923 oai.getOpenType(),
924 ai.isReadable(),
925 ai.isWritable(),
926 ai.isIs(),
927 ai.getDescriptor());
928 } else {
929 return new MBeanAttributeInfo(ai.getName(),
930 ai.getType(),
931 description,
932 ai.isReadable(),
933 ai.isWritable(),
934 ai.isIs(),
935 ai.getDescriptor());
936 }
937 }
938
939 private static OpenMBeanParameterInfo[]
940 paramsToOpenParams(MBeanParameterInfo[] params) {
941 if (params instanceof OpenMBeanParameterInfo[])
942 return (OpenMBeanParameterInfo[]) params;
943 OpenMBeanParameterInfo[] oparams =
944 new OpenMBeanParameterInfoSupport[params.length];
945 System.arraycopy(params, 0, oparams, 0, params.length);
946 return oparams;
947 }
948
949 // ------------------------------------------------------------------
950 // Build the custom MBeanConstructorInfo[]
951 // ------------------------------------------------------------------
952 private MBeanConstructorInfo[]
953 getConstructors(MBeanInfo info, Object impl) {
954 final MBeanConstructorInfo[] ctors =
955 getConstructors(info.getConstructors(), impl);
956 if (ctors == null)
957 return null;
958 final int ctorlen = ctors.length;
959 final MBeanConstructorInfo[] nctors = new MBeanConstructorInfo[ctorlen];
960 for (int i=0; i<ctorlen; i++) {
961 final MBeanConstructorInfo c = ctors[i];
962 final MBeanParameterInfo[] params = c.getSignature();
963 final MBeanParameterInfo[] nps;
964 if (params != null) {
965 final int plen = params.length;
966 nps = new MBeanParameterInfo[plen];
967 for (int ii=0;ii<plen;ii++) {
968 MBeanParameterInfo p = params[ii];
969 nps[ii] = customize(p,
970 getParameterName(c,p,ii),
971 getDescription(c,p,ii));
972 }
973 } else {
974 nps = null;
975 }
976 nctors[i] =
977 customize(c, getDescription(c), nps);
978 }
979 return nctors;
980 }
981
982 // ------------------------------------------------------------------
983 // Build the custom MBeanOperationInfo[]
984 // ------------------------------------------------------------------
985 private MBeanOperationInfo[] getOperations(MBeanInfo info) {
986 final MBeanOperationInfo[] ops = info.getOperations();
987 if (ops == null)
988 return null;
989 final int oplen = ops.length;
990 final MBeanOperationInfo[] nops = new MBeanOperationInfo[oplen];
991 for (int i=0; i<oplen; i++) {
992 final MBeanOperationInfo o = ops[i];
993 final MBeanParameterInfo[] params = o.getSignature();
994 final MBeanParameterInfo[] nps;
995 if (params != null) {
996 final int plen = params.length;
997 nps = new MBeanParameterInfo[plen];
998 for (int ii=0;ii<plen;ii++) {
999 MBeanParameterInfo p = params[ii];
1000 nps[ii] = customize(p,
1001 getParameterName(o,p,ii),
1002 getDescription(o,p,ii));
1003 }
1004 } else {
1005 nps = null;
1006 }
1007 nops[i] = customize(o, getDescription(o), nps, getImpact(o));
1008 }
1009 return nops;
1010 }
1011
1012 // ------------------------------------------------------------------
1013 // Build the custom MBeanAttributeInfo[]
1014 // ------------------------------------------------------------------
1015 private MBeanAttributeInfo[] getAttributes(MBeanInfo info) {
1016 final MBeanAttributeInfo[] atts = info.getAttributes();
1017 if (atts == null)
1018 return null; // should not happen
1019 final MBeanAttributeInfo[] natts;
1020 final int attlen = atts.length;
1021 natts = new MBeanAttributeInfo[attlen];
1022 for (int i=0; i<attlen; i++) {
1023 final MBeanAttributeInfo a = atts[i];
1024 natts[i] = customize(a, getDescription(a));
1025 }
1026 return natts;
1027 }
1028
1029 /**
1030 * <p>Allows the MBean to perform any operations it needs before
1031 * being registered in the MBean server. If the name of the MBean
1032 * is not specified, the MBean can provide a name for its
1033 * registration. If any exception is raised, the MBean will not be
1034 * registered in the MBean server.</p>
1035 *
1036 * <p>The default implementation of this method returns the {@code name}
1037 * parameter. It does nothing else for
1038 * Standard MBeans. For MXBeans, it records the {@code MBeanServer}
1039 * and {@code ObjectName} parameters so they can be used to translate
1040 * inter-MXBean references.</p>
1041 *
1042 * <p>It is good practice for a subclass that overrides this method
1043 * to call the overridden method via {@code super.preRegister(...)}.
1044 * This is necessary if this object is an MXBean that is referenced
1045 * by attributes or operations in other MXBeans.</p>
1046 *
1047 * @param server The MBean server in which the MBean will be registered.
1048 *
1049 * @param name The object name of the MBean. This name is null if
1050 * the name parameter to one of the <code>createMBean</code> or
1051 * <code>registerMBean</code> methods in the {@link MBeanServer}
1052 * interface is null. In that case, this method must return a
1053 * non-null ObjectName for the new MBean.
1054 *
1055 * @return The name under which the MBean is to be registered.
1056 * This value must not be null. If the <code>name</code>
1057 * parameter is not null, it will usually but not necessarily be
1058 * the returned value.
1059 *
1060 * @throws IllegalArgumentException if this is an MXBean and
1061 * {@code name} is null.
1062 *
1063 * @throws InstanceAlreadyExistsException if this is an MXBean and
1064 * it has already been registered under another name (in this
1065 * MBean Server or another).
1066 *
1067 * @throws Exception no other checked exceptions are thrown by
1068 * this method but {@code Exception} is declared so that subclasses
1069 * can override the method and throw their own exceptions.
1070 *
1071 * @since 1.6
1072 */
1073 public ObjectName preRegister(MBeanServer server, ObjectName name)
1074 throws Exception {
1075 mbean.register(server, name);
1076 return name;
1077 }
1078
1079 /**
1080 * <p>Allows the MBean to perform any operations needed after having been
1081 * registered in the MBean server or after the registration has failed.</p>
1082 *
1083 * <p>The default implementation of this method does nothing for
1084 * Standard MBeans. For MXBeans, it undoes any work done by
1085 * {@link #preRegister preRegister} if registration fails.</p>
1086 *
1087 * <p>It is good practice for a subclass that overrides this method
1088 * to call the overridden method via {@code super.postRegister(...)}.
1089 * This is necessary if this object is an MXBean that is referenced
1090 * by attributes or operations in other MXBeans.</p>
1091 *
1092 * @param registrationDone Indicates whether or not the MBean has
1093 * been successfully registered in the MBean server. The value
1094 * false means that the registration phase has failed.
1095 *
1096 * @since 1.6
1097 */
1098 public void postRegister(Boolean registrationDone) {
1099 if (!registrationDone)
1100 mbean.unregister();
1101 }
1102
1103 /**
1104 * <p>Allows the MBean to perform any operations it needs before
1105 * being unregistered by the MBean server.</p>
1106 *
1107 * <p>The default implementation of this method does nothing.</p>
1108 *
1109 * <p>It is good practice for a subclass that overrides this method
1110 * to call the overridden method via {@code super.preDeegister(...)}.</p>
1111 *
1112 * @throws Exception no checked exceptions are throw by this method
1113 * but {@code Exception} is declared so that subclasses can override
1114 * this method and throw their own exceptions.
1115 *
1116 * @since 1.6
1117 */
1118 public void preDeregister() throws Exception {
1119 }
1120
1121 /**
1122 * <p>Allows the MBean to perform any operations needed after having been
1123 * unregistered in the MBean server.</p>
1124 *
1125 * <p>The default implementation of this method does nothing for
1126 * Standard MBeans. For MXBeans, it removes any information that
1127 * was recorded by the {@link #preRegister preRegister} method.</p>
1128 *
1129 * <p>It is good practice for a subclass that overrides this method
1130 * to call the overridden method via {@code super.postRegister(...)}.
1131 * This is necessary if this object is an MXBean that is referenced
1132 * by attributes or operations in other MXBeans.</p>
1133 *
1134 * @since 1.6
1135 */
1136 public void postDeregister() {
1137 mbean.unregister();
1138 }
1139
1140 //
1141 // MBeanInfo immutability
1142 //
1143
1144 /**
1145 * Cached results of previous calls to immutableInfo. This is
1146 * a WeakHashMap so that we don't prevent a class from being
1147 * garbage collected just because we know whether its MBeanInfo
1148 * is immutable.
1149 */
1150 private static final Map<Class, Boolean> mbeanInfoSafeMap =
1151 new WeakHashMap<Class, Boolean>();
1152
1153 /**
1154 * Return true if {@code subclass} is known to preserve the immutability
1155 * of the {@code MBeanInfo}. The {@code subclass} is considered to have
1156 * an immutable {@code MBeanInfo} if it does not override any of the
1157 * getMBeanInfo, getCachedMBeanInfo, cacheMBeanInfo and getNotificationInfo
1158 * methods.
1159 */
1160 static boolean immutableInfo(Class<? extends StandardMBean> subclass) {
1161 if (subclass == StandardMBean.class ||
1162 subclass == StandardEmitterMBean.class)
1163 return true;
1164 synchronized (mbeanInfoSafeMap) {
1165 Boolean safe = mbeanInfoSafeMap.get(subclass);
1166 if (safe == null) {
1167 try {
1168 MBeanInfoSafeAction action =
1169 new MBeanInfoSafeAction(subclass);
1170 safe = AccessController.doPrivileged(action);
1171 } catch (Exception e) { // e.g. SecurityException
1172 /* We don't know, so we assume it isn't. */
1173 safe = false;
1174 }
1175 mbeanInfoSafeMap.put(subclass, safe);
1176 }
1177 return safe;
1178 }
1179 }
1180
1181 static boolean overrides(Class<?> subclass, Class<?> superclass,
1182 String name, Class<?>... params) {
1183 for (Class<?> c = subclass; c != superclass; c = c.getSuperclass()) {
1184 try {
1185 c.getDeclaredMethod(name, params);
1186 return true;
1187 } catch (NoSuchMethodException e) {
1188 // OK: this class doesn't override it
1189 }
1190 }
1191 return false;
1192 }
1193
1194 private static class MBeanInfoSafeAction
1195 implements PrivilegedAction<Boolean> {
1196
1197 private final Class subclass;
1198
1199 MBeanInfoSafeAction(Class subclass) {
1200 this.subclass = subclass;
1201 }
1202
1203 public Boolean run() {
1204 // Check for "void cacheMBeanInfo(MBeanInfo)" method.
1205 //
1206 if (overrides(subclass, StandardMBean.class,
1207 "cacheMBeanInfo", MBeanInfo.class))
1208 return false;
1209
1210 // Check for "MBeanInfo getCachedMBeanInfo()" method.
1211 //
1212 if (overrides(subclass, StandardMBean.class,
1213 "getCachedMBeanInfo", (Class[]) null))
1214 return false;
1215
1216 // Check for "MBeanInfo getMBeanInfo()" method.
1217 //
1218 if (overrides(subclass, StandardMBean.class,
1219 "getMBeanInfo", (Class[]) null))
1220 return false;
1221
1222 // Check for "MBeanNotificationInfo[] getNotificationInfo()"
1223 // method.
1224 //
1225 // This method is only taken into account for the MBeanInfo
1226 // immutability checks if and only if the given subclass is
1227 // StandardEmitterMBean itself or can be assigned to
1228 // StandardEmitterMBean.
1229 //
1230 if (StandardEmitterMBean.class.isAssignableFrom(subclass))
1231 if (overrides(subclass, StandardEmitterMBean.class,
1232 "getNotificationInfo", (Class[]) null))
1233 return false;
1234 return true;
1235 }
1236 }
1237
1238}