View Javadoc
1   /*
2    * Copyright (c) 2002, 2008, 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.util.logging.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.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;
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.preDeregister(...)}.</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 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 }