001    /* StandardMBean.java -- A standard reflection-based management bean.
002       Copyright (C) 2006 Free Software Foundation, Inc.
003    
004    This file is part of GNU Classpath.
005    
006    GNU Classpath is free software; you can redistribute it and/or modify
007    it under the terms of the GNU General Public License as published by
008    the Free Software Foundation; either version 2, or (at your option)
009    any later version.
010    
011    GNU Classpath is distributed in the hope that it will be useful, but
012    WITHOUT ANY WARRANTY; without even the implied warranty of
013    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
014    General Public License for more details.
015    
016    You should have received a copy of the GNU General Public License
017    along with GNU Classpath; see the file COPYING.  If not, write to the
018    Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
019    02110-1301 USA.
020    
021    Linking this library statically or dynamically with other modules is
022    making a combined work based on this library.  Thus, the terms and
023    conditions of the GNU General Public License cover the whole
024    combination.
025    
026    As a special exception, the copyright holders of this library give you
027    permission to link this library with independent modules to produce an
028    executable, regardless of the license terms of these independent
029    modules, and to copy and distribute the resulting executable under
030    terms of your choice, provided that you also meet, for each linked
031    independent module, the terms and conditions of the license of that
032    module.  An independent module is a module which is not derived from
033    or based on this library.  If you modify this library, you may extend
034    this exception to your version of the library, but you are not
035    obligated to do so.  If you do not wish to do so, delete this
036    exception statement from your version. */
037    
038    package javax.management;
039    
040    import java.lang.reflect.Constructor;
041    import java.lang.reflect.InvocationTargetException;
042    import java.lang.reflect.Method;
043    
044    import java.util.ArrayList;
045    import java.util.HashMap;
046    import java.util.Iterator;
047    import java.util.List;
048    import 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     */
065    public 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, null);
232          }
233        catch (NoSuchMethodException e)
234          {
235            try 
236              {
237                getter = iface.getMethod("is" + name, null);
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, null);
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 attributes = new HashMap();
573        List operations = new ArrayList();
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 attribs = new ArrayList(attributes.size());
618        Iterator it = attributes.entrySet().iterator();
619        while (it.hasNext())
620          {
621            Map.Entry entry = (Map.Entry) it.next();
622            Method[] amethods = (Method[]) entry.getValue();
623            try
624              {
625                attribs.add(new MBeanAttributeInfo((String) entry.getKey(),
626                                                   (String) entry.getKey(),
627                                                   amethods[0], amethods[1]));
628              }
629            catch (IntrospectionException e)
630              {
631                /* Shouldn't happen; both shouldn't be null */
632                throw new IllegalStateException("The two methods passed to " +
633                                                "the MBeanAttributeInfo " +
634                                                "constructor for " + entry +
635                                                "were null.", e);
636              }
637          }
638        MBeanAttributeInfo[] ainfo = new MBeanAttributeInfo[attribs.size()];
639        for (int a = 0; a < ainfo.length; ++a)
640          {
641            MBeanAttributeInfo oldInfo = (MBeanAttributeInfo) attribs.get(a);
642            String desc = getDescription(oldInfo);
643            ainfo[a] = new MBeanAttributeInfo(oldInfo.getName(),
644                                              oldInfo.getType(), desc,
645                                              oldInfo.isReadable(),
646                                              oldInfo.isWritable(),
647                                              oldInfo.isIs());
648          }
649        Constructor[] cons = impl.getClass().getConstructors();
650        MBeanConstructorInfo[] cinfo = new MBeanConstructorInfo[cons.length];
651        for (int a = 0; a < cinfo.length; ++a)
652          {
653            MBeanConstructorInfo oldInfo = new MBeanConstructorInfo(cons[a].getName(),
654                                                                    cons[a]);
655            String desc = getDescription(oldInfo);
656            MBeanParameterInfo[] params = oldInfo.getSignature();
657            MBeanParameterInfo[] pinfo = new MBeanParameterInfo[params.length];
658            for (int b = 0; b < pinfo.length; ++b)
659              {
660                String pdesc = getDescription(oldInfo, params[b], b);
661                String pname = getParameterName(oldInfo, params[b], b);
662                pinfo[b] = new MBeanParameterInfo(pname, params[b].getType(),
663                                                  pdesc);
664              }
665            cinfo[a] = new MBeanConstructorInfo(oldInfo.getName(), desc,
666                                                pinfo);
667          }
668        cinfo = getConstructors(cinfo, impl);
669        MBeanOperationInfo[] oinfo = new MBeanOperationInfo[operations.size()];
670        for (int a = 0; a < oinfo.length; ++a)
671          {
672            MBeanOperationInfo oldInfo = (MBeanOperationInfo) operations.get(a);
673            String desc = getDescription(oldInfo);
674            int impact = getImpact(oldInfo);
675            MBeanParameterInfo[] params = oldInfo.getSignature();
676            MBeanParameterInfo[] pinfo = new MBeanParameterInfo[params.length];
677            for (int b = 0; b < pinfo.length; ++b)
678              {
679                String pdesc = getDescription(oldInfo, params[b], b);
680                String pname = getParameterName(oldInfo, params[b], b);
681                pinfo[b] = new MBeanParameterInfo(pname, params[b].getType(),
682                                                  pdesc);
683              }
684            oinfo[a] = new MBeanOperationInfo(oldInfo.getName(), desc, pinfo,
685                                              oldInfo.getReturnType(), impact);
686          }
687        info = new MBeanInfo(impl.getClass().getName(), impl.getClass().getName(),
688                             ainfo, cinfo, oinfo, null);
689        String cname = getClassName(info);
690        String desc = getDescription(info);
691        MBeanNotificationInfo[] ninfo = null;
692        if (impl instanceof NotificationBroadcaster)
693          ninfo = ((NotificationBroadcaster) impl).getNotificationInfo();
694        info = new MBeanInfo(cname, desc, ainfo, cinfo, oinfo, ninfo);
695        cacheMBeanInfo(info);
696        return info;
697      }
698    
699      /**
700       * Returns the interface for this management bean.
701       *
702       * @return the management interface.
703       */
704      public final Class<?> getMBeanInterface()
705      {
706        return iface;
707      }
708    
709      /**
710       * Returns the name of the nth parameter of the constructor
711       * that will be used in the supplied {@link MBeanParameterInfo}
712       * instance.  This is a customization hook, so that subclasses
713       * can provide a custom name.  By default, this calls
714       * <code>param.getName()</code>.
715       *
716       * @param info the {@link MBeanConstructorInfo} instance constructed
717       *             via reflection.
718       * @param param the {@link MBeanParameterInfo} instance constructed
719       *             via reflection.
720       * @param n the number of the parameter, in order to link it to the
721       *          information on the constructor.
722       * @return the name to use in the instance.
723       */
724      protected String getParameterName(MBeanConstructorInfo info,
725                                        MBeanParameterInfo param, int n)
726      {
727        return param.getName();
728      }
729    
730      /**
731       * Returns the name of the nth parameter of the operation
732       * that will be used in the supplied {@link MBeanParameterInfo}
733       * instance.  This is a customization hook, so that subclasses
734       * can provide a custom name.  By default, this calls
735       * <code>param.getName()</code>.
736       *
737       * @param info the {@link MBeanOperationInfo} instance constructed
738       *             via reflection.
739       * @param param the {@link MBeanParameterInfo} instance constructed
740       *             via reflection.
741       * @param n the number of the parameter, in order to link it to the
742       *          information on the operation.
743       * @return the name to use in the instance.
744       */
745      protected String getParameterName(MBeanOperationInfo info,
746                                        MBeanParameterInfo param, int n)
747      {
748        return param.getName();
749      }
750    
751      /**
752       * Invokes the specified action on the management bean using
753       * the supplied parameters.  The signature of the action is
754       * specified by a {@link String} array, which lists the classes
755       * corresponding to each parameter.  The class loader used to
756       * load these classes is the same as that used for loading the
757       * management bean itself.
758       * 
759       * @param name the name of the action to invoke.
760       * @param params the parameters used to call the action.
761       * @param signature the signature of the action.
762       * @return the return value of the action.
763       * @throws MBeanException if the action throws an exception.  The
764       *                        thrown exception is the cause of this
765       *                        exception.
766       * @throws ReflectionException if an exception occurred in trying
767       *                             to use the reflection interface
768       *                             to invoke the action.  The
769       *                             thrown exception is the cause of
770       *                             this exception.
771       */
772      public Object invoke(String name, Object[] params, String[] signature)
773        throws MBeanException, ReflectionException
774      {
775        if (name.startsWith("get") || name.startsWith("is") ||
776            name.startsWith("set"))
777          throw new ReflectionException(new NoSuchMethodException(),
778                                        "Invocation of an attribute " +
779                                        "method is disallowed.");
780        ClassLoader loader = getClass().getClassLoader();
781        Class[] sigTypes;
782        if (signature != null)
783          {
784            sigTypes = new Class[signature.length];
785            for (int a = 0; a < signature.length; ++a)
786              try 
787                {
788                  sigTypes[a] = Class.forName(signature[a], true, loader);
789                }
790              catch (ClassNotFoundException e)
791                {
792                  throw new ReflectionException(e, "The class, " + signature[a] + 
793                                                ", in the method signature " +
794                                                "could not be loaded.");
795                }
796          }
797        else
798          sigTypes = null;
799        Method method;
800        try
801          {
802            method = iface.getMethod(name, sigTypes);
803          }
804        catch (NoSuchMethodException e)
805          {
806            throw new ReflectionException(e, "The method, " + name +
807                                          ", could not be found.");
808          }
809        Object result;
810        try
811          {
812            result = method.invoke(impl, params);
813          }
814        catch (IllegalAccessException e)
815          {
816            throw new ReflectionException(e, "Failed to call " + name);
817          }
818        catch (IllegalArgumentException e)
819          {
820            throw new ReflectionException(e, "Failed to call " + name);
821          }
822        catch (InvocationTargetException e)
823          {
824            throw new MBeanException((Exception) e.getCause(), "The method "
825                                     + name + " threw an exception");
826          }
827        return result;
828      }
829    
830      /**
831       * Sets the value of the specified attribute of the
832       * management bean.  The management bean should perform
833       * a lookup for the named attribute, and sets its value
834       * using the associated setter method, if possible.
835       *
836       * @param attribute the attribute to set.
837       * @throws AttributeNotFoundException if the attribute does not
838       *                                    correspond to an attribute
839       *                                    of the bean.
840       * @throws InvalidAttributeValueException if the value is invalid
841       *                                        for this particular
842       *                                        attribute of the bean.
843       * @throws MBeanException if setting the attribute causes
844       *                        the bean to throw an exception (which
845       *                        becomes the cause of this exception).
846       * @throws ReflectionException if an exception occurred in trying
847       *                             to use the reflection interface
848       *                             to lookup the attribute.  The
849       *                             thrown exception is the cause of
850       *                             this exception.
851       * @see #getAttribute(String)
852       */
853      public void setAttribute(Attribute attribute)
854        throws AttributeNotFoundException, InvalidAttributeValueException,
855               MBeanException, ReflectionException
856      {
857        String name = attribute.getName();
858        String attName = name.substring(0, 1).toUpperCase() + name.substring(1);
859        Object val = attribute.getValue();   
860        try
861          {
862            getMutator(attName, val.getClass()).invoke(impl, new Object[] { val });
863          }
864        catch (IllegalAccessException e)
865          {
866            throw new ReflectionException(e, "Failed to set " + name);
867          }
868        catch (IllegalArgumentException e)
869          {
870            throw ((InvalidAttributeValueException)
871                   new InvalidAttributeValueException(attribute.getValue() +
872                                                      " is an invalid value for " +
873                                                      name).initCause(e));
874          }
875        catch (InvocationTargetException e)
876          {
877            throw new MBeanException(e, "The getter of " + name +
878                                     " threw an exception");
879          }
880      }
881    
882      /**
883       * Sets the value of each of the specified attributes
884       * to that supplied by the {@link Attribute} object.
885       * The returned list contains the attributes that were
886       * set and their new values.
887       *
888       * @param attributes the attributes to set.
889       * @return a list of the changed attributes.
890       * @see #getAttributes(AttributeList)
891       */
892      public AttributeList setAttributes(AttributeList attributes)
893      {
894        AttributeList list = new AttributeList(attributes.size());
895        Iterator it = attributes.iterator();
896        while (it.hasNext())
897          {
898            try
899              {
900                Attribute attrib = (Attribute) it.next();
901                setAttribute(attrib);
902                list.add(attrib);
903              }
904            catch (AttributeNotFoundException e)
905              {
906                /* Ignored */
907              }
908            catch (InvalidAttributeValueException e)
909              {
910                /* Ignored */
911              }
912            catch (ReflectionException e)
913              {
914                /* Ignored */
915              }
916            catch (MBeanException e)
917              {
918                /* Ignored */
919              }
920          }
921        return list;
922      }
923    
924      /**
925       * Replaces the implementation of the interface used by this
926       * instance with the one specified.  The new implementation
927       * must be non-null and implement the interface specified on
928       * construction of this instance.
929       *
930       * @throws IllegalArgumentException if <code>impl</code> is <code>null</code>.
931       * @throws NotCompliantMBeanException if <code>impl</code> doesn't implement
932       *                                    the interface or a method appears
933       *                                    in the interface that doesn't comply
934       *                                    with the naming conventions.
935       */
936      public void setImplementation(Object impl)
937        throws NotCompliantMBeanException
938      {
939        if (impl == null)
940          throw new IllegalArgumentException("The specified implementation is null.");
941        if (!(iface.isInstance(impl)))
942          throw new NotCompliantMBeanException("The instance, " + impl + 
943                                               ", is not an instance of " + iface);
944        this.impl = impl;
945      }
946    
947      /**
948       * Returns the mutator method for a particular attribute name
949       * with a parameter type matching that of the given value.
950       *
951       * @param name the name of the attribute.
952       * @param type the type of the parameter.
953       * @return the appropriate mutator method.
954       * @throws AttributeNotFoundException if a method can't be found.
955       */
956      private Method getMutator(String name, Class<?> type)
957        throws AttributeNotFoundException
958      {
959        String mutator = "set" + name;
960        Exception ex = null;
961        try 
962          {
963            return iface.getMethod(mutator, type);
964          }
965        catch (NoSuchMethodException e)
966          {
967            /* Ignored; we'll try harder instead */
968            ex = e;
969          }
970        /* Special cases */
971        if (type == Boolean.class)
972          try
973            {
974              return iface.getMethod(mutator, Boolean.TYPE);
975            }
976          catch (NoSuchMethodException e)
977            {
978              throw ((AttributeNotFoundException) 
979                     new AttributeNotFoundException("The attribute, " + name +
980                                                    ", was not found.").initCause(e));
981            }
982        if (type == Byte.class)
983          try
984            {
985              return iface.getMethod(mutator, Byte.TYPE);
986            }
987          catch (NoSuchMethodException e)
988            {
989              throw ((AttributeNotFoundException) 
990                     new AttributeNotFoundException("The attribute, " + name +
991                                                    ", was not found.").initCause(e));
992            }
993        if (type == Character.class)
994          try
995            {
996              return iface.getMethod(mutator, Character.TYPE);
997            }
998          catch (NoSuchMethodException e)
999            {
1000              throw ((AttributeNotFoundException) 
1001                     new AttributeNotFoundException("The attribute, " + name +
1002                                                    ", was not found.").initCause(e));
1003            }
1004        if (type == Double.class)
1005          try
1006            {
1007              return iface.getMethod(mutator, Double.TYPE);
1008            }
1009          catch (NoSuchMethodException e)
1010            {
1011              throw ((AttributeNotFoundException) 
1012                     new AttributeNotFoundException("The attribute, " + name +
1013                                                    ", was not found.").initCause(e));
1014            }
1015        if (type == Float.class)
1016          try
1017            {
1018              return iface.getMethod(mutator, Float.TYPE);
1019            }
1020          catch (NoSuchMethodException e)
1021            {
1022              throw ((AttributeNotFoundException) 
1023                     new AttributeNotFoundException("The attribute, " + name +
1024                                                    ", was not found.").initCause(e));
1025            }
1026        if (type == Integer.class)
1027          try
1028            {
1029              return iface.getMethod(mutator, Integer.TYPE);
1030            }
1031          catch (NoSuchMethodException e)
1032            {
1033              throw ((AttributeNotFoundException) 
1034                     new AttributeNotFoundException("The attribute, " + name +
1035                                                    ", was not found.").initCause(e));
1036            }
1037        if (type == Long.class)
1038          try
1039            {
1040              return iface.getMethod(mutator, Long.TYPE);
1041            }
1042          catch (NoSuchMethodException e)
1043            {
1044              throw ((AttributeNotFoundException) 
1045                     new AttributeNotFoundException("The attribute, " + name +
1046                                                    ", was not found.").initCause(e));
1047            }
1048        if (type == Short.class)
1049          try
1050            {
1051              return iface.getMethod(mutator, Short.TYPE);
1052            }
1053          catch (NoSuchMethodException e)
1054            {
1055              throw ((AttributeNotFoundException) 
1056                     new AttributeNotFoundException("The attribute, " + name +
1057                                                    ", was not found.").initCause(e));
1058            }
1059        /* Superclasses and interfaces */
1060        for (Class<?> i : type.getInterfaces())
1061          try
1062            {
1063              return getMutator(name, i);
1064            }
1065          catch (AttributeNotFoundException e)
1066            {
1067              ex = e;
1068            }
1069        Class<?> sclass = type.getSuperclass();
1070        if (sclass != null && sclass != Object.class)
1071          try
1072            {
1073              return getMutator(name, sclass);
1074            }
1075          catch (AttributeNotFoundException e)
1076            {
1077              ex = e;
1078            }
1079        /* If we get this far, give up */
1080        throw ((AttributeNotFoundException) 
1081               new AttributeNotFoundException("The attribute, " + name +
1082                                              ", was not found.").initCause(ex)); 
1083      }
1084    
1085    }