001/* StandardMBean.java -- A standard reflection-based management bean.
002   Copyright (C) 2006 Free Software Foundation, Inc.
003
004This file is part of GNU Classpath.
005
006GNU Classpath is free software; you can redistribute it and/or modify
007it under the terms of the GNU General Public License as published by
008the Free Software Foundation; either version 2, or (at your option)
009any later version.
010
011GNU Classpath is distributed in the hope that it will be useful, but
012WITHOUT ANY WARRANTY; without even the implied warranty of
013MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
014General Public License for more details.
015
016You should have received a copy of the GNU General Public License
017along with GNU Classpath; see the file COPYING.  If not, write to the
018Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
01902110-1301 USA.
020
021Linking this library statically or dynamically with other modules is
022making a combined work based on this library.  Thus, the terms and
023conditions of the GNU General Public License cover the whole
024combination.
025
026As a special exception, the copyright holders of this library give you
027permission to link this library with independent modules to produce an
028executable, regardless of the license terms of these independent
029modules, and to copy and distribute the resulting executable under
030terms of your choice, provided that you also meet, for each linked
031independent module, the terms and conditions of the license of that
032module.  An independent module is a module which is not derived from
033or based on this library.  If you modify this library, you may extend
034this exception to your version of the library, but you are not
035obligated to do so.  If you do not wish to do so, delete this
036exception statement from your version. */
037
038package javax.management;
039
040import java.lang.reflect.Constructor;
041import java.lang.reflect.InvocationTargetException;
042import java.lang.reflect.Method;
043
044import java.util.ArrayList;
045import java.util.HashMap;
046import java.util.Iterator;
047import java.util.List;
048import java.util.Map;
049
050/**
051 * Provides a dynamic management bean by using reflection on an
052 * interface and an implementing class.  By default, a bean instance
053 * is paired up with its interface based on specific naming
054 * conventions (if the implementation is called X, the interface must
055 * be XMBean).  Using this class removes the need to use a specific
056 * naming system to match up the two.  Instead, an instance of this
057 * bean is created either via explicit construction or subclassing,
058 * and this provides access to the attributes, constructors and
059 * operations of the implementation via reflection.  Various hooks are
060 * provided in order to allow customization of this process.
061 *
062 * @author Andrew John Hughes (gnu_andrew@member.fsf.org)
063 * @since 1.5
064 */
065public class StandardMBean
066  implements DynamicMBean
067{
068
069  /**
070   * The interface for this bean.
071   */
072  private Class<?> iface;
073
074  /**
075   * The implementation of the interface.
076   */
077  private Object impl;
078
079  /**
080   * Cached bean information.
081   */
082  private MBeanInfo info;
083
084  /**
085   * Constructs a new {@link StandardMBean} using the specified
086   * interface and <code>this</code> as the instance.  This should
087   * be used to create an instance via subclassing.
088   *
089   * @param iface the interface this bean implements, or <code>null</code>
090   *              if the interface should be determined using the naming
091   *              convention (class X has interface XMBean).
092   * @throws NotCompliantMBeanException if this class doesn't implement
093   *                                    the interface or a method appears
094   *                                    in the interface that doesn't comply
095   *                                    with the naming conventions.
096   */
097  protected StandardMBean(Class<?> iface)
098    throws NotCompliantMBeanException
099  {
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   */
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   */
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   */
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   */
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   */
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   */
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   */
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   */
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   */
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   */
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   */
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   */
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   */
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   */
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   */
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   */
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   */
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   */
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   */
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   */
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   */
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   */
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   */
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   */
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   */
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   */
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}