View Javadoc

1   //
2   //  ========================================================================
3   //  Copyright (c) 1995-2013 Mort Bay Consulting Pty. Ltd.
4   //  ------------------------------------------------------------------------
5   //  All rights reserved. This program and the accompanying materials
6   //  are made available under the terms of the Eclipse Public License v1.0
7   //  and Apache License v2.0 which accompanies this distribution.
8   //
9   //      The Eclipse Public License is available at
10  //      http://www.eclipse.org/legal/epl-v10.html
11  //
12  //      The Apache License v2.0 is available at
13  //      http://www.opensource.org/licenses/apache2.0.php
14  //
15  //  You may elect to redistribute this code under either of these licenses.
16  //  ========================================================================
17  //
18  
19  package org.eclipse.jetty.jmx;
20  
21  import java.lang.annotation.Annotation;
22  import java.lang.reflect.Array;
23  import java.lang.reflect.Constructor;
24  import java.lang.reflect.InvocationTargetException;
25  import java.lang.reflect.Method;
26  import java.lang.reflect.Modifier;
27  import java.util.ArrayList;
28  import java.util.Collection;
29  import java.util.HashMap;
30  import java.util.HashSet;
31  import java.util.Iterator;
32  import java.util.List;
33  import java.util.Locale;
34  import java.util.Map;
35  import java.util.Set;
36  
37  import javax.management.Attribute;
38  import javax.management.AttributeList;
39  import javax.management.AttributeNotFoundException;
40  import javax.management.DynamicMBean;
41  import javax.management.InvalidAttributeValueException;
42  import javax.management.MBeanAttributeInfo;
43  import javax.management.MBeanConstructorInfo;
44  import javax.management.MBeanException;
45  import javax.management.MBeanInfo;
46  import javax.management.MBeanNotificationInfo;
47  import javax.management.MBeanOperationInfo;
48  import javax.management.MBeanParameterInfo;
49  import javax.management.ObjectName;
50  import javax.management.ReflectionException;
51  import javax.management.modelmbean.ModelMBean;
52  
53  import org.eclipse.jetty.util.Loader;
54  import org.eclipse.jetty.util.TypeUtil;
55  import org.eclipse.jetty.util.annotation.ManagedAttribute;
56  import org.eclipse.jetty.util.annotation.ManagedObject;
57  import org.eclipse.jetty.util.annotation.ManagedOperation;
58  import org.eclipse.jetty.util.annotation.Name;
59  import org.eclipse.jetty.util.log.Log;
60  import org.eclipse.jetty.util.log.Logger;
61  
62  /* ------------------------------------------------------------ */
63  /** ObjectMBean.
64   * A dynamic MBean that can wrap an arbitary Object instance.
65   * the attributes and methods exposed by this bean are controlled by
66   * the merge of property bundles discovered by names related to all
67   * superclasses and all superinterfaces.
68   *
69   * Attributes and methods exported may be "Object" and must exist on the
70   * wrapped object, or "MBean" and must exist on a subclass of OBjectMBean
71   * or "MObject" which exists on the wrapped object, but whose values are
72   * converted to MBean object names.
73   *
74   */
75  public class ObjectMBean implements DynamicMBean
76  {
77      private static final Logger LOG = Log.getLogger(ObjectMBean.class);
78  
79      private static Class<?>[] OBJ_ARG = new Class[]{Object.class};
80  
81      protected Object _managed;
82      private MBeanInfo _info;
83      private Map<String, Method> _getters=new HashMap<String, Method>();
84      private Map<String, Method> _setters=new HashMap<String, Method>();
85      private Map<String, Method> _methods=new HashMap<String, Method>();
86  
87      // set of attributes mined from influence hierarchy
88      private Set<String> _attributes = new HashSet<String>();
89  
90      // set of attributes that are automatically converted to ObjectName
91      // as they represent other managed beans which can be linked to
92      private Set<String> _convert=new HashSet<String>();
93      private ClassLoader _loader;
94      private MBeanContainer _mbeanContainer;
95  
96      private static String OBJECT_NAME_CLASS = ObjectName.class.getName();
97      private static String OBJECT_NAME_ARRAY_CLASS = ObjectName[].class.getName();
98  
99      /* ------------------------------------------------------------ */
100     /**
101      * Create MBean for Object. Attempts to create an MBean for the object by searching the package
102      * and class name space. For example an object of the type
103      *
104      * <PRE>
105      * class com.acme.MyClass extends com.acme.util.BaseClass implements com.acme.Iface
106      * </PRE>
107      *
108      * Then this method would look for the following classes:
109      * <UL>
110      * <LI>com.acme.jmx.MyClassMBean
111      * <LI>com.acme.util.jmx.BaseClassMBean
112      * <LI>org.eclipse.jetty.jmx.ObjectMBean
113      * </UL>
114      *
115      * @param o The object
116      * @return A new instance of an MBean for the object or null.
117      */
118     public static Object mbeanFor(Object o)
119     {
120         try
121         {
122             Class<?> oClass = o.getClass();
123             Object mbean = null;
124 
125             while ( mbean == null && oClass != null )
126             {
127                 String pName = oClass.getPackage().getName();
128                 String cName = oClass.getName().substring(pName.length() + 1);
129                 String mName = pName + ".jmx." + cName + "MBean";
130 
131                 try
132                 {
133                     Class<?> mClass = (Object.class.equals(oClass))?oClass=ObjectMBean.class:Loader.loadClass(oClass,mName,true);
134 
135                     LOG.debug("ObjectMbean: mbeanFor {} mClass={}", o, mClass);
136 
137                     try
138                     {
139                         Constructor<?> constructor = mClass.getConstructor(OBJ_ARG);
140                         mbean=constructor.newInstance(new Object[]{o});
141                     }
142                     catch(Exception e)
143                     {
144                         LOG.ignore(e);
145                         if (ModelMBean.class.isAssignableFrom(mClass))
146                         {
147                             mbean=mClass.newInstance();
148                             ((ModelMBean)mbean).setManagedResource(o, "objectReference");
149                         }
150                     }
151 
152                     LOG.debug("mbeanFor {} is {}", o, mbean);
153 
154                     return mbean;
155                 }
156                 catch (ClassNotFoundException e)
157                 {
158                     // The code below was modified to fix bugs 332200 and JETTY-1416
159                     // The issue was caused by additional information added to the
160                     // message after the class name when running in Apache Felix,
161                     // as well as before the class name when running in JBoss.
162                     if (e.getMessage().contains(mName))
163                         LOG.ignore(e);
164                     else
165                         LOG.warn(e);
166                 }
167                 catch (Error e)
168                 {
169                     LOG.warn(e);
170                     mbean = null;
171                 }
172                 catch (Exception e)
173                 {
174                     LOG.warn(e);
175                     mbean = null;
176                 }
177 
178                 oClass = oClass.getSuperclass();
179             }
180         }
181         catch (Exception e)
182         {
183             LOG.ignore(e);
184         }
185 
186         return null;
187     }
188 
189 
190     public ObjectMBean(Object managedObject)
191     {
192         _managed = managedObject;
193         _loader = Thread.currentThread().getContextClassLoader();
194     }
195 
196     public Object getManagedObject()
197     {
198         return _managed;
199     }
200 
201     public ObjectName getObjectName()
202     {
203         return null;
204     }
205 
206     public String getObjectContextBasis()
207     {
208         return null;
209     }
210 
211     public String getObjectNameBasis()
212     {
213         return null;
214     }
215 
216     protected void setMBeanContainer(MBeanContainer container)
217     {
218        this._mbeanContainer = container;
219     }
220 
221     public MBeanContainer getMBeanContainer ()
222     {
223         return this._mbeanContainer;
224     }
225 
226 
227     public MBeanInfo getMBeanInfo()
228     {
229         try
230         {
231             if (_info==null)
232             {
233                 // Start with blank lazy lists attributes etc.
234                 String desc=null;
235                 List<MBeanAttributeInfo> attributes = new ArrayList<MBeanAttributeInfo>();
236                 List<MBeanConstructorInfo> constructors = new ArrayList<MBeanConstructorInfo>();
237                 List<MBeanOperationInfo> operations = new ArrayList<MBeanOperationInfo>();
238                 List<MBeanNotificationInfo> notifications = new ArrayList<MBeanNotificationInfo>();
239 
240                 // Find list of classes that can influence the mbean
241                 Class<?> o_class=_managed.getClass();
242                 List<Class<?>> influences = findInfluences(new ArrayList<Class<?>>(), _managed.getClass());
243 
244                 LOG.debug("Influence Count: {}", influences.size() );
245 
246                 // Process Type Annotations
247                 ManagedObject primary = o_class.getAnnotation( ManagedObject.class);
248 
249                 if ( primary != null )
250                 {
251                     desc = primary.value();
252                 }
253                 else
254                 {
255                     LOG.debug("No @ManagedObject declared on {}", _managed.getClass());
256                 }
257 
258 
259                 // For each influence
260                 for (int i=0;i<influences.size();i++)
261                 {
262                     Class<?> oClass = influences.get(i);
263 
264                     ManagedObject typeAnnotation = oClass.getAnnotation( ManagedObject.class );
265 
266                     LOG.debug("Influenced by: " + oClass.getCanonicalName() );
267                     if ( typeAnnotation == null )
268                     {
269                         LOG.debug("Annotations not found for: {}", oClass.getCanonicalName() );
270                         continue;
271                     }
272 
273                     // Process Method Annotations
274 
275                     for (Method method : oClass.getDeclaredMethods())
276                     {
277                         ManagedAttribute methodAttributeAnnotation = method.getAnnotation(ManagedAttribute.class);
278 
279                         if (methodAttributeAnnotation != null)
280                         {
281                             // TODO sort out how a proper name could get here, its a method name as an attribute at this point.
282                             LOG.debug("Attribute Annotation found for: {}", method.getName());
283                             MBeanAttributeInfo mai = defineAttribute(method,methodAttributeAnnotation);
284                             if ( mai != null )
285                             {
286                                 attributes.add(mai);
287                             }
288                         }
289 
290                         ManagedOperation methodOperationAnnotation = method.getAnnotation(ManagedOperation.class);
291 
292                         if (methodOperationAnnotation != null)
293                         {
294                             LOG.debug("Method Annotation found for: {}", method.getName());
295                             MBeanOperationInfo oi = defineOperation(method,methodOperationAnnotation);
296 
297                             if (oi != null)
298                             {
299                                 operations.add(oi);
300                             }
301                         }
302                     }
303 
304                 }
305 
306                 _info = new MBeanInfo(o_class.getName(),
307                                 desc,
308                                 (MBeanAttributeInfo[])attributes.toArray(new MBeanAttributeInfo[attributes.size()]),
309                                 (MBeanConstructorInfo[])constructors.toArray(new MBeanConstructorInfo[constructors.size()]),
310                                 (MBeanOperationInfo[])operations.toArray(new MBeanOperationInfo[operations.size()]),
311                                 (MBeanNotificationInfo[])notifications.toArray(new MBeanNotificationInfo[notifications.size()]));
312             }
313         }
314         catch(RuntimeException e)
315         {
316             LOG.warn(e);
317             throw e;
318         }
319         return _info;
320     }
321 
322 
323     /* ------------------------------------------------------------ */
324     public Object getAttribute(String name) throws AttributeNotFoundException, MBeanException, ReflectionException
325     {
326         Method getter = (Method) _getters.get(name);
327         if (getter == null)
328         {
329             throw new AttributeNotFoundException(name);
330         }
331 
332         try
333         {
334             Object o = _managed;
335             if (getter.getDeclaringClass().isInstance(this))
336                 o = this; // mbean method
337 
338             // get the attribute
339             Object r=getter.invoke(o, (java.lang.Object[]) null);
340 
341             // convert to ObjectName if the type has the @ManagedObject annotation
342             if (r!=null )
343             {
344                 if (r.getClass().isArray())
345                 {
346                     if (r.getClass().getComponentType().isAnnotationPresent(ManagedObject.class))
347                     {
348                         ObjectName[] on = new ObjectName[Array.getLength(r)];
349                         for (int i = 0; i < on.length; i++)
350                         {
351                             on[i] = _mbeanContainer.findMBean(Array.get(r,i));
352                         }
353                         r = on;
354                     }
355                 }
356                 else if (r instanceof Collection<?>)
357                 {
358                     @SuppressWarnings("unchecked")
359                     Collection<Object> c = (Collection<Object>)r;
360 
361                     if (!c.isEmpty() && c.iterator().next().getClass().isAnnotationPresent(ManagedObject.class))
362                     {
363                         // check the first thing out
364 
365                         ObjectName[] on = new ObjectName[c.size()];
366                         int i = 0;
367                         for (Object obj : c)
368                         {
369                             on[i++] = _mbeanContainer.findMBean(obj);
370                         }
371                         r = on;
372                     }
373                 }
374                 else
375                 {
376                     Class<?> clazz = r.getClass();
377                     
378                     while (clazz != null)
379                     {
380                         if (clazz.isAnnotationPresent(ManagedObject.class))
381                         {
382                             ObjectName mbean = _mbeanContainer.findMBean(r);
383 
384                             if (mbean != null)
385                             {    
386                                 return mbean;
387                             }
388                             else
389                             {
390                                 return null;
391                             }
392                         }                   
393                         clazz = clazz.getSuperclass();
394                     }              
395                 }
396             }
397 
398             return r;
399         }
400         catch (IllegalAccessException e)
401         {
402             LOG.warn(Log.EXCEPTION, e);
403             throw new AttributeNotFoundException(e.toString());
404         }
405         catch (InvocationTargetException e)
406         {
407             LOG.warn(Log.EXCEPTION, e);
408             throw new ReflectionException(new Exception(e.getCause()));
409         }
410     }
411 
412     /* ------------------------------------------------------------ */
413     public AttributeList getAttributes(String[] names)
414     {
415         AttributeList results = new AttributeList(names.length);
416         for (int i = 0; i < names.length; i++)
417         {
418             try
419             {
420                 results.add(new Attribute(names[i], getAttribute(names[i])));
421             }
422             catch (Exception e)
423             {
424                 LOG.warn(Log.EXCEPTION, e);
425             }
426         }
427         return results;
428     }
429 
430     /* ------------------------------------------------------------ */
431     public void setAttribute(Attribute attr) throws AttributeNotFoundException, InvalidAttributeValueException, MBeanException, ReflectionException
432     {
433         if (attr == null)
434             return;
435 
436         if (LOG.isDebugEnabled())
437             LOG.debug("setAttribute " + _managed + ":" +attr.getName() + "=" + attr.getValue());
438         Method setter = (Method) _setters.get(attr.getName());
439         if (setter == null)
440             throw new AttributeNotFoundException(attr.getName());
441         try
442         {
443             Object o = _managed;
444             if (setter.getDeclaringClass().isInstance(this))
445                 o = this;
446 
447             // get the value
448             Object value = attr.getValue();
449 
450             // convert from ObjectName if need be
451             if (value!=null && _convert.contains(attr.getName()))
452             {
453                 if (value.getClass().isArray())
454                 {
455                     Class<?> t=setter.getParameterTypes()[0].getComponentType();
456                     Object na = Array.newInstance(t,Array.getLength(value));
457                     for (int i=Array.getLength(value);i-->0;)
458                         Array.set(na, i, _mbeanContainer.findBean((ObjectName)Array.get(value, i)));
459                     value=na;
460                 }
461                 else
462                     value=_mbeanContainer.findBean((ObjectName)value);
463             }
464 
465             // do the setting
466             setter.invoke(o, new Object[]{ value });
467         }
468         catch (IllegalAccessException e)
469         {
470             LOG.warn(Log.EXCEPTION, e);
471             throw new AttributeNotFoundException(e.toString());
472         }
473         catch (InvocationTargetException e)
474         {
475             LOG.warn(Log.EXCEPTION, e);
476             throw new ReflectionException(new Exception(e.getCause()));
477         }
478     }
479 
480     /* ------------------------------------------------------------ */
481     public AttributeList setAttributes(AttributeList attrs)
482     {
483         LOG.debug("setAttributes");
484 
485         AttributeList results = new AttributeList(attrs.size());
486         Iterator<Object> iter = attrs.iterator();
487         while (iter.hasNext())
488         {
489             try
490             {
491                 Attribute attr = (Attribute) iter.next();
492                 setAttribute(attr);
493                 results.add(new Attribute(attr.getName(), getAttribute(attr.getName())));
494             }
495             catch (Exception e)
496             {
497                 LOG.warn(Log.EXCEPTION, e);
498             }
499         }
500         return results;
501     }
502 
503     /* ------------------------------------------------------------ */
504     public Object invoke(String name, Object[] params, String[] signature) throws MBeanException, ReflectionException
505     {
506         LOG.debug("ObjectMBean:invoke " + name);
507 
508         String methodKey = name + "(";
509         if (signature != null)
510             for (int i = 0; i < signature.length; i++)
511                 methodKey += (i > 0 ? "," : "") + signature[i];
512         methodKey += ")";
513 
514         ClassLoader old_loader=Thread.currentThread().getContextClassLoader();
515         try
516         {
517             Thread.currentThread().setContextClassLoader(_loader);
518             Method method = (Method) _methods.get(methodKey);
519             if (method == null)
520                 throw new NoSuchMethodException(methodKey);
521 
522             Object o = _managed;
523 
524             if (method.getDeclaringClass().isInstance(this))
525             {
526                 o = this;
527             }
528             return method.invoke(o, params);
529         }
530         catch (NoSuchMethodException e)
531         {
532             LOG.warn(Log.EXCEPTION, e);
533             throw new ReflectionException(e);
534         }
535         catch (IllegalAccessException e)
536         {
537             LOG.warn(Log.EXCEPTION, e);
538             throw new MBeanException(e);
539         }
540         catch (InvocationTargetException e)
541         {
542             LOG.warn(Log.EXCEPTION, e);
543             throw new ReflectionException(new Exception(e.getCause()));
544         }
545         finally
546         {
547             Thread.currentThread().setContextClassLoader(old_loader);
548         }
549     }
550 
551     private static List<Class<?>> findInfluences(List<Class<?>> influences, Class<?> aClass)
552     {
553         if (aClass!=null)
554         {
555             // This class is an influence
556             influences.add(aClass);
557 
558             String pName = aClass.getPackage().getName();
559             String cName = aClass.getName().substring(pName.length() + 1);
560             String mName = pName + ".jmx." + cName + "MBean";
561 
562             try
563             {
564                 Class<?> mbeanClazz = Class.forName(mName);
565                 LOG.debug("MBean Influence found for " + aClass.getSimpleName());
566                 influences.add(mbeanClazz);
567             }
568             catch (ClassNotFoundException cnfe)
569             {
570                 LOG.debug("No MBean Influence for " + aClass.getSimpleName());
571             }
572 
573             // So are the super classes
574             influences=findInfluences(influences,aClass.getSuperclass());
575 
576             // So are the interfaces
577             Class<?>[] ifs = aClass.getInterfaces();
578             for (int i=0;ifs!=null && i<ifs.length;i++)
579                 influences=findInfluences(influences,ifs[i]);
580         }
581 
582         return influences;
583     }
584 
585     /* ------------------------------------------------------------ */
586     /**
587      * TODO update to new behavior
588      *
589      * Define an attribute on the managed object. The meta data is defined by looking for standard
590      * getter and setter methods. Descriptions are obtained with a call to findDescription with the
591      * attribute name.
592      *
593      * @param name
594      * @param metaData "description" or "access:description" or "type:access:description"  where type is
595      * one of: <ul>
596      * <li>"Object" The field/method is on the managed object.
597      * <li>"MBean" The field/method is on the mbean proxy object
598      * <li>"MObject" The field/method is on the managed object and value should be converted to MBean reference
599      * <li>"MMBean" The field/method is on the mbean proxy object and value should be converted to MBean reference
600      * </ul>
601      * the access is either "RW" or "RO".
602      */
603     public MBeanAttributeInfo defineAttribute(Method method, ManagedAttribute attributeAnnotation)
604     {
605         // determine the name of the managed attribute
606         String name = attributeAnnotation.name();
607 
608         if ("".equals(name))
609         {
610             name = toVariableName(method.getName());
611         }
612 
613         if ( _attributes.contains(name))
614         {
615             return null; // we have an attribute named this already
616         }
617 
618         String description = attributeAnnotation.value();
619         boolean readonly = attributeAnnotation.readonly();
620         boolean onMBean = attributeAnnotation.proxied();
621 
622         boolean convert = false;
623 
624         // determine if we should convert
625         Class<?> return_type = method.getReturnType();
626 
627         // get the component type
628         Class<?> component_type = return_type;
629         while ( component_type.isArray() )
630         {
631             component_type = component_type.getComponentType();
632         }
633            
634         // Test to see if the returnType or any of its super classes are managed objects
635         convert = isAnnotationPresent(component_type, ManagedObject.class);       
636         
637         String uName = name.substring(0, 1).toUpperCase(Locale.ENGLISH) + name.substring(1);
638         Class<?> oClass = onMBean ? this.getClass() : _managed.getClass();
639 
640         LOG.debug("defineAttribute {} {}:{}:{}:{}",name,onMBean,readonly,oClass,description);
641 
642         Method setter = null;
643 
644         // dig out a setter if one exists
645         if (!readonly)
646         {
647             String declaredSetter = attributeAnnotation.setter();
648 
649             LOG.debug("DeclaredSetter: {}", declaredSetter);
650             Method[] methods = oClass.getMethods();
651             for (int m = 0; m < methods.length; m++)
652             {
653                 if ((methods[m].getModifiers() & Modifier.PUBLIC) == 0)
654                     continue;
655 
656                 if (!"".equals(declaredSetter))
657                 {
658 
659                     // look for a declared setter
660                     if (methods[m].getName().equals(declaredSetter) && methods[m].getParameterTypes().length == 1)
661                     {
662                         if (setter != null)
663                         {
664                             LOG.warn("Multiple setters for mbean attr {} in {}", name, oClass);
665                             continue;
666                         }
667                         setter = methods[m];
668                         if ( !component_type.equals(methods[m].getParameterTypes()[0]))
669                         {
670                             LOG.warn("Type conflict for mbean attr {} in {}", name, oClass);
671                             continue;
672                         }
673                         LOG.debug("Declared Setter: " + declaredSetter);
674                     }
675                 }
676 
677                 // look for a setter
678                 if ( methods[m].getName().equals("set" + uName) && methods[m].getParameterTypes().length == 1)
679                 {
680                     if (setter != null)
681                     {
682                         LOG.warn("Multiple setters for mbean attr {} in {}", name, oClass);
683                         continue;
684                     }
685                     setter = methods[m];
686                     if ( !return_type.equals(methods[m].getParameterTypes()[0]))
687                     {                            
688                         LOG.warn("Type conflict for mbean attr {} in {}", name, oClass);
689                         continue;
690                     }
691                 }
692             }
693         }
694 
695         if (convert)
696         {
697             if (component_type==null)
698             {
699 	        LOG.warn("No mbean type for {} on {}", name, _managed.getClass());
700 		return null;
701 	    }
702 
703             if (component_type.isPrimitive() && !component_type.isArray())
704             {
705 	        LOG.warn("Cannot convert mbean primative {}", name);
706 		return null;
707 	    }
708             LOG.debug("passed convert checks {} for type {}", name, component_type);
709         }
710 
711         try
712         {
713             // Remember the methods
714             _getters.put(name, method);
715             _setters.put(name, setter);
716 
717             MBeanAttributeInfo info=null;
718             if (convert)
719             {
720                 _convert.add(name);
721 
722                 if (component_type.isArray())
723                 {
724                     info= new MBeanAttributeInfo(name,OBJECT_NAME_ARRAY_CLASS,description,true,setter!=null,method.getName().startsWith("is"));
725                 }
726                 else
727                 {
728                     info= new MBeanAttributeInfo(name,OBJECT_NAME_CLASS,description,true,setter!=null,method.getName().startsWith("is"));
729                 }
730             }
731             else
732             {
733                 info= new MBeanAttributeInfo(name,description,method,setter);
734             }
735 
736             _attributes.add(name);
737             
738             return info;
739         }
740         catch (Exception e)
741         {
742             LOG.warn(e);
743             throw new IllegalArgumentException(e.toString());
744         }
745     }
746 
747 
748     /* ------------------------------------------------------------ */
749     /**
750      *  TODO update to new behavior
751      *
752      * Define an operation on the managed object. Defines an operation with parameters. Refection is
753      * used to determine find the method and it's return type. The description of the method is
754      * found with a call to findDescription on "name(signature)". The name and description of each
755      * parameter is found with a call to findDescription with "name(signature)[n]", the returned
756      * description is for the last parameter of the partial signature and is assumed to start with
757      * the parameter name, followed by a colon.
758      *
759      * @param metaData "description" or "impact:description" or "type:impact:description", type is
760      * the "Object","MBean", "MMBean" or "MObject" to indicate the method is on the object, the MBean or on the
761      * object but converted to an MBean reference, and impact is either "ACTION","INFO","ACTION_INFO" or "UNKNOWN".
762      */
763     private MBeanOperationInfo defineOperation(Method method, ManagedOperation methodAnnotation)
764     {
765         String description = methodAnnotation.value();
766         boolean onMBean = methodAnnotation.proxied();
767 
768         boolean convert = false;
769 
770         // determine if we should convert
771         Class<?> returnType = method.getReturnType();
772 
773         if ( returnType.isArray() )
774         {
775             LOG.debug("returnType is array, get component type");
776             returnType = returnType.getComponentType();
777         }
778 
779         if ( returnType.isAnnotationPresent(ManagedObject.class))
780         {
781             convert = true;
782         }
783 
784         String impactName = methodAnnotation.impact();
785 
786 
787         LOG.debug("defineOperation {} {}:{}:{}", method.getName(), onMBean, impactName, description);
788 
789         String signature = method.getName();
790 
791         try
792         {
793             // Resolve the impact
794             int impact=MBeanOperationInfo.UNKNOWN;
795             if (impactName==null || impactName.equals("UNKNOWN"))
796                 impact=MBeanOperationInfo.UNKNOWN;
797             else if (impactName.equals("ACTION"))
798                 impact=MBeanOperationInfo.ACTION;
799             else if (impactName.equals("INFO"))
800                 impact=MBeanOperationInfo.INFO;
801             else if (impactName.equals("ACTION_INFO"))
802                 impact=MBeanOperationInfo.ACTION_INFO;
803             else
804                 LOG.warn("Unknown impact '"+impactName+"' for "+signature);
805 
806 
807             Annotation[][] allParameterAnnotations = method.getParameterAnnotations();
808             Class<?>[] methodTypes = method.getParameterTypes();
809             MBeanParameterInfo[] pInfo = new MBeanParameterInfo[allParameterAnnotations.length];
810 
811             for ( int i = 0 ; i < allParameterAnnotations.length ; ++i )
812             {
813                 Annotation[] parameterAnnotations = allParameterAnnotations[i];
814 
815                 for ( Annotation anno : parameterAnnotations )
816                 {
817                     if ( anno instanceof Name )
818                     {
819                         Name nameAnnotation = (Name) anno;
820 
821                         pInfo[i] = new MBeanParameterInfo(nameAnnotation.value(),methodTypes[i].getName(),nameAnnotation.description());
822                     }
823                 }
824             }
825 
826             signature += "(";
827             for ( int i = 0 ; i < methodTypes.length ; ++i )
828             {
829                 signature += methodTypes[i].getName();
830 
831                 if ( i != methodTypes.length - 1 )
832                 {
833                     signature += ",";
834                 }
835             }
836             signature += ")";
837 
838             Class<?> returnClass = method.getReturnType();
839             LOG.debug("Method Cache: " + signature );
840 
841             if ( _methods.containsKey(signature) )
842             {
843                 return null; // we have an operation for this already
844             }
845 
846             _methods.put(signature, method);
847             if (convert)
848                 _convert.add(signature);
849 
850             return new MBeanOperationInfo(method.getName(), description, pInfo, returnClass.isPrimitive() ? TypeUtil.toName(returnClass) : (returnClass.getName()), impact);
851         }
852         catch (Exception e)
853         {
854             LOG.warn("Operation '"+signature+"'", e);
855             throw new IllegalArgumentException(e.toString());
856         }
857 
858     }
859 
860     protected String toVariableName( String methodName )
861     {
862         String variableName = methodName;
863 
864         if ( methodName.startsWith("get") || methodName.startsWith("set") )
865         {
866             variableName = variableName.substring(3);
867         }
868         else if ( methodName.startsWith("is") )
869         {
870             variableName = variableName.substring(2);
871         }
872 
873         variableName = variableName.substring(0,1).toLowerCase(Locale.ENGLISH) + variableName.substring(1);
874 
875         return variableName;
876     }
877     
878     protected boolean isAnnotationPresent(Class<?> clazz, Class<? extends Annotation> annotation)
879     {
880         Class<?> test = clazz;
881         
882         while (test != null )
883         {  
884             if ( test.isAnnotationPresent(annotation))
885             {
886                 return true;
887             }
888             else
889             {
890                 test = test.getSuperclass();
891             }
892         }
893         return false;
894     }
895 }