View Javadoc

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