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