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