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.util.LazyList;
50 import org.mortbay.util.Loader;
51 import org.mortbay.util.TypeUtil;
52
53
54
55
56
57
58
59
60
61
62
63
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
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
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 + ".management." + 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
208 String desc=null;
209 Object attributes=null;
210 Object constructors=null;
211 Object operations=null;
212 Object notifications=null;
213
214
215 Class o_class=_managed.getClass();
216 Object influences = findInfluences(null, _managed.getClass());
217
218
219 Set defined=new HashSet();
220
221
222 for (int i=0;i<LazyList.size(influences);i++)
223 {
224 Class oClass = (Class)LazyList.get(influences, i);
225
226
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('.', '/') + "/management/" + cName+"-mbean";
232
233 try
234 {
235 Log.debug(rName);
236 ResourceBundle bundle = Loader.getResourceBundle(o_class, rName,true,Locale.getDefault());
237
238
239
240 Enumeration e = bundle.getKeys();
241 while (e.hasMoreElements())
242 {
243 String key = (String)e.nextElement();
244 String value = bundle.getString(key);
245
246
247 if (key.equals(cName))
248 {
249
250 if (desc==null)
251 desc=value;
252 }
253 else if (key.indexOf('(')>0)
254 {
255
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
265 if (!defined.contains(key))
266 {
267 defined.add(key);
268 attributes=LazyList.add(attributes,defineAttribute(key, value));
269 }
270 }
271 }
272
273 }
274 catch(MissingResourceException e)
275 {
276 Log.ignore(e);
277 }
278 }
279
280 _info = new MBeanInfo(o_class.getName(),
281 desc,
282 (MBeanAttributeInfo[])LazyList.toArray(attributes, MBeanAttributeInfo.class),
283 (MBeanConstructorInfo[])LazyList.toArray(constructors, MBeanConstructorInfo.class),
284 (MBeanOperationInfo[])LazyList.toArray(operations, MBeanOperationInfo.class),
285 (MBeanNotificationInfo[])LazyList.toArray(notifications, MBeanNotificationInfo.class));
286 }
287 }
288 catch(RuntimeException e)
289 {
290 Log.warn(e);
291 throw e;
292 }
293 return _info;
294 }
295
296
297
298 public Object getAttribute(String name) throws AttributeNotFoundException, MBeanException, ReflectionException
299 {
300 Method getter = (Method) _getters.get(name);
301 if (getter == null)
302 throw new AttributeNotFoundException(name);
303 try
304 {
305 Object o = _managed;
306 if (getter.getDeclaringClass().isInstance(this))
307 o = this;
308
309
310 Object r=getter.invoke(o, (java.lang.Object[]) null);
311
312
313 if (r!=null && _convert.contains(name))
314 {
315 if (r.getClass().isArray())
316 {
317 ObjectName[] on = new ObjectName[Array.getLength(r)];
318 for (int i=0;i<on.length;i++)
319 on[i]=_mbeanContainer.findMBean(Array.get(r, i));
320 r=on;
321 }
322 else
323 {
324 ObjectName mbean = _mbeanContainer.findMBean(r);
325 if (mbean==null)
326 return null;
327 r=mbean;
328 }
329 }
330 return r;
331 }
332 catch (IllegalAccessException e)
333 {
334 Log.warn(Log.EXCEPTION, e);
335 throw new AttributeNotFoundException(e.toString());
336 }
337 catch (InvocationTargetException e)
338 {
339 Log.warn(Log.EXCEPTION, e);
340 throw new ReflectionException((Exception) e.getTargetException());
341 }
342 }
343
344
345 public AttributeList getAttributes(String[] names)
346 {
347 AttributeList results = new AttributeList(names.length);
348 for (int i = 0; i < names.length; i++)
349 {
350 try
351 {
352 results.add(new Attribute(names[i], getAttribute(names[i])));
353 }
354 catch (Exception e)
355 {
356 Log.warn(Log.EXCEPTION, e);
357 }
358 }
359 return results;
360 }
361
362
363 public void setAttribute(Attribute attr) throws AttributeNotFoundException, InvalidAttributeValueException, MBeanException, ReflectionException
364 {
365 if (attr == null)
366 return;
367
368 if (Log.isDebugEnabled())
369 Log.debug("setAttribute " + _managed + ":" +attr.getName() + "=" + attr.getValue());
370 Method setter = (Method) _setters.get(attr.getName());
371 if (setter == null)
372 throw new AttributeNotFoundException(attr.getName());
373 try
374 {
375 Object o = _managed;
376 if (setter.getDeclaringClass().isInstance(this))
377 o = this;
378
379
380 Object value = attr.getValue();
381
382
383 if (value!=null && _convert.contains(attr.getName()))
384 {
385 if (value.getClass().isArray())
386 {
387 Class t=setter.getParameterTypes()[0].getComponentType();
388 Object na = Array.newInstance(t,Array.getLength(value));
389 for (int i=Array.getLength(value);i-->0;)
390 Array.set(na, i, _mbeanContainer.findBean((ObjectName)Array.get(value, i)));
391 value=na;
392 }
393 else
394 value=_mbeanContainer.findBean((ObjectName)value);
395 }
396
397
398 setter.invoke(o, new Object[]{ value });
399 }
400 catch (IllegalAccessException e)
401 {
402 Log.warn(Log.EXCEPTION, e);
403 throw new AttributeNotFoundException(e.toString());
404 }
405 catch (InvocationTargetException e)
406 {
407 Log.warn(Log.EXCEPTION, e);
408 throw new ReflectionException((Exception) e.getTargetException());
409 }
410 }
411
412
413 public AttributeList setAttributes(AttributeList attrs)
414 {
415 Log.debug("setAttributes");
416
417 AttributeList results = new AttributeList(attrs.size());
418 Iterator iter = attrs.iterator();
419 while (iter.hasNext())
420 {
421 try
422 {
423 Attribute attr = (Attribute) iter.next();
424 setAttribute(attr);
425 results.add(new Attribute(attr.getName(), getAttribute(attr.getName())));
426 }
427 catch (Exception e)
428 {
429 Log.warn(Log.EXCEPTION, e);
430 }
431 }
432 return results;
433 }
434
435
436 public Object invoke(String name, Object[] params, String[] signature) throws MBeanException, ReflectionException
437 {
438 if (Log.isDebugEnabled())
439 Log.debug("invoke " + name);
440
441 String methodKey = name + "(";
442 if (signature != null)
443 for (int i = 0; i < signature.length; i++)
444 methodKey += (i > 0 ? "," : "") + signature[i];
445 methodKey += ")";
446
447 ClassLoader old_loader=Thread.currentThread().getContextClassLoader();
448 try
449 {
450 Thread.currentThread().setContextClassLoader(_loader);
451 Method method = (Method) _methods.get(methodKey);
452 if (method == null)
453 throw new NoSuchMethodException(methodKey);
454
455 Object o = _managed;
456 if (method.getDeclaringClass().isInstance(this))
457 o = this;
458 return method.invoke(o, params);
459 }
460 catch (NoSuchMethodException e)
461 {
462 Log.warn(Log.EXCEPTION, e);
463 throw new ReflectionException(e);
464 }
465 catch (IllegalAccessException e)
466 {
467 Log.warn(Log.EXCEPTION, e);
468 throw new MBeanException(e);
469 }
470 catch (InvocationTargetException e)
471 {
472 Log.warn(Log.EXCEPTION, e);
473 throw new ReflectionException((Exception) e.getTargetException());
474 }
475 finally
476 {
477 Thread.currentThread().setContextClassLoader(old_loader);
478 }
479 }
480
481 private static Object findInfluences(Object influences, Class aClass)
482 {
483 if (aClass!=null)
484 {
485
486 influences=LazyList.add(influences,aClass);
487
488
489 influences=findInfluences(influences,aClass.getSuperclass());
490
491
492 Class[] ifs = aClass.getInterfaces();
493 for (int i=0;ifs!=null && i<ifs.length;i++)
494 influences=findInfluences(influences,ifs[i]);
495 }
496 return influences;
497 }
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515 public MBeanAttributeInfo defineAttribute(String name, String metaData)
516 {
517 String description = "";
518 boolean writable = true;
519 boolean onMBean = false;
520 boolean convert = false;
521
522 if (metaData!= null)
523 {
524 String[] tokens = metaData.split(":", 3);
525 for (int t=0;t<tokens.length-1;t++)
526 {
527 tokens[t]=tokens[t].trim();
528 if ("RO".equals(tokens[t]))
529 writable=false;
530 else
531 {
532 onMBean=("MMBean".equalsIgnoreCase(tokens[t]) || "MBean".equalsIgnoreCase(tokens[t]));
533 convert=("MMBean".equalsIgnoreCase(tokens[t]) || "MObject".equalsIgnoreCase(tokens[t]));
534 }
535 }
536 description=tokens[tokens.length-1];
537 }
538
539
540 String uName = name.substring(0, 1).toUpperCase() + name.substring(1);
541 Class oClass = onMBean ? this.getClass() : _managed.getClass();
542
543 if (Log.isDebugEnabled())
544 Log.debug("defineAttribute "+name+" "+onMBean+":"+writable+":"+oClass+":"+description);
545
546 Class type = null;
547 Method getter = null;
548 Method setter = null;
549 Method[] methods = oClass.getMethods();
550 for (int m = 0; m < methods.length; m++)
551 {
552 if ((methods[m].getModifiers() & Modifier.PUBLIC) == 0)
553 continue;
554
555
556 if (methods[m].getName().equals("get" + uName) && methods[m].getParameterTypes().length == 0)
557 {
558 if (getter != null)
559 throw new IllegalArgumentException("Multiple getters for attr " + name+ " in "+oClass);
560 getter = methods[m];
561 if (type != null && !type.equals(methods[m].getReturnType()))
562 throw new IllegalArgumentException("Type conflict for attr " + name+ " in "+oClass);
563 type = methods[m].getReturnType();
564 }
565
566
567 if (methods[m].getName().equals("is" + uName) && methods[m].getParameterTypes().length == 0)
568 {
569 if (getter != null)
570 throw new IllegalArgumentException("Multiple getters for attr " + name+ " in "+oClass);
571 getter = methods[m];
572 if (type != null && !type.equals(methods[m].getReturnType()))
573 throw new IllegalArgumentException("Type conflict for attr " + name+ " in "+oClass);
574 type = methods[m].getReturnType();
575 }
576
577
578 if (writable && methods[m].getName().equals("set" + uName) && methods[m].getParameterTypes().length == 1)
579 {
580 if (setter != null)
581 throw new IllegalArgumentException("Multiple setters for attr " + name+ " in "+oClass);
582 setter = methods[m];
583 if (type != null && !type.equals(methods[m].getParameterTypes()[0]))
584 throw new IllegalArgumentException("Type conflict for attr " + name+ " in "+oClass);
585 type = methods[m].getParameterTypes()[0];
586 }
587 }
588
589 if (convert && type.isPrimitive() && !type.isArray())
590 throw new IllegalArgumentException("Cannot convert primative " + name);
591
592
593 if (getter == null && setter == null)
594 throw new IllegalArgumentException("No getter or setters found for " + name+ " in "+oClass);
595
596 try
597 {
598
599 _getters.put(name, getter);
600 _setters.put(name, setter);
601
602
603
604 MBeanAttributeInfo info=null;
605 if (convert)
606 {
607 _convert.add(name);
608 if (type.isArray())
609 info= new MBeanAttributeInfo(name,OBJECT_NAME_ARRAY_CLASS,description,getter!=null,setter!=null,getter!=null&&getter.getName().startsWith("is"));
610
611 else
612 info= new MBeanAttributeInfo(name,OBJECT_NAME_CLASS,description,getter!=null,setter!=null,getter!=null&&getter.getName().startsWith("is"));
613 }
614 else
615 info= new MBeanAttributeInfo(name,description,getter,setter);
616
617 return info;
618 }
619 catch (Exception e)
620 {
621 Log.warn(Log.EXCEPTION, e);
622 throw new IllegalArgumentException(e.toString());
623 }
624 }
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640 private MBeanOperationInfo defineOperation(String signature, String metaData, ResourceBundle bundle)
641 {
642 String[] tokens=metaData.split(":",3);
643 int i=tokens.length-1;
644 String description=tokens[i--];
645 String impact_name = i<0?"UNKNOWN":tokens[i--].trim();
646 if (i==0)
647 tokens[0]=tokens[0].trim();
648 boolean onMBean= i==0 && ("MBean".equalsIgnoreCase(tokens[0])||"MMBean".equalsIgnoreCase(tokens[0]));
649 boolean convert= i==0 && ("MObject".equalsIgnoreCase(tokens[0])||"MMBean".equalsIgnoreCase(tokens[0]));
650
651 if (Log.isDebugEnabled())
652 Log.debug("defineOperation "+signature+" "+onMBean+":"+impact_name+":"+description);
653
654 Class oClass = onMBean ? this.getClass() : _managed.getClass();
655
656 try
657 {
658
659 int impact=MBeanOperationInfo.UNKNOWN;
660 if (impact_name==null || impact_name.equals("UNKNOWN"))
661 impact=MBeanOperationInfo.UNKNOWN;
662 else if (impact_name.equals("ACTION"))
663 impact=MBeanOperationInfo.ACTION;
664 else if (impact_name.equals("INFO"))
665 impact=MBeanOperationInfo.INFO;
666 else if (impact_name.equals("ACTION_INFO"))
667 impact=MBeanOperationInfo.ACTION_INFO;
668 else
669 Log.warn("Unknown impact '"+impact_name+"' for "+signature);
670
671
672
673 String[] parts=signature.split("[\\(\\)]");
674 String method_name=parts[0];
675 String arguments=parts.length==2?parts[1]:null;
676 String[] args=arguments==null?new String[0]:arguments.split(" *, *");
677
678
679 Class[] types = new Class[args.length];
680 MBeanParameterInfo[] pInfo = new MBeanParameterInfo[args.length];
681 signature=method_name;
682 for (i = 0; i < args.length; i++)
683 {
684 Class type = TypeUtil.fromName(args[i]);
685 if (type == null)
686 type = Thread.currentThread().getContextClassLoader().loadClass(args[i]);
687 types[i] = type;
688 args[i] = type.isPrimitive() ? TypeUtil.toName(type) : args[i];
689 signature+=(i>0?",":"(")+args[i];
690 }
691 signature+=(i>0?")":"()");
692
693
694 for (i = 0; i < args.length; i++)
695 {
696 String param_desc = bundle.getString(signature + "[" + i + "]");
697 parts=param_desc.split(" *: *",2);
698 if (Log.isDebugEnabled())
699 Log.debug(parts[0]+": "+parts[1]);
700 pInfo[i] = new MBeanParameterInfo(parts[0].trim(), args[i], parts[1].trim());
701 }
702
703
704 Method method = oClass.getMethod(method_name, types);
705 Class returnClass = method.getReturnType();
706 _methods.put(signature, method);
707 if (convert)
708 _convert.add(signature);
709
710 return new MBeanOperationInfo(method_name, description, pInfo, returnClass.isPrimitive() ? TypeUtil.toName(returnClass) : (returnClass.getName()), impact);
711 }
712 catch (Exception e)
713 {
714 Log.warn("Operation '"+signature+"'", e);
715 throw new IllegalArgumentException(e.toString());
716 }
717
718 }
719
720 }