View Javadoc

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