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