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