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