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.classfalse);
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.classfalse);
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, falsefalse);
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, truefalse);
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 != nullreturn 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 == nullreturn 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 == nullreturn 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 == nullreturn 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 == nullreturn 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 == nullreturn 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 == nullreturn 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 == nullreturn 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 == nullreturn 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 defaultthis 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 == nullreturn null;
711             if (impl != null && impl != thisreturn 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 casethis 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