1
2
3
4
5
6
7
8
9
10
11
12
13
14 package org.eclipse.jetty.jmx;
15
16 import java.lang.reflect.Array;
17 import java.lang.reflect.Constructor;
18 import java.lang.reflect.InvocationTargetException;
19 import java.lang.reflect.Method;
20 import java.lang.reflect.Modifier;
21 import java.util.Collection;
22 import java.util.Enumeration;
23 import java.util.HashMap;
24 import java.util.HashSet;
25 import java.util.Iterator;
26 import java.util.Locale;
27 import java.util.Map;
28 import java.util.MissingResourceException;
29 import java.util.ResourceBundle;
30 import java.util.Set;
31
32 import javax.management.Attribute;
33 import javax.management.AttributeList;
34 import javax.management.AttributeNotFoundException;
35 import javax.management.DynamicMBean;
36 import javax.management.InvalidAttributeValueException;
37 import javax.management.MBeanAttributeInfo;
38 import javax.management.MBeanConstructorInfo;
39 import javax.management.MBeanException;
40 import javax.management.MBeanInfo;
41 import javax.management.MBeanNotificationInfo;
42 import javax.management.MBeanOperationInfo;
43 import javax.management.MBeanParameterInfo;
44 import javax.management.ObjectName;
45 import javax.management.ReflectionException;
46 import javax.management.modelmbean.ModelMBean;
47
48 import org.eclipse.jetty.util.LazyList;
49 import org.eclipse.jetty.util.Loader;
50 import org.eclipse.jetty.util.TypeUtil;
51 import org.eclipse.jetty.util.log.Log;
52
53
54
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 + ".jmx." + cName + "MBean";
113
114
115 try
116 {
117 Class mClass = (Object.class.equals(oClass))?oClass=ObjectMBean.class:Loader.loadClass(oClass,mName,true);
118 if (Log.isDebugEnabled())
119 Log.debug("mbeanFor " + o + " mClass=" + mClass);
120
121 try
122 {
123 Constructor constructor = mClass.getConstructor(OBJ_ARG);
124 mbean=constructor.newInstance(new Object[]{o});
125 }
126 catch(Exception e)
127 {
128 Log.ignore(e);
129 if (ModelMBean.class.isAssignableFrom(mClass))
130 {
131 mbean=mClass.newInstance();
132 ((ModelMBean)mbean).setManagedResource(o, "objectReference");
133 }
134 }
135
136 if (Log.isDebugEnabled())
137 Log.debug("mbeanFor " + o + " is " + mbean);
138 return mbean;
139 }
140 catch (ClassNotFoundException e)
141 {
142 if (e.toString().endsWith("MBean"))
143 Log.ignore(e);
144 else
145 Log.warn(e);
146 }
147 catch (Error e)
148 {
149 Log.warn(e);
150 mbean = null;
151 }
152 catch (Exception e)
153 {
154 Log.warn(e);
155 mbean = null;
156 }
157
158 oClass = oClass.getSuperclass();
159 }
160 }
161 catch (Exception e)
162 {
163 Log.ignore(e);
164 }
165 return null;
166 }
167
168
169 public ObjectMBean(Object managedObject)
170 {
171 _managed = managedObject;
172 _loader = Thread.currentThread().getContextClassLoader();
173 }
174
175 public Object getManagedObject()
176 {
177 return _managed;
178 }
179
180 public ObjectName getObjectName()
181 {
182 return null;
183 }
184
185 public String getObjectNameBasis()
186 {
187 return null;
188 }
189
190 protected void setMBeanContainer(MBeanContainer container)
191 {
192 this._mbeanContainer = container;
193 }
194
195 public MBeanContainer getMBeanContainer ()
196 {
197 return this._mbeanContainer;
198 }
199
200
201 public MBeanInfo getMBeanInfo()
202 {
203 try
204 {
205 if (_info==null)
206 {
207
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('.', '/') + "/jmx/" + cName+"-mbean";
232
233 try
234 {
235 Log.debug(rName);
236 ResourceBundle bundle = Loader.getResourceBundle(o_class, rName,true,Locale.getDefault());
237
238
239
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 MBeanAttributeInfo info=defineAttribute(key, value);
269 if (info!=null)
270 attributes=LazyList.add(attributes,info);
271 }
272 }
273 }
274 }
275 catch(MissingResourceException e)
276 {
277 Log.ignore(e);
278 }
279 }
280
281 _info = new MBeanInfo(o_class.getName(),
282 desc,
283 (MBeanAttributeInfo[])LazyList.toArray(attributes, MBeanAttributeInfo.class),
284 (MBeanConstructorInfo[])LazyList.toArray(constructors, MBeanConstructorInfo.class),
285 (MBeanOperationInfo[])LazyList.toArray(operations, MBeanOperationInfo.class),
286 (MBeanNotificationInfo[])LazyList.toArray(notifications, MBeanNotificationInfo.class));
287 }
288 }
289 catch(RuntimeException e)
290 {
291 Log.warn(e);
292 throw e;
293 }
294 return _info;
295 }
296
297
298
299 public Object getAttribute(String name) throws AttributeNotFoundException, MBeanException, ReflectionException
300 {
301 Method getter = (Method) _getters.get(name);
302 if (getter == null)
303 throw new AttributeNotFoundException(name);
304 try
305 {
306 Object o = _managed;
307 if (getter.getDeclaringClass().isInstance(this))
308 o = this;
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 if (r instanceof Collection<?>)
324 {
325 Collection<Object> c = (Collection<Object>)r;
326 ObjectName[] on = new ObjectName[c.size()];
327 int i=0;
328 for (Object obj :c)
329 on[i++]=_mbeanContainer.findMBean(obj);
330 r=on;
331 }
332 else
333 {
334 ObjectName mbean = _mbeanContainer.findMBean(r);
335 if (mbean==null)
336 return null;
337 r=mbean;
338 }
339 }
340 return r;
341 }
342 catch (IllegalAccessException e)
343 {
344 Log.warn(Log.EXCEPTION, e);
345 throw new AttributeNotFoundException(e.toString());
346 }
347 catch (InvocationTargetException e)
348 {
349 Log.warn(Log.EXCEPTION, e);
350 throw new ReflectionException(new Exception(e.getCause()));
351 }
352 }
353
354
355 public AttributeList getAttributes(String[] names)
356 {
357 AttributeList results = new AttributeList(names.length);
358 for (int i = 0; i < names.length; i++)
359 {
360 try
361 {
362 results.add(new Attribute(names[i], getAttribute(names[i])));
363 }
364 catch (Exception e)
365 {
366 Log.warn(Log.EXCEPTION, e);
367 }
368 }
369 return results;
370 }
371
372
373 public void setAttribute(Attribute attr) throws AttributeNotFoundException, InvalidAttributeValueException, MBeanException, ReflectionException
374 {
375 if (attr == null)
376 return;
377
378 if (Log.isDebugEnabled())
379 Log.debug("setAttribute " + _managed + ":" +attr.getName() + "=" + attr.getValue());
380 Method setter = (Method) _setters.get(attr.getName());
381 if (setter == null)
382 throw new AttributeNotFoundException(attr.getName());
383 try
384 {
385 Object o = _managed;
386 if (setter.getDeclaringClass().isInstance(this))
387 o = this;
388
389
390 Object value = attr.getValue();
391
392
393 if (value!=null && _convert.contains(attr.getName()))
394 {
395 if (value.getClass().isArray())
396 {
397 Class t=setter.getParameterTypes()[0].getComponentType();
398 Object na = Array.newInstance(t,Array.getLength(value));
399 for (int i=Array.getLength(value);i-->0;)
400 Array.set(na, i, _mbeanContainer.findBean((ObjectName)Array.get(value, i)));
401 value=na;
402 }
403 else
404 value=_mbeanContainer.findBean((ObjectName)value);
405 }
406
407
408 setter.invoke(o, new Object[]{ value });
409 }
410 catch (IllegalAccessException e)
411 {
412 Log.warn(Log.EXCEPTION, e);
413 throw new AttributeNotFoundException(e.toString());
414 }
415 catch (InvocationTargetException e)
416 {
417 Log.warn(Log.EXCEPTION, e);
418 throw new ReflectionException(new Exception(e.getCause()));
419 }
420 }
421
422
423 public AttributeList setAttributes(AttributeList attrs)
424 {
425 Log.debug("setAttributes");
426
427 AttributeList results = new AttributeList(attrs.size());
428 Iterator iter = attrs.iterator();
429 while (iter.hasNext())
430 {
431 try
432 {
433 Attribute attr = (Attribute) iter.next();
434 setAttribute(attr);
435 results.add(new Attribute(attr.getName(), getAttribute(attr.getName())));
436 }
437 catch (Exception e)
438 {
439 Log.warn(Log.EXCEPTION, e);
440 }
441 }
442 return results;
443 }
444
445
446 public Object invoke(String name, Object[] params, String[] signature) throws MBeanException, ReflectionException
447 {
448 if (Log.isDebugEnabled())
449 Log.debug("invoke " + name);
450
451 String methodKey = name + "(";
452 if (signature != null)
453 for (int i = 0; i < signature.length; i++)
454 methodKey += (i > 0 ? "," : "") + signature[i];
455 methodKey += ")";
456
457 ClassLoader old_loader=Thread.currentThread().getContextClassLoader();
458 try
459 {
460 Thread.currentThread().setContextClassLoader(_loader);
461 Method method = (Method) _methods.get(methodKey);
462 if (method == null)
463 throw new NoSuchMethodException(methodKey);
464
465 Object o = _managed;
466 if (method.getDeclaringClass().isInstance(this))
467 o = this;
468 return method.invoke(o, params);
469 }
470 catch (NoSuchMethodException e)
471 {
472 Log.warn(Log.EXCEPTION, e);
473 throw new ReflectionException(e);
474 }
475 catch (IllegalAccessException e)
476 {
477 Log.warn(Log.EXCEPTION, e);
478 throw new MBeanException(e);
479 }
480 catch (InvocationTargetException e)
481 {
482 Log.warn(Log.EXCEPTION, e);
483 throw new ReflectionException(new Exception(e.getCause()));
484 }
485 finally
486 {
487 Thread.currentThread().setContextClassLoader(old_loader);
488 }
489 }
490
491 private static Object findInfluences(Object influences, Class aClass)
492 {
493 if (aClass!=null)
494 {
495
496 influences=LazyList.add(influences,aClass);
497
498
499 influences=findInfluences(influences,aClass.getSuperclass());
500
501
502 Class[] ifs = aClass.getInterfaces();
503 for (int i=0;ifs!=null && i<ifs.length;i++)
504 influences=findInfluences(influences,ifs[i]);
505 }
506 return influences;
507 }
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525 public MBeanAttributeInfo defineAttribute(String name, String metaData)
526 {
527 String description = "";
528 boolean writable = true;
529 boolean onMBean = false;
530 boolean convert = false;
531
532 if (metaData!= null)
533 {
534 String[] tokens = metaData.split(":", 3);
535 for (int t=0;t<tokens.length-1;t++)
536 {
537 tokens[t]=tokens[t].trim();
538 if ("RO".equals(tokens[t]))
539 writable=false;
540 else
541 {
542 onMBean=("MMBean".equalsIgnoreCase(tokens[t]) || "MBean".equalsIgnoreCase(tokens[t]));
543 convert=("MMBean".equalsIgnoreCase(tokens[t]) || "MObject".equalsIgnoreCase(tokens[t]));
544 }
545 }
546 description=tokens[tokens.length-1];
547 }
548
549
550 String uName = name.substring(0, 1).toUpperCase() + name.substring(1);
551 Class oClass = onMBean ? this.getClass() : _managed.getClass();
552
553 if (Log.isDebugEnabled())
554 Log.debug("defineAttribute "+name+" "+onMBean+":"+writable+":"+oClass+":"+description);
555
556 Class type = null;
557 Method getter = null;
558 Method setter = null;
559 Method[] methods = oClass.getMethods();
560 for (int m = 0; m < methods.length; m++)
561 {
562 if ((methods[m].getModifiers() & Modifier.PUBLIC) == 0)
563 continue;
564
565
566 if (methods[m].getName().equals("get" + uName) && methods[m].getParameterTypes().length == 0)
567 {
568 if (getter != null)
569 {
570 Log.warn("Multiple mbean getters for attr " + name+ " in "+oClass);
571 continue;
572 }
573 getter = methods[m];
574 if (type != null && !type.equals(methods[m].getReturnType()))
575 {
576 Log.warn("Type conflict for mbean attr " + name+ " in "+oClass);
577 continue;
578 }
579 type = methods[m].getReturnType();
580 }
581
582
583 if (methods[m].getName().equals("is" + uName) && methods[m].getParameterTypes().length == 0)
584 {
585 if (getter != null)
586 {
587 Log.warn("Multiple mbean getters for attr " + name+ " in "+oClass);
588 continue;
589 }
590 getter = methods[m];
591 if (type != null && !type.equals(methods[m].getReturnType()))
592 {
593 Log.warn("Type conflict for mbean attr " + name+ " in "+oClass);
594 continue;
595 }
596 type = methods[m].getReturnType();
597 }
598
599
600 if (writable && methods[m].getName().equals("set" + uName) && methods[m].getParameterTypes().length == 1)
601 {
602 if (setter != null)
603 {
604 Log.warn("Multiple setters for mbean attr " + name+ " in "+oClass);
605 continue;
606 }
607 setter = methods[m];
608 if (type != null && !type.equals(methods[m].getParameterTypes()[0]))
609 {
610 Log.warn("Type conflict for mbean attr " + name+ " in "+oClass);
611 continue;
612 }
613 type = methods[m].getParameterTypes()[0];
614 }
615 }
616
617 if (convert)
618 {
619 if (type==null)
620 {
621 Log.warn("No mbean type for " + name+" on "+_managed.getClass());
622 return null;
623 }
624
625 if (type.isPrimitive() && !type.isArray())
626 {
627 Log.warn("Cannot convert mbean primative " + name);
628 return null;
629 }
630 }
631
632 if (getter == null && setter == null)
633 {
634 Log.warn("No mbean getter or setters found for " + name+ " in "+oClass);
635 return null;
636 }
637
638 try
639 {
640
641 _getters.put(name, getter);
642 _setters.put(name, setter);
643
644 MBeanAttributeInfo info=null;
645 if (convert)
646 {
647 _convert.add(name);
648 if (type.isArray())
649 info= new MBeanAttributeInfo(name,OBJECT_NAME_ARRAY_CLASS,description,getter!=null,setter!=null,getter!=null&&getter.getName().startsWith("is"));
650
651 else
652 info= new MBeanAttributeInfo(name,OBJECT_NAME_CLASS,description,getter!=null,setter!=null,getter!=null&&getter.getName().startsWith("is"));
653 }
654 else
655 info= new MBeanAttributeInfo(name,description,getter,setter);
656
657 return info;
658 }
659 catch (Exception e)
660 {
661 Log.warn(name+": "+metaData, e);
662 throw new IllegalArgumentException(e.toString());
663 }
664 }
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680 private MBeanOperationInfo defineOperation(String signature, String metaData, ResourceBundle bundle)
681 {
682 String[] tokens=metaData.split(":",3);
683 int i=tokens.length-1;
684 String description=tokens[i--];
685 String impact_name = i<0?"UNKNOWN":tokens[i--].trim();
686 if (i==0)
687 tokens[0]=tokens[0].trim();
688 boolean onMBean= i==0 && ("MBean".equalsIgnoreCase(tokens[0])||"MMBean".equalsIgnoreCase(tokens[0]));
689 boolean convert= i==0 && ("MObject".equalsIgnoreCase(tokens[0])||"MMBean".equalsIgnoreCase(tokens[0]));
690
691 if (Log.isDebugEnabled())
692 Log.debug("defineOperation "+signature+" "+onMBean+":"+impact_name+":"+description);
693
694 Class oClass = onMBean ? this.getClass() : _managed.getClass();
695
696 try
697 {
698
699 int impact=MBeanOperationInfo.UNKNOWN;
700 if (impact_name==null || impact_name.equals("UNKNOWN"))
701 impact=MBeanOperationInfo.UNKNOWN;
702 else if (impact_name.equals("ACTION"))
703 impact=MBeanOperationInfo.ACTION;
704 else if (impact_name.equals("INFO"))
705 impact=MBeanOperationInfo.INFO;
706 else if (impact_name.equals("ACTION_INFO"))
707 impact=MBeanOperationInfo.ACTION_INFO;
708 else
709 Log.warn("Unknown impact '"+impact_name+"' for "+signature);
710
711
712
713 String[] parts=signature.split("[\\(\\)]");
714 String method_name=parts[0];
715 String arguments=parts.length==2?parts[1]:null;
716 String[] args=arguments==null?new String[0]:arguments.split(" *, *");
717
718
719 Class[] types = new Class[args.length];
720 MBeanParameterInfo[] pInfo = new MBeanParameterInfo[args.length];
721 signature=method_name;
722 for (i = 0; i < args.length; i++)
723 {
724 Class type = TypeUtil.fromName(args[i]);
725 if (type == null)
726 type = Thread.currentThread().getContextClassLoader().loadClass(args[i]);
727 types[i] = type;
728 args[i] = type.isPrimitive() ? TypeUtil.toName(type) : args[i];
729 signature+=(i>0?",":"(")+args[i];
730 }
731 signature+=(i>0?")":"()");
732
733
734 for (i = 0; i < args.length; i++)
735 {
736 String param_desc = bundle.getString(signature + "[" + i + "]");
737 parts=param_desc.split(" *: *",2);
738 if (Log.isDebugEnabled())
739 Log.debug(parts[0]+": "+parts[1]);
740 pInfo[i] = new MBeanParameterInfo(parts[0].trim(), args[i], parts[1].trim());
741 }
742
743
744 Method method = oClass.getMethod(method_name, types);
745 Class returnClass = method.getReturnType();
746 _methods.put(signature, method);
747 if (convert)
748 _convert.add(signature);
749
750 return new MBeanOperationInfo(method_name, description, pInfo, returnClass.isPrimitive() ? TypeUtil.toName(returnClass) : (returnClass.getName()), impact);
751 }
752 catch (Exception e)
753 {
754 Log.warn("Operation '"+signature+"'", e);
755 throw new IllegalArgumentException(e.toString());
756 }
757
758 }
759
760 }