1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 package org.mortbay.management;
16
17 import java.lang.reflect.Array;
18 import java.lang.reflect.Constructor;
19 import java.lang.reflect.InvocationTargetException;
20 import java.lang.reflect.Method;
21 import java.lang.reflect.Modifier;
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.mortbay.log.Log;
49 import org.mortbay.log.StdErrLog;
50 import org.mortbay.util.LazyList;
51 import org.mortbay.util.Loader;
52 import org.mortbay.util.TypeUtil;
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67 public class ObjectMBean implements DynamicMBean
68 {
69 private static Class[] OBJ_ARG = new Class[]{Object.class};
70
71 protected Object _managed;
72 private MBeanInfo _info;
73 private Map _getters=new HashMap();
74 private Map _setters=new HashMap();
75 private Map _methods=new HashMap();
76 private Set _convert=new HashSet();
77 private ClassLoader _loader;
78 private MBeanContainer _mbeanContainer;
79
80 private static String OBJECT_NAME_CLASS = ObjectName.class.getName();
81 private static String OBJECT_NAME_ARRAY_CLASS = ObjectName[].class.getName();
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102 public static Object mbeanFor(Object o)
103 {
104 try
105 {
106 Class oClass = o.getClass();
107 Object mbean = null;
108
109 while (mbean == null && oClass != null)
110 {
111 String pName = oClass.getPackage().getName();
112 String cName = oClass.getName().substring(pName.length() + 1);
113 String mName = pName + ".management." + cName + "MBean";
114
115
116 try
117 {
118 Class mClass = (Object.class.equals(oClass))?oClass=ObjectMBean.class:Loader.loadClass(oClass,mName,true);
119 if (Log.isDebugEnabled())
120 Log.debug("mbeanFor " + o + " mClass=" + mClass);
121
122 try
123 {
124 Constructor constructor = mClass.getConstructor(OBJ_ARG);
125 mbean=constructor.newInstance(new Object[]{o});
126 }
127 catch(Exception e)
128 {
129 Log.ignore(e);
130 if (ModelMBean.class.isAssignableFrom(mClass))
131 {
132 mbean=mClass.newInstance();
133 ((ModelMBean)mbean).setManagedResource(o, "objectReference");
134 }
135 }
136
137 if (Log.isDebugEnabled())
138 Log.debug("mbeanFor " + o + " is " + mbean);
139 return mbean;
140 }
141 catch (ClassNotFoundException e)
142 {
143 if (e.toString().endsWith("MBean"))
144 Log.ignore(e);
145 else
146 Log.warn(e);
147 }
148 catch (Error e)
149 {
150 Log.warn(e);
151 mbean = null;
152 }
153 catch (Exception e)
154 {
155 Log.warn(e);
156 mbean = null;
157 }
158
159 oClass = oClass.getSuperclass();
160 }
161 }
162 catch (Exception e)
163 {
164 Log.ignore(e);
165 }
166 return null;
167 }
168
169
170 public ObjectMBean(Object managedObject)
171 {
172 _managed = managedObject;
173 _loader = Thread.currentThread().getContextClassLoader();
174 }
175
176 public Object getManagedObject()
177 {
178 return _managed;
179 }
180
181 public ObjectName getObjectName()
182 {
183 return null;
184 }
185
186 public String getObjectNameBasis()
187 {
188 return null;
189 }
190
191 protected void setMBeanContainer(MBeanContainer container)
192 {
193 this._mbeanContainer = container;
194 }
195
196 public MBeanContainer getMBeanContainer ()
197 {
198 return this._mbeanContainer;
199 }
200
201
202 public MBeanInfo getMBeanInfo()
203 {
204 try
205 {
206 if (_info==null)
207 {
208
209 String desc=null;
210 Object attributes=null;
211 Object constructors=null;
212 Object operations=null;
213 Object notifications=null;
214
215
216 Class o_class=_managed.getClass();
217 Object influences = findInfluences(null, _managed.getClass());
218
219
220 Set defined=new HashSet();
221
222
223 for (int i=0;i<LazyList.size(influences);i++)
224 {
225 Class oClass = (Class)LazyList.get(influences, i);
226
227
228 if (Object.class.equals(oClass))
229 oClass=ObjectMBean.class;
230 String pName = oClass.getPackage().getName();
231 String cName = oClass.getName().substring(pName.length() + 1);
232 String rName = pName.replace('.', '/') + "/management/" + cName+"-mbean";
233
234 try
235 {
236 Log.debug(rName);
237 ResourceBundle bundle = Loader.getResourceBundle(o_class, rName,true,Locale.getDefault());
238
239
240
241 Enumeration e = bundle.getKeys();
242 while (e.hasMoreElements())
243 {
244 String key = (String)e.nextElement();
245 String value = bundle.getString(key);
246
247
248 if (key.equals(cName))
249 {
250
251 if (desc==null)
252 desc=value;
253 }
254 else if (key.indexOf('(')>0)
255 {
256
257 if (!defined.contains(key) && key.indexOf('[')<0)
258 {
259 defined.add(key);
260 operations=LazyList.add(operations,defineOperation(key, value, bundle));
261 }
262 }
263 else
264 {
265
266 if (!defined.contains(key))
267 {
268 defined.add(key);
269 attributes=LazyList.add(attributes,defineAttribute(key, value));
270 }
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;
309
310
311 Object r=getter.invoke(o, (java.lang.Object[]) null);
312
313
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
324 {
325 ObjectName mbean = _mbeanContainer.findMBean(r);
326 if (mbean==null)
327 return null;
328 r=mbean;
329 }
330 }
331 return r;
332 }
333 catch (IllegalAccessException e)
334 {
335 Log.warn(Log.EXCEPTION, e);
336 throw new AttributeNotFoundException(e.toString());
337 }
338 catch (InvocationTargetException e)
339 {
340 Log.warn(Log.EXCEPTION, e);
341 throw new ReflectionException((Exception) e.getTargetException());
342 }
343 }
344
345
346 public AttributeList getAttributes(String[] names)
347 {
348 AttributeList results = new AttributeList(names.length);
349 for (int i = 0; i < names.length; i++)
350 {
351 try
352 {
353 results.add(new Attribute(names[i], getAttribute(names[i])));
354 }
355 catch (Exception e)
356 {
357 Log.warn(Log.EXCEPTION, e);
358 }
359 }
360 return results;
361 }
362
363
364 public void setAttribute(Attribute attr) throws AttributeNotFoundException, InvalidAttributeValueException, MBeanException, ReflectionException
365 {
366 if (attr == null)
367 return;
368
369 if (Log.isDebugEnabled())
370 Log.debug("setAttribute " + _managed + ":" +attr.getName() + "=" + attr.getValue());
371 Method setter = (Method) _setters.get(attr.getName());
372 if (setter == null)
373 throw new AttributeNotFoundException(attr.getName());
374 try
375 {
376 Object o = _managed;
377 if (setter.getDeclaringClass().isInstance(this))
378 o = this;
379
380
381 Object value = attr.getValue();
382
383
384 if (value!=null && _convert.contains(attr.getName()))
385 {
386 if (value.getClass().isArray())
387 {
388 Class t=setter.getParameterTypes()[0].getComponentType();
389 Object na = Array.newInstance(t,Array.getLength(value));
390 for (int i=Array.getLength(value);i-->0;)
391 Array.set(na, i, _mbeanContainer.findBean((ObjectName)Array.get(value, i)));
392 value=na;
393 }
394 else
395 value=_mbeanContainer.findBean((ObjectName)value);
396 }
397
398
399 setter.invoke(o, new Object[]{ value });
400 }
401 catch (IllegalAccessException e)
402 {
403 Log.warn(Log.EXCEPTION, e);
404 throw new AttributeNotFoundException(e.toString());
405 }
406 catch (InvocationTargetException e)
407 {
408 Log.warn(Log.EXCEPTION, e);
409 throw new ReflectionException((Exception) e.getTargetException());
410 }
411 }
412
413
414 public AttributeList setAttributes(AttributeList attrs)
415 {
416 Log.debug("setAttributes");
417
418 AttributeList results = new AttributeList(attrs.size());
419 Iterator iter = attrs.iterator();
420 while (iter.hasNext())
421 {
422 try
423 {
424 Attribute attr = (Attribute) iter.next();
425 setAttribute(attr);
426 results.add(new Attribute(attr.getName(), getAttribute(attr.getName())));
427 }
428 catch (Exception e)
429 {
430 Log.warn(Log.EXCEPTION, e);
431 }
432 }
433 return results;
434 }
435
436
437 public Object invoke(String name, Object[] params, String[] signature) throws MBeanException, ReflectionException
438 {
439 if (Log.isDebugEnabled())
440 Log.debug("invoke " + name);
441
442 String methodKey = name + "(";
443 if (signature != null)
444 for (int i = 0; i < signature.length; i++)
445 methodKey += (i > 0 ? "," : "") + signature[i];
446 methodKey += ")";
447
448 ClassLoader old_loader=Thread.currentThread().getContextClassLoader();
449 try
450 {
451 Thread.currentThread().setContextClassLoader(_loader);
452 Method method = (Method) _methods.get(methodKey);
453 if (method == null)
454 throw new NoSuchMethodException(methodKey);
455
456 Object o = _managed;
457 if (method.getDeclaringClass().isInstance(this))
458 o = this;
459 return method.invoke(o, params);
460 }
461 catch (NoSuchMethodException e)
462 {
463 Log.warn(Log.EXCEPTION, e);
464 throw new ReflectionException(e);
465 }
466 catch (IllegalAccessException e)
467 {
468 Log.warn(Log.EXCEPTION, e);
469 throw new MBeanException(e);
470 }
471 catch (InvocationTargetException e)
472 {
473 Log.warn(Log.EXCEPTION, e);
474 throw new ReflectionException((Exception) e.getTargetException());
475 }
476 finally
477 {
478 Thread.currentThread().setContextClassLoader(old_loader);
479 }
480 }
481
482 private static Object findInfluences(Object influences, Class aClass)
483 {
484 if (aClass!=null)
485 {
486
487 influences=LazyList.add(influences,aClass);
488
489
490 influences=findInfluences(influences,aClass.getSuperclass());
491
492
493 Class[] ifs = aClass.getInterfaces();
494 for (int i=0;ifs!=null && i<ifs.length;i++)
495 influences=findInfluences(influences,ifs[i]);
496 }
497 return influences;
498 }
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516 public MBeanAttributeInfo defineAttribute(String name, String metaData)
517 {
518 String description = "";
519 boolean writable = true;
520 boolean onMBean = false;
521 boolean convert = false;
522
523 if (metaData!= null)
524 {
525 String[] tokens = metaData.split(":", 3);
526 for (int t=0;t<tokens.length-1;t++)
527 {
528 tokens[t]=tokens[t].trim();
529 if ("RO".equals(tokens[t]))
530 writable=false;
531 else
532 {
533 onMBean=("MMBean".equalsIgnoreCase(tokens[t]) || "MBean".equalsIgnoreCase(tokens[t]));
534 convert=("MMBean".equalsIgnoreCase(tokens[t]) || "MObject".equalsIgnoreCase(tokens[t]));
535 }
536 }
537 description=tokens[tokens.length-1];
538 }
539
540
541 String uName = name.substring(0, 1).toUpperCase() + name.substring(1);
542 Class oClass = onMBean ? this.getClass() : _managed.getClass();
543
544 if (Log.isDebugEnabled())
545 Log.debug("defineAttribute "+name+" "+onMBean+":"+writable+":"+oClass+":"+description);
546
547 Class type = null;
548 Method getter = null;
549 Method setter = null;
550 Method[] methods = oClass.getMethods();
551 for (int m = 0; m < methods.length; m++)
552 {
553 if ((methods[m].getModifiers() & Modifier.PUBLIC) == 0)
554 continue;
555
556
557 if (methods[m].getName().equals("get" + uName) && methods[m].getParameterTypes().length == 0)
558 {
559 if (getter != null)
560 throw new IllegalArgumentException("Multiple getters for attr " + name+ " in "+oClass);
561 getter = methods[m];
562 if (type != null && !type.equals(methods[m].getReturnType()))
563 throw new IllegalArgumentException("Type conflict for attr " + name+ " in "+oClass);
564 type = methods[m].getReturnType();
565 }
566
567
568 if (methods[m].getName().equals("is" + uName) && methods[m].getParameterTypes().length == 0)
569 {
570 if (getter != null)
571 throw new IllegalArgumentException("Multiple getters for attr " + name+ " in "+oClass);
572 getter = methods[m];
573 if (type != null && !type.equals(methods[m].getReturnType()))
574 throw new IllegalArgumentException("Type conflict for attr " + name+ " in "+oClass);
575 type = methods[m].getReturnType();
576 }
577
578
579 if (writable && methods[m].getName().equals("set" + uName) && methods[m].getParameterTypes().length == 1)
580 {
581 if (setter != null)
582 throw new IllegalArgumentException("Multiple setters for attr " + name+ " in "+oClass);
583 setter = methods[m];
584 if (type != null && !type.equals(methods[m].getParameterTypes()[0]))
585 throw new IllegalArgumentException("Type conflict for attr " + name+ " in "+oClass);
586 type = methods[m].getParameterTypes()[0];
587 }
588 }
589
590 if (convert && type.isPrimitive() && !type.isArray())
591 throw new IllegalArgumentException("Cannot convert primative " + name);
592
593
594 if (getter == null && setter == null)
595 throw new IllegalArgumentException("No getter or setters found for " + name+ " in "+oClass);
596
597 try
598 {
599
600 _getters.put(name, getter);
601 _setters.put(name, setter);
602
603
604
605 MBeanAttributeInfo info=null;
606 if (convert)
607 {
608 _convert.add(name);
609 if (type.isArray())
610 info= new MBeanAttributeInfo(name,OBJECT_NAME_ARRAY_CLASS,description,getter!=null,setter!=null,getter!=null&&getter.getName().startsWith("is"));
611
612 else
613 info= new MBeanAttributeInfo(name,OBJECT_NAME_CLASS,description,getter!=null,setter!=null,getter!=null&&getter.getName().startsWith("is"));
614 }
615 else
616 info= new MBeanAttributeInfo(name,description,getter,setter);
617
618 return info;
619 }
620 catch (Exception e)
621 {
622 Log.warn(Log.EXCEPTION, e);
623 throw new IllegalArgumentException(e.toString());
624 }
625 }
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641 private MBeanOperationInfo defineOperation(String signature, String metaData, ResourceBundle bundle)
642 {
643 String[] tokens=metaData.split(":",3);
644 int i=tokens.length-1;
645 String description=tokens[i--];
646 String impact_name = i<0?"UNKNOWN":tokens[i--].trim();
647 if (i==0)
648 tokens[0]=tokens[0].trim();
649 boolean onMBean= i==0 && ("MBean".equalsIgnoreCase(tokens[0])||"MMBean".equalsIgnoreCase(tokens[0]));
650 boolean convert= i==0 && ("MObject".equalsIgnoreCase(tokens[0])||"MMBean".equalsIgnoreCase(tokens[0]));
651
652 if (Log.isDebugEnabled())
653 Log.debug("defineOperation "+signature+" "+onMBean+":"+impact_name+":"+description);
654
655 Class oClass = onMBean ? this.getClass() : _managed.getClass();
656
657 try
658 {
659
660 int impact=MBeanOperationInfo.UNKNOWN;
661 if (impact_name==null || impact_name.equals("UNKNOWN"))
662 impact=MBeanOperationInfo.UNKNOWN;
663 else if (impact_name.equals("ACTION"))
664 impact=MBeanOperationInfo.ACTION;
665 else if (impact_name.equals("INFO"))
666 impact=MBeanOperationInfo.INFO;
667 else if (impact_name.equals("ACTION_INFO"))
668 impact=MBeanOperationInfo.ACTION_INFO;
669 else
670 Log.warn("Unknown impact '"+impact_name+"' for "+signature);
671
672
673
674 String[] parts=signature.split("[\\(\\)]");
675 String method_name=parts[0];
676 String arguments=parts.length==2?parts[1]:null;
677 String[] args=arguments==null?new String[0]:arguments.split(" *, *");
678
679
680 Class[] types = new Class[args.length];
681 MBeanParameterInfo[] pInfo = new MBeanParameterInfo[args.length];
682 signature=method_name;
683 for (i = 0; i < args.length; i++)
684 {
685 Class type = TypeUtil.fromName(args[i]);
686 if (type == null)
687 type = Thread.currentThread().getContextClassLoader().loadClass(args[i]);
688 types[i] = type;
689 args[i] = type.isPrimitive() ? TypeUtil.toName(type) : args[i];
690 signature+=(i>0?",":"(")+args[i];
691 }
692 signature+=(i>0?")":"()");
693
694
695 for (i = 0; i < args.length; i++)
696 {
697 String param_desc = bundle.getString(signature + "[" + i + "]");
698 parts=param_desc.split(" *: *",2);
699 if (Log.isDebugEnabled())
700 Log.debug(parts[0]+": "+parts[1]);
701 pInfo[i] = new MBeanParameterInfo(parts[0].trim(), args[i], parts[1].trim());
702 }
703
704
705 Method method = oClass.getMethod(method_name, types);
706 Class returnClass = method.getReturnType();
707 _methods.put(signature, method);
708 if (convert)
709 _convert.add(signature);
710
711 return new MBeanOperationInfo(method_name, description, pInfo, returnClass.isPrimitive() ? TypeUtil.toName(returnClass) : (returnClass.getName()), impact);
712 }
713 catch (Exception e)
714 {
715 Log.warn("Operation '"+signature+"'", e);
716 throw new IllegalArgumentException(e.toString());
717 }
718
719 }
720
721 }