View Javadoc

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