Source code: org/mortbay/util/jmx/ModelMBeanImpl.java
1 // ========================================================================
2 // Copyright (c) 1999 Mort Bay Consulting (Australia) Pty. Ltd.
3 // $Id: ModelMBeanImpl.java,v 1.8 2003/10/05 23:46:29 gregwilkins Exp $
4 // ========================================================================
5
6 package org.mortbay.util.jmx;
7
8 import java.lang.reflect.InvocationTargetException;
9 import java.lang.reflect.Method;
10 import java.lang.reflect.Modifier;
11 import java.util.ArrayList;
12 import java.util.HashMap;
13 import java.util.Iterator;
14 import java.util.Locale;
15 import java.util.Map;
16 import java.util.ResourceBundle;
17
18 import javax.management.Attribute;
19 import javax.management.AttributeChangeNotification;
20 import javax.management.AttributeList;
21 import javax.management.AttributeNotFoundException;
22 import javax.management.InstanceNotFoundException;
23 import javax.management.InvalidAttributeValueException;
24 import javax.management.ListenerNotFoundException;
25 import javax.management.MBeanException;
26 import javax.management.MBeanInfo;
27 import javax.management.MBeanNotificationInfo;
28 import javax.management.MBeanOperationInfo;
29 import javax.management.MBeanParameterInfo;
30 import javax.management.MBeanRegistration;
31 import javax.management.MBeanServer;
32 import javax.management.Notification;
33 import javax.management.NotificationFilter;
34 import javax.management.NotificationListener;
35 import javax.management.ObjectName;
36 import javax.management.ReflectionException;
37 import javax.management.RuntimeOperationsException;
38 import javax.management.modelmbean.InvalidTargetObjectTypeException;
39 import javax.management.modelmbean.ModelMBean;
40 import javax.management.modelmbean.ModelMBeanAttributeInfo;
41 import javax.management.modelmbean.ModelMBeanInfo;
42 import javax.management.modelmbean.ModelMBeanInfoSupport;
43 import javax.management.modelmbean.ModelMBeanNotificationInfo;
44 import javax.management.modelmbean.ModelMBeanOperationInfo;
45
46 import org.apache.commons.logging.Log;
47 import org.apache.commons.logging.LogFactory;
48 import org.mortbay.util.LogSupport;
49 import org.mortbay.util.TypeUtil;
50
51
52 /* ------------------------------------------------------------ */
53 /** Model MBean Implementation.
54 * This implementation of the JMX Model MBean API is designed to allow
55 * easy creation of Model MBeans. From minimal descriptions of
56 * operations and attributes, reflection is used to determine the full
57 * signature and ResourceBundles are used to determine other meta data.
58 *
59 * This class is normally used in one of the following patterns:<UL>
60 * <LI>As a base class for a real MBean that contains the actual
61 * attributes and operations of the MBean. Such an Object is only
62 * usable in a JMX environment.
63 * <LI>As a proxy MBean to another non-JMX object. The attributes and
64 * operations of the proxied object are defined in the MBean. This
65 * pattern is used when an existing non-JMX objects API is to be
66 * exposed as an MBean.
67 * <LI>As a base class for a proxy MBean. The attributes and oepration
68 * of the MBean are implemented by the derived class but delegate to
69 * one or more other objects. This pattern is used if existing objects
70 * are to be managed by JMX, but a new management API needs to be
71 * defined.
72 * </UL>
73 *
74 * @version $Revision: 1.8 $
75 * @author Greg Wilkins (gregw)
76 */
77 public class ModelMBeanImpl
78 implements ModelMBean,
79 MBeanRegistration
80 {
81 private static Log log = LogFactory.getLog(ModelMBeanImpl.class);
82
83 public final static int IMPACT_ACTION = MBeanOperationInfo.ACTION;
84 public final static int IMPACT_ACTION_INFO = MBeanOperationInfo.ACTION_INFO;
85 public final static int IMPACT_INFO = MBeanOperationInfo.INFO;
86 public final static int IMPACT_UNKOWN = MBeanOperationInfo.UNKNOWN;
87
88 public final static String STRING="java.lang.String";
89 public final static String OBJECT="java.lang.Object";
90 public final static String INT="int";
91
92 public final static String[] NO_PARAMS=new String[0];
93
94 public final static boolean READ_WRITE=true;
95 public final static boolean READ_ONLY=false;
96 public final static boolean ON_MBEAN=true;
97 public final static boolean ON_OBJECT=false;
98
99
100 private static HashMap __objectId = new HashMap();
101
102 private static String __defaultDomain="org.mortbay";
103
104 protected ModelMBeanInfoSupport _beanInfo;
105 private MBeanServer _mBeanServer;
106 private Object _object;
107 private ObjectName _objectName;
108
109 private boolean _dirty=false;
110 private HashMap _getter = new HashMap(4);
111 private HashMap _setter = new HashMap(4);
112 private HashMap _method = new HashMap(4);
113 private ArrayList _attributes = new ArrayList(4);
114 private ArrayList _operations = new ArrayList(4);
115 private ArrayList _notifications = new ArrayList(4);
116 private String _baseObjectName=null;
117 private Map _components = new HashMap(4);
118
119 /* ------------------------------------------------------------ */
120 /* ------------------------------------------------------------ */
121 /** Create MBean for Object.
122 * Attempts to create an MBean for the object by searching the
123 * package and class name space. For example an object of the
124 * type <PRE>
125 * class com.acme.MyClass extends com.acme.util.BaseClass
126 * </PRE>
127 * Then this method would look for the following
128 * classes:<UL>
129 * <LI>com.acme.MyClassMBean
130 * <LI>com.acme.jmx.MyClassMBean
131 * <LI>com.acme.util.BaseClassMBean
132 * <LI>com.acme.util.jmx.BaseClassMBean
133 * </UL>
134 * @param o The object
135 * @return A new instance of an MBean for the object or null.
136 */
137 public static ModelMBean mbeanFor(Object o)
138 {
139 try
140 {
141 Class oClass = o.getClass();
142 ClassLoader loader =oClass.getClassLoader();
143
144 ModelMBean mbean = null;
145 boolean jmx=false;
146 Class[] interfaces=null;
147 int i=0;
148
149 while (mbean==null && oClass!=null)
150 {
151 Class focus=interfaces==null?oClass:interfaces[i];
152 String pName = focus.getPackage().getName();
153 String cName = focus.getName().substring(pName.length()+1);
154 String mName=pName+(jmx?".jmx.":".")+cName+"MBean";
155
156 try{
157 Class mClass=loader.loadClass(mName);
158 if(LogSupport.isTraceEnabled(log))log.trace("mbeanFor "+o+" mClass="+mClass);
159 mbean=(ModelMBean)mClass.newInstance();
160 mbean.setManagedResource(o,"objectReference");
161 if(log.isDebugEnabled())log.debug("mbeanFor "+o+" is "+mbean);
162 return mbean;
163 }
164 catch(ClassNotFoundException e)
165 {
166 if (e.toString().endsWith("MBean"))
167 { if(LogSupport.isTraceEnabled(log))log.trace(e.toString());}
168 else
169 log.warn(LogSupport.EXCEPTION,e);
170 }
171 catch(Error e)
172 {
173 log.warn(LogSupport.EXCEPTION,e);
174 mbean=null;
175 }
176 catch(Exception e)
177 {
178 log.warn(LogSupport.EXCEPTION,e);
179 mbean=null;
180 }
181
182 if (jmx)
183 {
184 if (interfaces!=null)
185 {
186 i++;
187 if (i>=interfaces.length)
188 {
189 interfaces=null;
190 oClass=oClass.getSuperclass();
191 }
192 }
193 else
194 {
195 interfaces=oClass.getInterfaces();
196 i=0;
197 if (interfaces==null || interfaces.length==0)
198 {
199 interfaces=null;
200 oClass=oClass.getSuperclass();
201 }
202 }
203 }
204 jmx=!jmx;
205 }
206 }
207 catch(Exception e)
208 {
209 LogSupport.ignore(log,e);
210 }
211 return null;
212 }
213
214 /* ------------------------------------------------------------ */
215 /** MBean Constructor.
216 * No proxy object is defined. Attributes and operations are
217 * defined on this instance.
218 */
219 public ModelMBeanImpl()
220 {}
221
222 /* ------------------------------------------------------------ */
223 /** Proxy MBean Constructor.
224 * @param proxyObject The actual object on which attributes and
225 * operations are to be defined and called.
226 */
227 public ModelMBeanImpl(Object proxyObject)
228 {
229 try
230 {
231 setManagedResource(proxyObject,"objectReference");
232 }
233 catch(Exception e)
234 {
235 log.warn(LogSupport.EXCEPTION,e);
236 throw new IllegalArgumentException(e.toString());
237 }
238 }
239
240
241 /* ------------------------------------------------------------ */
242 public static String getDefaultDomain() { return __defaultDomain; }
243
244 /* ------------------------------------------------------------ */
245 public static void setDefaultDomain(String d) { __defaultDomain=d; }
246
247 /* ------------------------------------------------------------ */
248 public MBeanServer getMBeanServer() { return _mBeanServer; }
249
250 /* ------------------------------------------------------------ */
251 public ObjectName getObjectName() { return _objectName; }
252
253 /* ------------------------------------------------------------ */
254 public Object getManagedResource() { return _object; }
255
256 /* ------------------------------------------------------------ */
257 public void setManagedResource(Object proxyObject, String type)
258 throws MBeanException,
259 RuntimeOperationsException,
260 InstanceNotFoundException,
261 InvalidTargetObjectTypeException
262 {
263 if (proxyObject==null)
264 {
265 proxyObject=null;
266 return;
267 }
268
269 log.debug("setManagedResource");
270 if (!"objectreference".equalsIgnoreCase(type))
271 throw new InvalidTargetObjectTypeException(type);
272
273 if (_object==null)
274 {
275 // first set so define attributes etc.
276 _object=proxyObject;
277
278 defineManagedResource();
279 }
280 else
281 _object=proxyObject;
282 }
283
284 /* ------------------------------------------------------------ */
285 /** Define the Managed Resource.
286 * This method is called the first time setManagedResource is
287 * called with a non-null object. It should be implemented by a
288 * derived ModelMBean to define the attributes and operations
289 * after an initial object has been set.
290 */
291 protected void defineManagedResource()
292 {}
293
294 /* ------------------------------------------------------------ */
295 /** Not Supported.
296 * Use RequiredModelMBean for this style of MBean creation.
297 */
298 public void setModelMBeanInfo(ModelMBeanInfo info)
299 throws MBeanException,
300 RuntimeOperationsException
301 {
302 throw new Error("setModelMBeanInfo not supported");
303 }
304
305 /* ------------------------------------------------------------ */
306 /** Define an attribute on the managed object.
307 * The meta data is defined by looking for standard getter and
308 * setter methods. Descriptions are obtained with a call to
309 * findDescription with the attribute name.
310 * @param name The name of the attribute. Normal java bean
311 * capitlization is enforced on this name.
312 */
313 public synchronized void defineAttribute(String name)
314 {
315 defineAttribute(name,true,false);
316 }
317
318 /* ------------------------------------------------------------ */
319 /** Define an attribute on the managed object.
320 * The meta data is defined by looking for standard getter and
321 * setter methods. Descriptions are obtained with a call to
322 * findDescription with the attribute name.
323 * @param name The name of the attribute. Normal java bean
324 * capitlization is enforced on this name.
325 * @param writable If false, do not look for a setter.
326 */
327 public synchronized void defineAttribute(String name, boolean writable)
328 {
329 defineAttribute(name,writable,false);
330 }
331
332 /* ------------------------------------------------------------ */
333 /** Define an attribute on the managed object.
334 * The meta data is defined by looking for standard getter and
335 * setter methods. Descriptions are obtained with a call to
336 * findDescription with the attribute name.
337 * @param name The name of the attribute. Normal java bean
338 * capitlization is enforced on this name.
339 * @param writable If false, do not look for a setter.
340 * @param onMBean .
341 */
342 public synchronized void defineAttribute(String name,
343 boolean writable,
344 boolean onMBean)
345 {
346 _dirty=true;
347
348 String uName=name.substring(0,1).toUpperCase()+name.substring(1);
349 name=java.beans.Introspector.decapitalize(name);
350 Class oClass=onMBean?this.getClass():_object.getClass();
351
352 Class type=null;
353 Method getter=null;
354 Method setter=null;
355 Method[] methods=oClass.getMethods();
356 for (int m=0;m<methods.length;m++)
357 {
358 if ((methods[m].getModifiers()&Modifier.PUBLIC)==0)
359 continue;
360
361 // Look for a getter
362 if (methods[m].getName().equals("get"+uName) &&
363 methods[m].getParameterTypes().length==0)
364 {
365 if (getter!=null)
366 throw new IllegalArgumentException("Multiple getters for attr "+name);
367 getter=methods[m];
368 if (type!=null &&
369 !type.equals(methods[m].getReturnType()))
370 throw new IllegalArgumentException("Type conflict for attr "+name);
371 type=methods[m].getReturnType();
372 }
373
374 // Look for an is getter
375 if (methods[m].getName().equals("is"+uName) &&
376 methods[m].getParameterTypes().length==0)
377 {
378 if (getter!=null)
379 throw new IllegalArgumentException("Multiple getters for attr "+name);
380 getter=methods[m];
381 if (type!=null &&
382 !type.equals(methods[m].getReturnType()))
383 throw new IllegalArgumentException("Type conflict for attr "+name);
384 type=methods[m].getReturnType();
385 }
386
387 // look for a setter
388 if (writable &&
389 methods[m].getName().equals("set"+uName) &&
390 methods[m].getParameterTypes().length==1)
391 {
392 if (setter!=null)
393 throw new IllegalArgumentException("Multiple setters for attr "+name);
394 setter=methods[m];
395 if (type!=null &&
396 !type.equals(methods[m].getParameterTypes()[0]))
397 throw new IllegalArgumentException("Type conflict for attr "+name);
398 type=methods[m].getParameterTypes()[0];
399 }
400 }
401
402 if (getter==null && setter==null)
403 throw new IllegalArgumentException("No getter or setters found for "+name);
404
405 try
406 {
407 // Remember the methods
408 _getter.put(name,getter);
409 _setter.put(name,setter);
410 // create and add the info
411 _attributes.add(new ModelMBeanAttributeInfo(name,
412 findDescription(name),
413 getter,
414 setter));
415 }
416 catch(Exception e)
417 {
418 log.warn(LogSupport.EXCEPTION,e);
419 throw new IllegalArgumentException(e.toString());
420 }
421 }
422
423 /* ------------------------------------------------------------ */
424 /** Define an attribute.
425 * Explicit definition of an attribute. Reflection is used to
426 * locate the actual getter and setter methods.
427 * @param attrInfo ModelMBeanAttributeInfo.
428 */
429 public synchronized void defineAttribute(ModelMBeanAttributeInfo attrInfo)
430 {
431 if (_object==null)
432 throw new IllegalStateException("No Object");
433
434 _dirty=true;
435
436 String name=attrInfo.getName();
437 String uName=name.substring(0,1).toUpperCase()+name.substring(1);
438 Class oClass=_object.getClass();
439
440 try
441 {
442 Class type=TypeUtil.fromName(attrInfo.getType());
443 if (type==null)
444 type=Thread.currentThread().getContextClassLoader().loadClass(attrInfo.getType());
445
446 Method getter=null;
447 Method setter=null;
448
449 if (attrInfo.isReadable())
450 getter=oClass.getMethod((attrInfo.isIs()?"is":"get")+uName,null);
451
452 if (attrInfo.isWritable())
453 setter=oClass.getMethod("set"+uName,new Class[] {type});
454
455 _getter.put(name,getter);
456 _setter.put(name,setter);
457 _attributes.add(attrInfo);
458 }
459 catch(Exception e)
460 {
461 log.warn(LogSupport.EXCEPTION,e);
462 throw new IllegalArgumentException(e.toString());
463 }
464 }
465
466 /* ------------------------------------------------------------ */
467 /** Define an operation on the managed object.
468 * Defines an operation with no parameters. Refection is used to
469 * determine the return type and the description is found with a
470 * call to findDescription on "name()".
471 * @param name Name of the method call
472 * @param impact Impact as defined in MBeanOperationInfo
473 */
474 public synchronized void defineOperation(String name,int impact)
475 {
476 defineOperation(name,null,impact);
477 }
478
479 /* ------------------------------------------------------------ */
480 /** Define an operation on the managed object.
481 * Defines an operation with parameters. Refection is used to
482 * determine find the method and it's return type. The description
483 * of the method is found with a call to findDescription on
484 * "name(signature)". The name and description of each parameter
485 * is found with a call to findDescription with
486 * "name(partialSignature", the returned description is for the
487 * last parameter of the partial signature and is assumed to start
488 * with the parameter name, followed by a colon.
489 * @param name The name of the method call.
490 * @param signature The types of the operation parameters.
491 * @param impact Impact as defined in MBeanOperationInfo
492 */
493 public synchronized void defineOperation(String name,
494 String[] signature,
495 int impact)
496 {
497 _dirty=true;
498 Class oClass=_object.getClass();
499 if (signature==null) signature=new String[0];
500
501 try
502 {
503 Class[] types = new Class[signature.length];
504 MBeanParameterInfo[] pInfo = new
505 MBeanParameterInfo[signature.length];
506
507 // Check types and build methodKey
508 String methodKey=name+"(";
509 for (int i=0;i<signature.length;i++)
510 {
511 Class type=TypeUtil.fromName(signature[i]);
512 if (type==null)
513 type=Thread.currentThread().getContextClassLoader().loadClass(signature[i]);
514 types[i]=type;
515 signature[i]=type.isPrimitive()?TypeUtil.toName(type):signature[i];
516 methodKey+=(i>0?",":"")+signature[i];
517 }
518 methodKey+=")";
519
520 // Build param infos
521 for (int i=0;i<signature.length;i++)
522 {
523 String description=findDescription(methodKey+"["+i+"]");
524 int colon=description.indexOf(":");
525 if (colon<0)
526 {
527 description="param"+i+":"+description;
528 colon=description.indexOf(":");
529 }
530 pInfo[i]=new
531 MBeanParameterInfo(description.substring(0,colon).trim(),
532 signature[i],
533 description.substring(colon+1).trim());
534 }
535
536 // build the operation info
537 Method method=oClass.getMethod(name,types);
538 Class returnClass=method.getReturnType();
539 _method.put(methodKey,method);
540 _operations.add(new ModelMBeanOperationInfo
541 (name,
542 findDescription(methodKey),
543 pInfo,
544 returnClass.isPrimitive()?TypeUtil.toName(returnClass):(returnClass.getName()),
545 impact));
546 }
547 catch(Exception e)
548 {
549 log.warn("operation "+name,e);
550 throw new IllegalArgumentException(e.toString());
551 }
552
553 }
554
555 /* ------------------------------------------------------------ */
556 /** Define an operation.
557 * Explicit definition of an operation. Reflection is used to
558 * locate method called.
559 * @param opInfo
560 */
561 public synchronized void defineOperation(ModelMBeanOperationInfo opInfo)
562 {
563 _dirty=true;
564 Class oClass=_object.getClass();
565
566 try
567 {
568 MBeanParameterInfo[] pInfo = opInfo.getSignature();
569
570 Class[] types = new Class[pInfo.length];
571 String method=opInfo.getName()+"(";
572 for (int i=0;i<pInfo.length;i++)
573 {
574 Class type=TypeUtil.fromName(pInfo[i].getType());
575 if (type==null)
576 type=Thread.currentThread().getContextClassLoader().loadClass(pInfo[i].getType());
577 types[i]=type;
578 method+=(i>0?",":"")+pInfo[i].getType();
579 }
580 method+=")";
581
582 _method.put(method,oClass.getMethod(opInfo.getName(),types));
583 _operations.add(opInfo);
584 }
585 catch(Exception e)
586 {
587 log.warn(LogSupport.EXCEPTION,e);
588 throw new IllegalArgumentException(e.toString());
589 }
590 }
591
592 /* ------------------------------------------------------------ */
593 public synchronized MBeanInfo getMBeanInfo()
594 {
595 log.debug("getMBeanInfo");
596
597 if (_dirty)
598 {
599 _dirty=false;
600 ModelMBeanAttributeInfo[] attributes = (ModelMBeanAttributeInfo[])
601 _attributes.toArray(new ModelMBeanAttributeInfo[0]);
602 ModelMBeanOperationInfo[] operations = (ModelMBeanOperationInfo[])
603 _operations.toArray(new ModelMBeanOperationInfo[0]);
604 ModelMBeanNotificationInfo[] notifications =(ModelMBeanNotificationInfo[])
605 _notifications.toArray(new ModelMBeanNotificationInfo[0]);
606
607 _beanInfo =
608 new ModelMBeanInfoSupport(_object.getClass().getName(),
609 findDescription(null),
610 attributes,
611 null,
612 operations,
613 notifications);
614 }
615
616 return _beanInfo;
617 }
618
619 /* ------------------------------------------------------------ */
620 public Object getAttribute(String name)
621 throws AttributeNotFoundException,
622 MBeanException,
623 ReflectionException
624 {
625 if(log.isDebugEnabled())log.debug("getAttribute "+name);
626 Method getter = (Method)_getter.get(name);
627 if (getter==null)
628 throw new AttributeNotFoundException(name);
629 try
630 {
631 Object o=_object;
632 if (getter.getDeclaringClass().isInstance(this))
633 o=this;
634 return getter.invoke(o,null);
635 }
636 catch(IllegalAccessException e)
637 {
638 log.warn(LogSupport.EXCEPTION,e);
639 throw new AttributeNotFoundException(e.toString());
640 }
641 catch(InvocationTargetException e)
642 {
643 log.warn(LogSupport.EXCEPTION,e);
644 throw new ReflectionException((Exception)e.getTargetException());
645 }
646 }
647
648 /* ------------------------------------------------------------ */
649 public AttributeList getAttributes(String[] names)
650 {
651 log.debug("getAttributes");
652 AttributeList results=new AttributeList(names.length);
653 for (int i=0;i<names.length;i++)
654 {
655 try
656 {
657 results.add(new Attribute(names[i],
658 getAttribute(names[i])));
659 }
660 catch(Exception e)
661 {
662 log.warn(LogSupport.EXCEPTION,e);
663 }
664 }
665 return results;
666 }
667
668
669 /* ------------------------------------------------------------ */
670 public void setAttribute(Attribute attr)
671 throws AttributeNotFoundException,
672 InvalidAttributeValueException,
673 MBeanException,
674 ReflectionException
675 {
676 if(log.isDebugEnabled())log.debug("setAttribute "+attr.getName()+"="+attr.getValue());
677 Method setter = (Method)_setter.get(attr.getName());
678 if (setter==null)
679 throw new AttributeNotFoundException(attr.getName());
680 try
681 {
682 Object o=_object;
683 if (setter.getDeclaringClass().isInstance(this))
684 o=this;
685 setter.invoke(o,new Object[]{attr.getValue()});
686 }
687 catch(IllegalAccessException e)
688 {
689 log.warn(LogSupport.EXCEPTION,e);
690 throw new AttributeNotFoundException(e.toString());
691 }
692 catch(InvocationTargetException e)
693 {
694 log.warn(LogSupport.EXCEPTION,e);
695 throw new ReflectionException((Exception)e.getTargetException());
696 }
697 }
698
699 /* ------------------------------------------------------------ */
700 public AttributeList setAttributes(AttributeList attrs)
701 {
702 log.debug("setAttributes");
703
704 AttributeList results=new AttributeList(attrs.size());
705 Iterator iter = attrs.iterator();
706 while(iter.hasNext())
707 {
708 try
709 {
710 Attribute attr=(Attribute)iter.next();
711 setAttribute(attr);
712 results.add(new Attribute(attr.getName(),
713 getAttribute(attr.getName())));
714 }
715 catch(Exception e)
716 {
717 log.warn(LogSupport.EXCEPTION,e);
718 }
719 }
720 return results;
721 }
722
723 /* ------------------------------------------------------------ */
724 public Object invoke(String name, Object[] params, String[] signature)
725 throws MBeanException,
726 ReflectionException
727 {
728 if(log.isDebugEnabled())log.debug("invoke "+name);
729
730 String methodKey=name+"(";
731 for (int i=0;i<signature.length;i++)
732 methodKey+=(i>0?",":"")+signature[i];
733 methodKey+=")";
734
735 try
736 {
737 Method method = (Method)_method.get(methodKey);
738 if (method==null)
739 throw new NoSuchMethodException(methodKey);
740
741 return method.invoke(_object,params);
742 }
743 catch(NoSuchMethodException e)
744 {
745 log.warn(LogSupport.EXCEPTION,e);
746 throw new ReflectionException(e);
747 }
748 catch(IllegalAccessException e)
749 {
750 log.warn(LogSupport.EXCEPTION,e);
751 throw new MBeanException(e);
752 }
753 catch(InvocationTargetException e)
754 {
755 log.warn(LogSupport.EXCEPTION,e);
756 throw new ReflectionException((Exception)e.getTargetException());
757 }
758
759 }
760
761 /* ------------------------------------------------------------ */
762 public void load()
763 throws MBeanException,
764 RuntimeOperationsException,
765 InstanceNotFoundException
766 {
767 log.debug("load");
768 }
769
770 /* ------------------------------------------------------------ */
771 public void store()
772 throws MBeanException,
773 RuntimeOperationsException,
774 InstanceNotFoundException
775 {
776 log.debug("store");
777 }
778
779 /* ------------------------------------------------------------ */
780 public void addNotificationListener(NotificationListener listener,
781 NotificationFilter filter,
782 Object handback)
783 throws IllegalArgumentException
784 {
785 log.debug("addNotificationListener");
786 }
787
788 /* ------------------------------------------------------------ */
789 public MBeanNotificationInfo[] getNotificationInfo()
790 {
791 log.debug("getNotificationInfo");
792 return null;
793 }
794
795 /* ------------------------------------------------------------ */
796 public void removeNotificationListener(NotificationListener listener)
797 throws ListenerNotFoundException
798 {
799 log.debug("removeNotificationListener");
800 }
801
802 /* ------------------------------------------------------------ */
803 public void addAttributeChangeNotificationListener(NotificationListener listener,
804 String name,
805 Object handback)
806 throws MBeanException,
807 RuntimeOperationsException,
808 IllegalArgumentException
809 {
810 log.debug("addAttributeChangeNotificationListener");
811 }
812
813 /* ------------------------------------------------------------ */
814 public void removeAttributeChangeNotificationListener(NotificationListener listener,
815 String name)
816 throws MBeanException,
817 RuntimeOperationsException,
818 ListenerNotFoundException
819 {
820 log.debug("removeAttributeChangeNotificationListener");
821 }
822
823 /* ------------------------------------------------------------ */
824 public void sendAttributeChangeNotification(Attribute oldAttr,
825 Attribute newAttr)
826 throws MBeanException,
827 RuntimeOperationsException
828 {
829 log.debug("sendAttributeChangeNotification");
830 }
831
832 /* ------------------------------------------------------------ */
833 public void sendAttributeChangeNotification(AttributeChangeNotification notify)
834 throws MBeanException,
835 RuntimeOperationsException
836 {
837 log.debug("sendAttributeChangeNotification");
838 }
839
840 /* ------------------------------------------------------------ */
841 public void sendNotification(String notify)
842 throws MBeanException,
843 RuntimeOperationsException
844 {
845 log.debug("sendNotification");
846 }
847
848 /* ------------------------------------------------------------ */
849 public void sendNotification(Notification notify)
850 throws MBeanException,
851 RuntimeOperationsException
852 {
853 log.debug("sendNotification");
854 }
855
856 /* ------------------------------------------------------------ */
857 /* Find MBean descriptions.
858 * MBean descriptions are searched for in ResourceBundles. Bundles
859 * are looked for in a mbean.property files within each package of
860 * the MBean class inheritance hierachy.
861 * Once a bundle is found, the key is added to object names in the
862 * following order: fully qualied managed resource class name, tail
863 * managed resource class name, tail mbean class name. The string
864 * "MBean" is stripped from the tail of any name.
865 * <P>For example, if the class a.b.C is managed by a MBean
866 * p.q.RMBean which is derived from p.SMBean, then the seach order
867 * for a key x is as follows:<PRE>
868 * bundle: p.q.mbean name: a.b.C.x
869 * bundle: p.q.mbean name: C.x
870 * bundle: p.q.mbean name: R.x
871 * bundle: p.mbean name: a.b.C.x
872 * bundle: p.mbean name: C.x
873 * bundle: p.mbean name: S.x
874 * </PRE>
875 * <P>The convention used for keys passed to this method are:<PRE>
876 * null or empty - Object description
877 * xxx - Attribute xxx description
878 * xxx() - Simple operation xxx description
879 * xxx(type,..) - Operation xxx with signature desciption
880 * xxx(type,..)[n] - Param n of operation xxx description
881 * </PRE>
882 * @param key
883 * @return Description string.
884 */
885 private String findDescription(String key)
886 {
887 Class lookIn = this.getClass();
888
889 // Array of possible objectNames
890 String[] objectNames=new String[3];
891 objectNames[0]=_object.getClass().getName();
892 if (objectNames[0].indexOf(".")>=0)
893 objectNames[1]=objectNames[0].substring(objectNames[0].lastIndexOf(".")+1);
894
895 while(lookIn!=null)
896 {
897 String pkg=lookIn.getName();
898 int lastDot= pkg.lastIndexOf(".");
899 if (lastDot>0)
900 {
901 objectNames[2]=pkg.substring(lastDot+1);
902 pkg=pkg.substring(0,lastDot);
903 }
904 else
905 {
906 objectNames[2]=pkg;
907 pkg=null;
908 }
909
910 String resource=(pkg==null?"mbean":(pkg.replace('.','/')+"/mbean"));
911 if(LogSupport.isTraceEnabled(log))log.trace("Look for: "+resource);
912
913 try
914 {
915 ResourceBundle bundle=
916 ResourceBundle.getBundle(resource,
917 Locale.getDefault(),
918 _object.getClass().getClassLoader());
919
920 if(LogSupport.isTraceEnabled(log))log.trace("Bundle "+resource);
921
922 for (int i=0;i<objectNames.length;i++)
923 {
924 String name=objectNames[i];
925
926 if (name==null)
927 continue;
928 if (name.endsWith("MBean"))
929 name=name.substring(0,name.length()-5);
930 if (key!=null && key.length()>0)
931 name+="."+key;
932
933 try{
934 String description=bundle.getString(name);
935 if (description!=null && description.length()>0)
936 return description;
937 }
938 catch(Exception e) { if(LogSupport.isTraceEnabled(log))log.trace(e.toString()); }
939 }
940 }
941 catch(Exception e) { if(LogSupport.isTraceEnabled(log))log.trace(e.toString()); }
942
943 lookIn=lookIn.getSuperclass();
944 }
945
946 if (key==null || key.length()==0)
947 return objectNames[0];
948
949 return key;
950 }
951
952 /* ------------------------------------------------------------ */
953 /** Create a new ObjectName.
954 * Return a new object name. The default implementation is the
955 * results of uniqueObjectName(baseObjectName), if baseObjectName
956 * is not set, then the results of uniqueObjectName(defaultDomain+":");
957 * @return The Object name
958 */
959 protected ObjectName newObjectName(MBeanServer server)
960 {
961 // Create own ObjectName of the form:
962 // package:class=id
963 if (_baseObjectName!=null)
964 {
965 if (_baseObjectName.indexOf(':')>=0)
966 return uniqueObjectName(server,_baseObjectName);
967 return uniqueObjectName(server,getDefaultDomain()+":"+
968 _baseObjectName);
969 }
970 return uniqueObjectName(server,getDefaultDomain()+":");
971 }
972
973 /* ------------------------------------------------------------ */
974 public void setBaseObjectName(String s)
975 {
976 _baseObjectName=s;
977 }
978
979 /* ------------------------------------------------------------ */
980 public String getBaseObjectName()
981 {
982 return _baseObjectName;
983 }
984
985 /* ------------------------------------------------------------ */
986 /** Pre registration notification.
987 * If this method is specialized by a derived class that may set
988 * the objectName, then it should call this implementation with
989 * the new objectName.
990 * @param server
991 * @param oName
992 * @return The ObjectName to use.
993 */
994 public synchronized ObjectName preRegister(MBeanServer server, ObjectName oName)
995 {
996 _mBeanServer=server;
997 _objectName=oName;
998 if (_objectName==null)
999 {
1000 try{oName=newObjectName(server);}
1001 catch(Exception e){log.warn(LogSupport.EXCEPTION,e);}
1002 }
1003 if(log.isDebugEnabled())log.debug("preRegister "+_objectName+" -> "+oName);
1004 _objectName=oName;
1005
1006 return _objectName;
1007 }
1008
1009
1010 /* ------------------------------------------------------------ */
1011 public void postRegister(Boolean ok)
1012 {
1013 if (ok.booleanValue())
1014 log.info("Registered "+_objectName);
1015 else
1016 {
1017 _mBeanServer=null;
1018 _objectName=null;
1019 }
1020 }
1021
1022 /* ------------------------------------------------------------ */
1023 public void preDeregister()
1024 {
1025 log.info("Deregister "+_objectName);
1026 getComponentMBeans(null,_components);
1027 _components.clear();
1028 }
1029
1030 /* ------------------------------------------------------------ */
1031 /** Post Deregister.
1032 * This implementation destroys this MBean and it cannot be used again.
1033 */
1034 public void postDeregister()
1035 {
1036 _beanInfo=null;
1037 _mBeanServer=null;
1038 _object=null;
1039 _objectName=null;
1040 if (_getter!=null)
1041 _getter.clear();
1042 _getter=null;
1043 if (_setter!=null)
1044 _setter.clear();
1045 _setter=null;
1046 if (_method!=null)
1047 _method.clear();
1048 _method=null;
1049 if (_attributes!=null)
1050 _attributes.clear();
1051 _attributes=null;
1052 if (_operations!=null)
1053 _operations.clear();
1054 _operations=null;
1055 if (_notifications!=null)
1056 _notifications.clear();
1057 _notifications=null;
1058 }
1059
1060 /* ------------------------------------------------------------ */
1061 /** Add an id clause to a JMX object name.
1062 * Used to make unique objectnames when there are no other
1063 * distinguishing attributes.
1064 * If the passed object name ends with '=', just a unique ID is
1065 * added. Otherwise and classname= clause is added.
1066 * @param objectName
1067 * @return objectName with id= class.
1068 */
1069 public synchronized ObjectName uniqueObjectName(MBeanServer server,
1070 String objectName)
1071 {
1072 return uniqueObjectName(server,_object,objectName);
1073 }
1074
1075 /* ------------------------------------------------------------ */
1076 public synchronized ObjectName uniqueObjectName(MBeanServer server,
1077 Object object,
1078 String objectName)
1079 {
1080 if (!objectName.endsWith("="))
1081 {
1082 String className = object.getClass().getName();
1083 if (className.indexOf(".")>0)
1084 className=className.substring(className.lastIndexOf(".")+1);
1085 if (className.endsWith("MBean"))
1086 className=className.substring(0,className.length()-5);
1087 if (!objectName.endsWith(":"))
1088 objectName+=",";
1089 objectName+=className+"=";
1090 }
1091
1092 ObjectName oName=null;
1093 try
1094 {
1095 while(true)
1096 {
1097 Integer id=(Integer)__objectId.get(objectName);
1098 if (id==null)
1099 id=new Integer(0);
1100 oName=new ObjectName(objectName+id);
1101 id=new Integer(id.intValue()+1);
1102 __objectId.put(objectName,id);
1103
1104 // If no server, this must be unique
1105 if (server==null)
1106 break;
1107
1108 // Otherwise let's check it is unique
1109 // if not found then it is unique
1110 if (!server.isRegistered(oName))
1111 break;
1112 }
1113 }
1114 catch(Exception e)
1115 {
1116 log.warn(LogSupport.EXCEPTION,e);
1117 }
1118
1119 return oName;
1120 }
1121
1122
1123 /* ------------------------------------------------------------ */
1124 /** Get Component MBeans.
1125 * Creates, registers and deregisters MBeans for an array of components.
1126 * On each call the passed map is used to determine components that have
1127 * already been registers and those that need to be deregistered.
1128 * @param components the components.
1129 * @param map A map of previously registered components to object
1130 * name. If null is passed, a default map for the mbean is used.
1131 * @return An array of ObjectNames for each component.
1132 */
1133 protected ObjectName[] getComponentMBeans(Object[] components, Map map)
1134 {
1135 if (map==null)
1136 map=_components;
1137 ObjectName[] beans=null;
1138 if (components==null)
1139 beans = new ObjectName[0];
1140 else
1141 {
1142 beans = new ObjectName[components==null?0:components.length];
1143
1144 // Add new beans
1145 for (int i=0;i<components.length;i++)
1146 {
1147 ObjectName on = (ObjectName)map.get(components[i]);
1148 if (on==null)
1149 {
1150 ModelMBean mbean = mbeanFor(components[i]);
1151 if (mbean==null)
1152 log.warn("No mbean for "+components[i]);
1153 else
1154 {
1155 try
1156 {
1157 if (mbean instanceof ModelMBeanImpl)
1158 {
1159 ((ModelMBeanImpl)mbean).setBaseObjectName(getObjectName().toString());
1160 on=getMBeanServer().registerMBean(mbean,null).getObjectName();
1161 }
1162 else
1163 {
1164 on=uniqueObjectName(getMBeanServer(),
1165 components[i],
1166 getObjectName().toString());
1167 on=getMBeanServer().registerMBean(mbean,on).getObjectName();
1168 }
1169 map.put(components[i],on);
1170 }
1171 catch (Exception e)
1172 {
1173 log.warn(LogSupport.EXCEPTION,e);
1174 }
1175 }
1176 }
1177 beans[i]=on;
1178 }
1179 }
1180
1181 // Delete old beans
1182 if (components==null || map.size()>components.length)
1183 {
1184 Object[] to_delete=new Object[map.size()-beans.length];
1185 int d=0;
1186 Iterator iter = map.keySet().iterator();
1187 keys:
1188 while(iter.hasNext())
1189 {
1190 Object bean = iter.next();
1191 if (components!=null)
1192 {
1193 for(int i=0;i<components.length;i++)
1194 if (components[i]==bean)
1195 continue keys;
1196 }
1197 to_delete[d++]=bean;
1198 }
1199
1200 for (;d-->0;)
1201 {
1202 try{getMBeanServer().unregisterMBean((ObjectName)map.remove(to_delete[d]));}
1203 catch (Exception e) {log.warn(LogSupport.EXCEPTION,e);}
1204 }
1205 }
1206
1207 return beans;
1208 }
1209}