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);
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    }