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