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