1 /* StandardMBean.java -- A standard reflection-based management bean.
2    Copyright (C) 2006 Free Software Foundation, Inc.
3 
4 This file is part of GNU Classpath.
5 
6 GNU Classpath is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2, or (at your option)
9 any later version.
10 
11 GNU Classpath is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 General Public License for more details.
15 
16 You should have received a copy of the GNU General Public License
17 along with GNU Classpath; see the file COPYING.  If not, write to the
18 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19 02110-1301 USA.
20 
21 Linking this library statically or dynamically with other modules is
22 making a combined work based on this library.  Thus, the terms and
23 conditions of the GNU General Public License cover the whole
24 combination.
25 
26 As a special exception, the copyright holders of this library give you
27 permission to link this library with independent modules to produce an
28 executable, regardless of the license terms of these independent
29 modules, and to copy and distribute the resulting executable under
30 terms of your choice, provided that you also meet, for each linked
31 independent module, the terms and conditions of the license of that
32 module.  An independent module is a module which is not derived from
33 or based on this library.  If you modify this library, you may extend
34 this exception to your version of the library, but you are not
35 obligated to do so.  If you do not wish to do so, delete this
36 exception statement from your version. */
37 
38 package javax.management;
39 
40 import java.lang.reflect.Constructor;
41 import java.lang.reflect.InvocationTargetException;
42 import java.lang.reflect.Method;
43 
44 import java.util.ArrayList;
45 import java.util.HashMap;
46 import java.util.Iterator;
47 import java.util.List;
48 import java.util.Map;
49 
50 /**
51  * Provides a dynamic management bean by using reflection on an
52  * interface and an implementing class.  By default, a bean instance
53  * is paired up with its interface based on specific naming
54  * conventions (if the implementation is called X, the interface must
55  * be XMBean).  Using this class removes the need to use a specific
56  * naming system to match up the two.  Instead, an instance of this
57  * bean is created either via explicit construction or subclassing,
58  * and this provides access to the attributes, constructors and
59  * operations of the implementation via reflection.  Various hooks are
60  * provided in order to allow customization of this process.
61  *
62  * @author Andrew John Hughes (gnu_andrew@member.fsf.org)
63  * @since 1.5
64  */
65 public class StandardMBean
66   implements DynamicMBean
67 {
68 
69   /**
70    * The interface for this bean.
71    */
72   private Class<?> iface;
73 
74   /**
75    * The implementation of the interface.
76    */
77   private Object impl;
78 
79   /**
80    * Cached bean information.
81    */
82   private MBeanInfo info;
83 
84   /**
85    * Constructs a new {@link StandardMBean} using the specified
86    * interface and <code>this</code> as the instance.  This should
87    * be used to create an instance via subclassing.
88    *
89    * @param iface the interface this bean implements, or <code>null</code>
90    *              if the interface should be determined using the naming
91    *              convention (class X has interface XMBean).
92    * @throws NotCompliantMBeanException if this class doesn't implement
93    *                                    the interface or a method appears
94    *                                    in the interface that doesn't comply
95    *                                    with the naming conventions.
96    */
StandardMBean(Class<?> iface)97   protected StandardMBean(Class<?> iface)
98     throws NotCompliantMBeanException
99   {
100     if (iface == null)
101       {
102         String className = getClass().getName();
103         try
104           {
105             iface = Class.forName(className + "MBean");
106           }
107         catch (ClassNotFoundException e)
108           {
109             for (Class<?> nextIface : getClass().getInterfaces())
110             {
111               if (JMX.isMXBeanInterface(nextIface))
112                 {
113                   iface = nextIface;
114                   break;
115                 }
116             }
117             if (iface == null)
118               throw (NotCompliantMBeanException)
119                 (new NotCompliantMBeanException("An interface for the class "
120                                                 + className +
121                                                 " was not found.").initCause(e));
122           }
123       }
124     if (!(iface.isInstance(this)))
125       throw new NotCompliantMBeanException("The instance, " + impl +
126                                            ", is not an instance of " + iface);
127     impl = this;
128     this.iface = iface;
129   }
130 
131   /**
132    * Constructs a new {@link StandardMBean} using the specified
133    * interface and the supplied instance as the implementation.
134    *
135    * @param impl the implementation.
136    * @param iface the interface the bean implements, or <code>null</code>
137    *              if the interface should be determined using the naming
138    *              convention (class X has interface XMBean).
139    * @throws IllegalArgumentException if <code>impl</code> is <code>null</code>.
140    * @throws NotCompliantMBeanException if <code>impl</code> doesn't implement
141    *                                    the interface or a method appears
142    *                                    in the interface that doesn't comply
143    *                                    with the naming conventions.
144    */
StandardMBean(T impl, Class<T> iface)145   public <T> StandardMBean(T impl, Class<T> iface)
146     throws NotCompliantMBeanException
147   {
148     if (impl == null)
149       throw new IllegalArgumentException("The specified implementation is null.");
150     if (iface == null)
151       {
152         Class<?> implClass = impl.getClass();
153         String className = implClass.getName();
154         try
155           {
156             this.iface = Class.forName(className + "MBean", true,
157                                        implClass.getClassLoader());
158           }
159         catch (ClassNotFoundException e)
160           {
161             for (Class<?> nextIface : implClass.getInterfaces())
162             {
163               if (JMX.isMXBeanInterface(nextIface))
164                 {
165                   this.iface = nextIface;
166                   break;
167                 }
168             }
169             if (this.iface == null)
170               throw (NotCompliantMBeanException)
171                 (new NotCompliantMBeanException("An interface for the class " +
172                                                 className +
173                                                 " was not found.").initCause(e));
174           }
175       }
176     else
177       this.iface = iface;
178     if (!(this.iface.isInstance(impl)))
179       throw new NotCompliantMBeanException("The instance, " + impl +
180                                            ", is not an instance of " + iface);
181     this.impl = impl;
182   }
183 
184   /**
185    * Caches the {@link MBeanInfo} instance for this object.  This is a
186    * customization hook, so that subclasses can choose the caching policy
187    * used.  The default implementation caches the value in the instance
188    * itself.  Subclasses may override this so as to not cache the data
189    * at all, or so as to use a cache shared between multiple beans.
190    *
191    * @param info the {@link MBeanInfo} instance to cache, or <code>null</code>
192    *             if there is no new value to cache.  When the value is not
193    *             <code>null</code>, the cache should replace the current value
194    *             with the value supplied here.
195    * @see #getCachedMBeanInfo()
196    */
cacheMBeanInfo(MBeanInfo info)197   protected void cacheMBeanInfo(MBeanInfo info)
198   {
199     if (info != null)
200       this.info = info;
201   }
202 
203   /**
204    * Obtains the value of the specified attribute of the
205    * management bean.  The management bean should perform
206    * a lookup for the named attribute, and return its value
207    * by calling the appropriate getter method, if possible.
208    *
209    * @param name the name of the attribute to retrieve.
210    * @return the value of the specified attribute.
211    * @throws AttributeNotFoundException if the name does not
212    *                                    correspond to an attribute
213    *                                    of the bean.
214    * @throws MBeanException if retrieving the attribute causes
215    *                        the bean to throw an exception (which
216    *                        becomes the cause of this exception).
217    * @throws ReflectionException if an exception occurred in trying
218    *                             to use the reflection interface
219    *                             to lookup the attribute.  The
220    *                             thrown exception is the cause of
221    *                             this exception.
222    * @see #setAttribute(String)
223    */
getAttribute(String name)224   public Object getAttribute(String name)
225     throws AttributeNotFoundException, MBeanException,
226            ReflectionException
227   {
228     Method getter;
229     try
230       {
231         getter = iface.getMethod("get" + name);
232       }
233     catch (NoSuchMethodException e)
234       {
235         try
236           {
237             getter = iface.getMethod("is" + name);
238           }
239         catch (NoSuchMethodException ex)
240           {
241             throw ((AttributeNotFoundException)
242                    new AttributeNotFoundException("The attribute, " + name +
243                                                   ", was not found.").initCause(ex));
244           }
245       }
246     Object result;
247     try
248       {
249         result = getter.invoke(impl);
250       }
251     catch (IllegalAccessException e)
252       {
253         throw new ReflectionException(e, "Failed to retrieve " + name);
254       }
255     catch (IllegalArgumentException e)
256       {
257         throw new ReflectionException(e, "Failed to retrieve " + name);
258       }
259     catch (InvocationTargetException e)
260       {
261         throw new MBeanException((Exception) e.getCause(),
262                                  "The getter of " + name +
263                                  " threw an exception");
264       }
265     return result;
266   }
267 
268   /**
269    * Obtains the values of each of the specified attributes
270    * of the management bean.  The returned list includes
271    * those attributes that were retrieved and their
272    * corresponding values.
273    *
274    * @param names the names of the attributes to retrieve.
275    * @return a list of the retrieved attributes.
276    * @see #setAttributes(AttributeList)
277    */
getAttributes(String[] names)278   public AttributeList getAttributes(String[] names)
279   {
280     AttributeList list = new AttributeList(names.length);
281     for (int a = 0; a < names.length; ++a)
282       {
283         try
284           {
285             Object value = getAttribute(names[a]);
286             list.add(new Attribute(names[a], value));
287           }
288         catch (AttributeNotFoundException e)
289           {
290             /* Ignored */
291           }
292         catch (ReflectionException e)
293           {
294             /* Ignored */
295           }
296         catch (MBeanException e)
297           {
298             /* Ignored */
299           }
300       }
301     return list;
302   }
303 
304   /**
305    * Returns the cached {@link MBeanInfo} instance for this object.  This is a
306    * customization hook, so that subclasses can choose the caching policy
307    * used.  The default implementation caches the value in the instance
308    * itself, and returns this value on calls to this method.
309    *
310    * @return the cached {@link MBeanInfo} instance, or <code>null</code>
311    *         if no value is cached.
312    * @see #cacheMBeanInfo(javax.management.MBeanInfo)
313    */
getCachedMBeanInfo()314   protected MBeanInfo getCachedMBeanInfo()
315   {
316     return info;
317   }
318 
319   /**
320    * Returns the class name that will be used in the {@link MBeanInfo}
321    * instance.  This is a customization hook, so that subclasses can
322    * provide a custom class name.  By default, this returns the class
323    * name from the supplied {@link MBeanInfo} instance.
324    *
325    * @param info the {@link MBeanInfo} instance constructed via
326    *             reflection.
327    * @return the class name to use in the instance.
328    */
getClassName(MBeanInfo info)329   protected String getClassName(MBeanInfo info)
330   {
331     return info.getClassName();
332   }
333 
334   /**
335    * Returns information on the constructors that will be used in
336    * the {@link MBeanInfo} instance.  This is a customization hook,
337    * so that subclasses can provide their own information on the
338    * bean's constructors, if necessary.  By default, this method
339    * returns <code>null</code> unless the implementation supplied
340    * is either <code>null</code> or <code>this</code>.  This default
341    * implementation prevents the use of
342    * {@link MBeanServer#createMBean} in cases where the bean is
343    * not created as a subclass of {@link StandardMBean}.
344    *
345    * @param constructors the constructor information created via
346    *                     reflection.
347    * @param impl the implementation, or <code>null</code> if this
348    *             should be ignored.
349    * @return the constructor information to use.
350    */
getConstructors(MBeanConstructorInfo[] constructors, Object impl)351   protected MBeanConstructorInfo[] getConstructors(MBeanConstructorInfo[]
352                                                    constructors, Object impl)
353   {
354     if (impl == null || impl == this)
355       return constructors;
356     return null;
357   }
358 
359   /**
360    * Returns the description of the attribute that will be used in
361    * the supplied {@link MBeanAttributeInfo} instance.  This is a
362    * customization hook, so that subclasses can provide a custom
363    * description.  By default, this calls
364    * {@link #getDescription(MBeanFeatureInfo)} with the supplied
365    * {@link MBeanAttributeInfo} instance.
366    *
367    * @param info the {@link MBeanAttributeInfo} instance constructed
368    *             via reflection.
369    * @return the description to use in the instance.
370    */
getDescription(MBeanAttributeInfo info)371   protected String getDescription(MBeanAttributeInfo info)
372   {
373     return getDescription((MBeanFeatureInfo) info);
374   }
375 
376   /**
377    * Returns the description of the constructor that will be used in
378    * the supplied {@link MBeanConstructorInfo} instance.  This is a
379    * customization hook, so that subclasses can provide a custom
380    * description.  By default, this calls
381    * {@link #getDescription(MBeanFeatureInfo)} with the supplied
382    * {@link MBeanConstructorInfo} instance.
383    *
384    * @param info the {@link MBeanConstructorInfo} instance constructed
385    *             via reflection.
386    * @return the description to use in the instance.
387    */
getDescription(MBeanConstructorInfo info)388   protected String getDescription(MBeanConstructorInfo info)
389   {
390     return getDescription((MBeanFeatureInfo) info);
391   }
392 
393   /**
394    * Returns the description of the nth parameter of the constructor
395    * that will be used in the supplied {@link MBeanParameterInfo}
396    * instance.  This is a customization hook, so that subclasses
397    * can provide a custom description.  By default, this calls
398    * <code>param.getDescription()</code>.
399    *
400    * @param info the {@link MBeanConstructorInfo} instance constructed
401    *             via reflection.
402    * @param param the {@link MBeanParameterInfo} instance constructed
403    *             via reflection.
404    * @param n the number of the parameter, in order to link it to the
405    *          information on the constructor.
406    * @return the description to use in the instance.
407    */
getDescription(MBeanConstructorInfo info, MBeanParameterInfo param, int n)408   protected String getDescription(MBeanConstructorInfo info,
409                                   MBeanParameterInfo param, int n)
410   {
411     return param.getDescription();
412   }
413 
414   /**
415    * Returns the description of the supplied feature that
416    * will be used in the supplied {@link MBeanFeatureInfo}
417    * instance.  This is a customization hook, so that subclasses
418    * can provide a custom description.  By default, this calls
419    * <code>info.getDescription()</code>.  This method is also called
420    * by default for the more specific description methods for attributes,
421    * constructors and operations.
422    *
423    * @param info the {@link MBeanFeatureInfo} instance constructed
424    *             via reflection.
425    * @return the description to use in the instance.
426    */
getDescription(MBeanFeatureInfo info)427   protected String getDescription(MBeanFeatureInfo info)
428   {
429     return info.getDescription();
430   }
431 
432   /**
433    * Returns the description of the bean that will be used in the
434    * supplied {@link MBeanInfo} instance.  This is a customization
435    * hook, so that subclasses can provide a custom description.  By
436    * default, this calls <code>info.getDescription()</code>.
437    *
438    * @param info the {@link MBeanInfo} instance constructed
439    *             via reflection.
440    * @return the description to use in the instance.
441    */
getDescription(MBeanInfo info)442   protected String getDescription(MBeanInfo info)
443   {
444     return info.getDescription();
445   }
446 
447   /**
448    * Returns the description of the operation that will be used in
449    * the supplied {@link MBeanOperationInfo} instance.  This is a
450    * customization hook, so that subclasses can provide a custom
451    * description.  By default, this calls
452    * {@link #getDescription(MBeanFeatureInfo)} with the supplied
453    * {@link MBeanOperationInfo} instance.
454    *
455    * @param info the {@link MBeanOperationInfo} instance constructed
456    *             via reflection.
457    * @return the description to use in the instance.
458    */
getDescription(MBeanOperationInfo info)459   protected String getDescription(MBeanOperationInfo info)
460   {
461     return getDescription((MBeanFeatureInfo) info);
462   }
463 
464   /**
465    * Returns the description of the nth parameter of the operation
466    * that will be used in the supplied {@link MBeanParameterInfo}
467    * instance.  This is a customization hook, so that subclasses
468    * can provide a custom description.  By default, this calls
469    * <code>param.getDescription()</code>.
470    *
471    * @param info the {@link MBeanOperationInfo} instance constructed
472    *             via reflection.
473    * @param param the {@link MBeanParameterInfo} instance constructed
474    *             via reflection.
475    * @param n the number of the parameter, in order to link it to the
476    *          information on the operation.
477    * @return the description to use in the instance.
478    */
getDescription(MBeanOperationInfo info, MBeanParameterInfo param, int n)479   protected String getDescription(MBeanOperationInfo info,
480                                   MBeanParameterInfo param, int n)
481   {
482     return param.getDescription();
483   }
484 
485   /**
486    * Returns the impact of the operation that will be used in the
487    * supplied {@link MBeanOperationInfo} instance.  This is a
488    * customization hook, so that subclasses can provide a custom
489    * impact flag.  By default, this returns
490    * <code>info.getImpact()</code>.
491    *
492    * @param info the {@link MBeanOperationInfo} instance constructed
493    *             via reflection.
494    * @return the impact flag to use in the instance.
495    */
getImpact(MBeanOperationInfo info)496   protected int getImpact(MBeanOperationInfo info)
497   {
498     return info.getImpact();
499   }
500 
501   /**
502    * Returns the instance that implements this bean.
503    *
504    * @return the implementation.
505    */
getImplementation()506   public Object getImplementation()
507   {
508     return impl;
509   }
510 
511   /**
512    * Returns the class of the instance that implements this bean.
513    *
514    * @return the implementation class.
515    */
getImplementationClass()516   public Class<?> getImplementationClass()
517   {
518     return impl.getClass();
519   }
520 
521   /**
522    * <p>
523    * Returns an information object which lists the attributes
524    * and actions associated with the management bean.  This
525    * implementation proceeds as follows:
526    * </p>
527    * <ol>
528    * <li>{@link #getCachedMBeanInfo()} is called to obtain
529    * the cached instance.  If this returns a non-null value,
530    * this value is returned.</li>
531    * <li>If there is no cached value, then the method proceeds
532    * to create one. During this process, the customization hooks
533    * detailed in this class are called to allow the values used
534    * to be overrided:
535    * <ul>
536    * <li>For each attribute,
537    * {@link #getDescription(MBeanAttributeInfo)} is called.</li>
538    * <li>For each constructor,
539    * {@link #getDescription(MBeanConstructorInfo)} is called,
540    * along with {@link #getDescription(MBeanConstructorInfo,
541    * MBeanParameterInfo, int)} and
542    * {@link #getParameterName(MBeanConstructorInfo,
543    * MBeanParameterInfo, int)} for each parameter.</li>
544    * <li>The constructors may be replaced as a whole by
545    * a call to
546    * {@link #getConstructors(MBeanConstructorInfo[], Object)}.</li>
547    * <li>For each operation,
548    * {@link #getDescription(MBeanOperationInfo)} and
549    * {@link #getImpact(MBeanOperationInfo)} are called,
550    * along with {@link #getDescription(MBeanOperationInfo,
551    * MBeanParameterInfo, int)} and
552    * {@link #getParameterName(MBeanOperationInfo,
553    * MBeanParameterInfo, int)} for each parameter.</li>
554    * <li>{@link #getClassName(MBeanInfo)} and
555    * {@link #getDescription(MBeanInfo)} are called to customise
556    * the basic information about the class.</li>
557    * </ul>
558    * </li>
559    * <li>Finally, {@link #cacheMBeanInfo(MBeanInfo)} is called
560    * with the created instance before it is returned.</li>
561    * </ol>
562    *
563    * @return a description of the management bean, including
564    *         all exposed attributes and actions.
565    */
getMBeanInfo()566   public MBeanInfo getMBeanInfo()
567   {
568     MBeanInfo info = getCachedMBeanInfo();
569     if (info != null)
570       return info;
571     Method[] methods = iface.getMethods();
572     Map<String,Method[]> attributes = new HashMap<String,Method[]>();
573     List<MBeanOperationInfo> operations = new ArrayList<MBeanOperationInfo>();
574     for (int a = 0; a < methods.length; ++a)
575       {
576         String name = methods[a].getName();
577         if (((name.startsWith("get") &&
578               methods[a].getReturnType() != Void.TYPE) ||
579              (name.startsWith("is") &&
580               methods[a].getReturnType() == Boolean.TYPE)) &&
581             methods[a].getParameterTypes().length == 0)
582           {
583             Method[] amethods;
584             String attrib;
585             if (name.startsWith("is"))
586               attrib = name.substring(2);
587             else
588               attrib = name.substring(3);
589             if (attributes.containsKey(attrib))
590               amethods = (Method[]) attributes.get(attrib);
591             else
592               {
593                 amethods = new Method[2];
594                 attributes.put(attrib, amethods);
595               }
596             amethods[0] = methods[a];
597           }
598         else if (name.startsWith("set") &&
599                  methods[a].getReturnType() == Void.TYPE &&
600                  methods[a].getParameterTypes().length == 1)
601           {
602             Method[] amethods;
603             String attrib = name.substring(3);
604             if (attributes.containsKey(attrib))
605               amethods = (Method[]) attributes.get(attrib);
606             else
607               {
608                 amethods = new Method[2];
609                 attributes.put(attrib, amethods);
610               }
611             amethods[1] = methods[a];
612           }
613         else
614           operations.add(new MBeanOperationInfo(methods[a].getName(),
615                                                 methods[a]));
616       }
617     List<MBeanAttributeInfo> attribs = new ArrayList<MBeanAttributeInfo>(attributes.size());
618     for (Map.Entry<String,Method[]> entry : attributes.entrySet())
619       {
620         Method[] amethods = entry.getValue();
621         try
622           {
623             attribs.add(new MBeanAttributeInfo(entry.getKey(),
624                                                entry.getKey(),
625                                                amethods[0], amethods[1]));
626           }
627         catch (IntrospectionException e)
628           {
629             /* Shouldn't happen; both shouldn't be null */
630             throw new IllegalStateException("The two methods passed to " +
631                                             "the MBeanAttributeInfo " +
632                                             "constructor for " + entry +
633                                             "were null.", e);
634           }
635       }
636     MBeanAttributeInfo[] ainfo = new MBeanAttributeInfo[attribs.size()];
637     for (int a = 0; a < ainfo.length; ++a)
638       {
639         MBeanAttributeInfo oldInfo = (MBeanAttributeInfo) attribs.get(a);
640         String desc = getDescription(oldInfo);
641         ainfo[a] = new MBeanAttributeInfo(oldInfo.getName(),
642                                           oldInfo.getType(), desc,
643                                           oldInfo.isReadable(),
644                                           oldInfo.isWritable(),
645                                           oldInfo.isIs());
646       }
647     Constructor<?>[] cons = impl.getClass().getConstructors();
648     MBeanConstructorInfo[] cinfo = new MBeanConstructorInfo[cons.length];
649     for (int a = 0; a < cinfo.length; ++a)
650       {
651         MBeanConstructorInfo oldInfo = new MBeanConstructorInfo(cons[a].getName(),
652                                                                 cons[a]);
653         String desc = getDescription(oldInfo);
654         MBeanParameterInfo[] params = oldInfo.getSignature();
655         MBeanParameterInfo[] pinfo = new MBeanParameterInfo[params.length];
656         for (int b = 0; b < pinfo.length; ++b)
657           {
658             String pdesc = getDescription(oldInfo, params[b], b);
659             String pname = getParameterName(oldInfo, params[b], b);
660             pinfo[b] = new MBeanParameterInfo(pname, params[b].getType(),
661                                               pdesc);
662           }
663         cinfo[a] = new MBeanConstructorInfo(oldInfo.getName(), desc,
664                                             pinfo);
665       }
666     cinfo = getConstructors(cinfo, impl);
667     MBeanOperationInfo[] oinfo = new MBeanOperationInfo[operations.size()];
668     for (int a = 0; a < oinfo.length; ++a)
669       {
670         MBeanOperationInfo oldInfo = (MBeanOperationInfo) operations.get(a);
671         String desc = getDescription(oldInfo);
672         int impact = getImpact(oldInfo);
673         MBeanParameterInfo[] params = oldInfo.getSignature();
674         MBeanParameterInfo[] pinfo = new MBeanParameterInfo[params.length];
675         for (int b = 0; b < pinfo.length; ++b)
676           {
677             String pdesc = getDescription(oldInfo, params[b], b);
678             String pname = getParameterName(oldInfo, params[b], b);
679             pinfo[b] = new MBeanParameterInfo(pname, params[b].getType(),
680                                               pdesc);
681           }
682         oinfo[a] = new MBeanOperationInfo(oldInfo.getName(), desc, pinfo,
683                                           oldInfo.getReturnType(), impact);
684       }
685     info = new MBeanInfo(impl.getClass().getName(), impl.getClass().getName(),
686                          ainfo, cinfo, oinfo, null);
687     String cname = getClassName(info);
688     String desc = getDescription(info);
689     MBeanNotificationInfo[] ninfo = null;
690     if (impl instanceof NotificationBroadcaster)
691       ninfo = ((NotificationBroadcaster) impl).getNotificationInfo();
692     info = new MBeanInfo(cname, desc, ainfo, cinfo, oinfo, ninfo);
693     cacheMBeanInfo(info);
694     return info;
695   }
696 
697   /**
698    * Returns the interface for this management bean.
699    *
700    * @return the management interface.
701    */
getMBeanInterface()702   public final Class<?> getMBeanInterface()
703   {
704     return iface;
705   }
706 
707   /**
708    * Returns the name of the nth parameter of the constructor
709    * that will be used in the supplied {@link MBeanParameterInfo}
710    * instance.  This is a customization hook, so that subclasses
711    * can provide a custom name.  By default, this calls
712    * <code>param.getName()</code>.
713    *
714    * @param info the {@link MBeanConstructorInfo} instance constructed
715    *             via reflection.
716    * @param param the {@link MBeanParameterInfo} instance constructed
717    *             via reflection.
718    * @param n the number of the parameter, in order to link it to the
719    *          information on the constructor.
720    * @return the name to use in the instance.
721    */
getParameterName(MBeanConstructorInfo info, MBeanParameterInfo param, int n)722   protected String getParameterName(MBeanConstructorInfo info,
723                                     MBeanParameterInfo param, int n)
724   {
725     return param.getName();
726   }
727 
728   /**
729    * Returns the name of the nth parameter of the operation
730    * that will be used in the supplied {@link MBeanParameterInfo}
731    * instance.  This is a customization hook, so that subclasses
732    * can provide a custom name.  By default, this calls
733    * <code>param.getName()</code>.
734    *
735    * @param info the {@link MBeanOperationInfo} instance constructed
736    *             via reflection.
737    * @param param the {@link MBeanParameterInfo} instance constructed
738    *             via reflection.
739    * @param n the number of the parameter, in order to link it to the
740    *          information on the operation.
741    * @return the name to use in the instance.
742    */
getParameterName(MBeanOperationInfo info, MBeanParameterInfo param, int n)743   protected String getParameterName(MBeanOperationInfo info,
744                                     MBeanParameterInfo param, int n)
745   {
746     return param.getName();
747   }
748 
749   /**
750    * Invokes the specified action on the management bean using
751    * the supplied parameters.  The signature of the action is
752    * specified by a {@link String} array, which lists the classes
753    * corresponding to each parameter.  The class loader used to
754    * load these classes is the same as that used for loading the
755    * management bean itself.
756    *
757    * @param name the name of the action to invoke.
758    * @param params the parameters used to call the action.
759    * @param signature the signature of the action.
760    * @return the return value of the action.
761    * @throws MBeanException if the action throws an exception.  The
762    *                        thrown exception is the cause of this
763    *                        exception.
764    * @throws ReflectionException if an exception occurred in trying
765    *                             to use the reflection interface
766    *                             to invoke the action.  The
767    *                             thrown exception is the cause of
768    *                             this exception.
769    */
invoke(String name, Object[] params, String[] signature)770   public Object invoke(String name, Object[] params, String[] signature)
771     throws MBeanException, ReflectionException
772   {
773     if (name.startsWith("get") || name.startsWith("is") ||
774         name.startsWith("set"))
775       throw new ReflectionException(new NoSuchMethodException(),
776                                     "Invocation of an attribute " +
777                                     "method is disallowed.");
778     ClassLoader loader = getClass().getClassLoader();
779     Class<?>[] sigTypes;
780     if (signature != null)
781       {
782         sigTypes = new Class<?>[signature.length];
783         for (int a = 0; a < signature.length; ++a)
784           try
785             {
786               sigTypes[a] = Class.forName(signature[a], true, loader);
787             }
788           catch (ClassNotFoundException e)
789             {
790               throw new ReflectionException(e, "The class, " + signature[a] +
791                                             ", in the method signature " +
792                                             "could not be loaded.");
793             }
794       }
795     else
796       sigTypes = null;
797     Method method;
798     try
799       {
800         method = iface.getMethod(name, sigTypes);
801       }
802     catch (NoSuchMethodException e)
803       {
804         throw new ReflectionException(e, "The method, " + name +
805                                       ", could not be found.");
806       }
807     Object result;
808     try
809       {
810         result = method.invoke(impl, params);
811       }
812     catch (IllegalAccessException e)
813       {
814         throw new ReflectionException(e, "Failed to call " + name);
815       }
816     catch (IllegalArgumentException e)
817       {
818         throw new ReflectionException(e, "Failed to call " + name);
819       }
820     catch (InvocationTargetException e)
821       {
822         throw new MBeanException((Exception) e.getCause(), "The method "
823                                  + name + " threw an exception");
824       }
825     return result;
826   }
827 
828   /**
829    * Sets the value of the specified attribute of the
830    * management bean.  The management bean should perform
831    * a lookup for the named attribute, and sets its value
832    * using the associated setter method, if possible.
833    *
834    * @param attribute the attribute to set.
835    * @throws AttributeNotFoundException if the attribute does not
836    *                                    correspond to an attribute
837    *                                    of the bean.
838    * @throws InvalidAttributeValueException if the value is invalid
839    *                                        for this particular
840    *                                        attribute of the bean.
841    * @throws MBeanException if setting the attribute causes
842    *                        the bean to throw an exception (which
843    *                        becomes the cause of this exception).
844    * @throws ReflectionException if an exception occurred in trying
845    *                             to use the reflection interface
846    *                             to lookup the attribute.  The
847    *                             thrown exception is the cause of
848    *                             this exception.
849    * @see #getAttribute(String)
850    */
setAttribute(Attribute attribute)851   public void setAttribute(Attribute attribute)
852     throws AttributeNotFoundException, InvalidAttributeValueException,
853            MBeanException, ReflectionException
854   {
855     String name = attribute.getName();
856     String attName = name.substring(0, 1).toUpperCase() + name.substring(1);
857     Object val = attribute.getValue();
858     try
859       {
860         getMutator(attName, val.getClass()).invoke(impl, new Object[] { val });
861       }
862     catch (IllegalAccessException e)
863       {
864         throw new ReflectionException(e, "Failed to set " + name);
865       }
866     catch (IllegalArgumentException e)
867       {
868         throw ((InvalidAttributeValueException)
869                new InvalidAttributeValueException(attribute.getValue() +
870                                                   " is an invalid value for " +
871                                                   name).initCause(e));
872       }
873     catch (InvocationTargetException e)
874       {
875         throw new MBeanException(e, "The getter of " + name +
876                                  " threw an exception");
877       }
878   }
879 
880   /**
881    * Sets the value of each of the specified attributes
882    * to that supplied by the {@link Attribute} object.
883    * The returned list contains the attributes that were
884    * set and their new values.
885    *
886    * @param attributes the attributes to set.
887    * @return a list of the changed attributes.
888    * @see #getAttributes(AttributeList)
889    */
setAttributes(AttributeList attributes)890   public AttributeList setAttributes(AttributeList attributes)
891   {
892     AttributeList list = new AttributeList(attributes.size());
893     Iterator<Object> it = attributes.iterator();
894     while (it.hasNext())
895       {
896         try
897           {
898             Attribute attrib = (Attribute) it.next();
899             setAttribute(attrib);
900             list.add(attrib);
901           }
902         catch (AttributeNotFoundException e)
903           {
904             /* Ignored */
905           }
906         catch (InvalidAttributeValueException e)
907           {
908             /* Ignored */
909           }
910         catch (ReflectionException e)
911           {
912             /* Ignored */
913           }
914         catch (MBeanException e)
915           {
916             /* Ignored */
917           }
918       }
919     return list;
920   }
921 
922   /**
923    * Replaces the implementation of the interface used by this
924    * instance with the one specified.  The new implementation
925    * must be non-null and implement the interface specified on
926    * construction of this instance.
927    *
928    * @throws IllegalArgumentException if <code>impl</code> is <code>null</code>.
929    * @throws NotCompliantMBeanException if <code>impl</code> doesn't implement
930    *                                    the interface or a method appears
931    *                                    in the interface that doesn't comply
932    *                                    with the naming conventions.
933    */
setImplementation(Object impl)934   public void setImplementation(Object impl)
935     throws NotCompliantMBeanException
936   {
937     if (impl == null)
938       throw new IllegalArgumentException("The specified implementation is null.");
939     if (!(iface.isInstance(impl)))
940       throw new NotCompliantMBeanException("The instance, " + impl +
941                                            ", is not an instance of " + iface);
942     this.impl = impl;
943   }
944 
945   /**
946    * Returns the mutator method for a particular attribute name
947    * with a parameter type matching that of the given value.
948    *
949    * @param name the name of the attribute.
950    * @param type the type of the parameter.
951    * @return the appropriate mutator method.
952    * @throws AttributeNotFoundException if a method can't be found.
953    */
getMutator(String name, Class<?> type)954   private Method getMutator(String name, Class<?> type)
955     throws AttributeNotFoundException
956   {
957     String mutator = "set" + name;
958     Exception ex = null;
959     try
960       {
961         return iface.getMethod(mutator, type);
962       }
963     catch (NoSuchMethodException e)
964       {
965         /* Ignored; we'll try harder instead */
966         ex = e;
967       }
968     /* Special cases */
969     if (type == Boolean.class)
970       try
971         {
972           return iface.getMethod(mutator, Boolean.TYPE);
973         }
974       catch (NoSuchMethodException e)
975         {
976           throw ((AttributeNotFoundException)
977                  new AttributeNotFoundException("The attribute, " + name +
978                                                 ", was not found.").initCause(e));
979         }
980     if (type == Byte.class)
981       try
982         {
983           return iface.getMethod(mutator, Byte.TYPE);
984         }
985       catch (NoSuchMethodException e)
986         {
987           throw ((AttributeNotFoundException)
988                  new AttributeNotFoundException("The attribute, " + name +
989                                                 ", was not found.").initCause(e));
990         }
991     if (type == Character.class)
992       try
993         {
994           return iface.getMethod(mutator, Character.TYPE);
995         }
996       catch (NoSuchMethodException e)
997         {
998           throw ((AttributeNotFoundException)
999                  new AttributeNotFoundException("The attribute, " + name +
1000                                                 ", was not found.").initCause(e));
1001         }
1002     if (type == Double.class)
1003       try
1004         {
1005           return iface.getMethod(mutator, Double.TYPE);
1006         }
1007       catch (NoSuchMethodException e)
1008         {
1009           throw ((AttributeNotFoundException)
1010                  new AttributeNotFoundException("The attribute, " + name +
1011                                                 ", was not found.").initCause(e));
1012         }
1013     if (type == Float.class)
1014       try
1015         {
1016           return iface.getMethod(mutator, Float.TYPE);
1017         }
1018       catch (NoSuchMethodException e)
1019         {
1020           throw ((AttributeNotFoundException)
1021                  new AttributeNotFoundException("The attribute, " + name +
1022                                                 ", was not found.").initCause(e));
1023         }
1024     if (type == Integer.class)
1025       try
1026         {
1027           return iface.getMethod(mutator, Integer.TYPE);
1028         }
1029       catch (NoSuchMethodException e)
1030         {
1031           throw ((AttributeNotFoundException)
1032                  new AttributeNotFoundException("The attribute, " + name +
1033                                                 ", was not found.").initCause(e));
1034         }
1035     if (type == Long.class)
1036       try
1037         {
1038           return iface.getMethod(mutator, Long.TYPE);
1039         }
1040       catch (NoSuchMethodException e)
1041         {
1042           throw ((AttributeNotFoundException)
1043                  new AttributeNotFoundException("The attribute, " + name +
1044                                                 ", was not found.").initCause(e));
1045         }
1046     if (type == Short.class)
1047       try
1048         {
1049           return iface.getMethod(mutator, Short.TYPE);
1050         }
1051       catch (NoSuchMethodException e)
1052         {
1053           throw ((AttributeNotFoundException)
1054                  new AttributeNotFoundException("The attribute, " + name +
1055                                                 ", was not found.").initCause(e));
1056         }
1057     /* Superclasses and interfaces */
1058     for (Class<?> i : type.getInterfaces())
1059       try
1060         {
1061           return getMutator(name, i);
1062         }
1063       catch (AttributeNotFoundException e)
1064         {
1065           ex = e;
1066         }
1067     Class<?> sclass = type.getSuperclass();
1068     if (sclass != null && sclass != Object.class)
1069       try
1070         {
1071           return getMutator(name, sclass);
1072         }
1073       catch (AttributeNotFoundException e)
1074         {
1075           ex = e;
1076         }
1077     /* If we get this far, give up */
1078     throw ((AttributeNotFoundException)
1079            new AttributeNotFoundException("The attribute, " + name +
1080                                           ", was not found.").initCause(ex));
1081   }
1082 
1083 }
1084