1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.eclipse.jetty.jmx;
20
21 import java.lang.annotation.Annotation;
22 import java.lang.reflect.Array;
23 import java.lang.reflect.Constructor;
24 import java.lang.reflect.InvocationTargetException;
25 import java.lang.reflect.Method;
26 import java.lang.reflect.Modifier;
27 import java.util.ArrayList;
28 import java.util.Collection;
29 import java.util.HashMap;
30 import java.util.HashSet;
31 import java.util.Iterator;
32 import java.util.List;
33 import java.util.Locale;
34 import java.util.Map;
35 import java.util.Set;
36
37 import javax.management.Attribute;
38 import javax.management.AttributeList;
39 import javax.management.AttributeNotFoundException;
40 import javax.management.DynamicMBean;
41 import javax.management.InvalidAttributeValueException;
42 import javax.management.MBeanAttributeInfo;
43 import javax.management.MBeanConstructorInfo;
44 import javax.management.MBeanException;
45 import javax.management.MBeanInfo;
46 import javax.management.MBeanNotificationInfo;
47 import javax.management.MBeanOperationInfo;
48 import javax.management.MBeanParameterInfo;
49 import javax.management.ObjectName;
50 import javax.management.ReflectionException;
51 import javax.management.modelmbean.ModelMBean;
52
53 import org.eclipse.jetty.util.Loader;
54 import org.eclipse.jetty.util.TypeUtil;
55 import org.eclipse.jetty.util.annotation.ManagedAttribute;
56 import org.eclipse.jetty.util.annotation.ManagedObject;
57 import org.eclipse.jetty.util.annotation.ManagedOperation;
58 import org.eclipse.jetty.util.annotation.Name;
59 import org.eclipse.jetty.util.log.Log;
60 import org.eclipse.jetty.util.log.Logger;
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75 public class ObjectMBean implements DynamicMBean
76 {
77 private static final Logger LOG = Log.getLogger(ObjectMBean.class);
78
79 private static Class<?>[] OBJ_ARG = new Class[]{Object.class};
80
81 protected Object _managed;
82 private MBeanInfo _info;
83 private Map<String, Method> _getters=new HashMap<String, Method>();
84 private Map<String, Method> _setters=new HashMap<String, Method>();
85 private Map<String, Method> _methods=new HashMap<String, Method>();
86
87
88 private Set<String> _attributes = new HashSet<String>();
89
90
91
92 private Set<String> _convert=new HashSet<String>();
93 private ClassLoader _loader;
94 private MBeanContainer _mbeanContainer;
95
96 private static String OBJECT_NAME_CLASS = ObjectName.class.getName();
97 private static String OBJECT_NAME_ARRAY_CLASS = ObjectName[].class.getName();
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118 public static Object mbeanFor(Object o)
119 {
120 try
121 {
122 Class<?> oClass = o.getClass();
123 Object mbean = null;
124
125 while ( mbean == null && oClass != null )
126 {
127 String pName = oClass.getPackage().getName();
128 String cName = oClass.getName().substring(pName.length() + 1);
129 String mName = pName + ".jmx." + cName + "MBean";
130
131 try
132 {
133 Class<?> mClass = (Object.class.equals(oClass))?oClass=ObjectMBean.class:Loader.loadClass(oClass,mName);
134
135 if (LOG.isDebugEnabled())
136 LOG.debug("ObjectMbean: mbeanFor {} mClass={}", o, mClass);
137
138 try
139 {
140 Constructor<?> constructor = mClass.getConstructor(OBJ_ARG);
141 mbean=constructor.newInstance(new Object[]{o});
142 }
143 catch(Exception e)
144 {
145 LOG.ignore(e);
146 if (ModelMBean.class.isAssignableFrom(mClass))
147 {
148 mbean=mClass.newInstance();
149 ((ModelMBean)mbean).setManagedResource(o, "objectReference");
150 }
151 }
152
153 if (LOG.isDebugEnabled())
154 LOG.debug("mbeanFor {} is {}", o, mbean);
155
156 return mbean;
157 }
158 catch (ClassNotFoundException e)
159 {
160
161
162
163
164 if (e.getMessage().contains(mName))
165 LOG.ignore(e);
166 else
167 LOG.warn(e);
168 }
169 catch (Error e)
170 {
171 LOG.warn(e);
172 mbean = null;
173 }
174 catch (Exception e)
175 {
176 LOG.warn(e);
177 mbean = null;
178 }
179
180 oClass = oClass.getSuperclass();
181 }
182 }
183 catch (Exception e)
184 {
185 LOG.ignore(e);
186 }
187
188 return null;
189 }
190
191
192 public ObjectMBean(Object managedObject)
193 {
194 _managed = managedObject;
195 _loader = Thread.currentThread().getContextClassLoader();
196 }
197
198 public Object getManagedObject()
199 {
200 return _managed;
201 }
202
203 public ObjectName getObjectName()
204 {
205 return null;
206 }
207
208 public String getObjectContextBasis()
209 {
210 return null;
211 }
212
213 public String getObjectNameBasis()
214 {
215 return null;
216 }
217
218 protected void setMBeanContainer(MBeanContainer container)
219 {
220 this._mbeanContainer = container;
221 }
222
223 public MBeanContainer getMBeanContainer ()
224 {
225 return this._mbeanContainer;
226 }
227
228
229 public MBeanInfo getMBeanInfo()
230 {
231 try
232 {
233 if (_info==null)
234 {
235
236 String desc=null;
237 List<MBeanAttributeInfo> attributes = new ArrayList<MBeanAttributeInfo>();
238 List<MBeanConstructorInfo> constructors = new ArrayList<MBeanConstructorInfo>();
239 List<MBeanOperationInfo> operations = new ArrayList<MBeanOperationInfo>();
240 List<MBeanNotificationInfo> notifications = new ArrayList<MBeanNotificationInfo>();
241
242
243 Class<?> o_class=_managed.getClass();
244 List<Class<?>> influences = findInfluences(new ArrayList<Class<?>>(), _managed.getClass());
245
246 if (LOG.isDebugEnabled())
247 LOG.debug("Influence Count: {}", influences.size() );
248
249
250 ManagedObject primary = o_class.getAnnotation( ManagedObject.class);
251
252 if ( primary != null )
253 {
254 desc = primary.value();
255 }
256 else
257 {
258 if (LOG.isDebugEnabled())
259 LOG.debug("No @ManagedObject declared on {}", _managed.getClass());
260 }
261
262
263
264 for (int i=0;i<influences.size();i++)
265 {
266 Class<?> oClass = influences.get(i);
267
268 ManagedObject typeAnnotation = oClass.getAnnotation( ManagedObject.class );
269
270 if (LOG.isDebugEnabled())
271 LOG.debug("Influenced by: " + oClass.getCanonicalName() );
272
273 if ( typeAnnotation == null )
274 {
275 if (LOG.isDebugEnabled())
276 LOG.debug("Annotations not found for: {}", oClass.getCanonicalName() );
277 continue;
278 }
279
280
281
282 for (Method method : oClass.getDeclaredMethods())
283 {
284 ManagedAttribute methodAttributeAnnotation = method.getAnnotation(ManagedAttribute.class);
285
286 if (methodAttributeAnnotation != null)
287 {
288
289 if (LOG.isDebugEnabled())
290 LOG.debug("Attribute Annotation found for: {}", method.getName());
291 MBeanAttributeInfo mai = defineAttribute(method,methodAttributeAnnotation);
292 if ( mai != null )
293 {
294 attributes.add(mai);
295 }
296 }
297
298 ManagedOperation methodOperationAnnotation = method.getAnnotation(ManagedOperation.class);
299
300 if (methodOperationAnnotation != null)
301 {
302 if (LOG.isDebugEnabled())
303 LOG.debug("Method Annotation found for: {}", method.getName());
304 MBeanOperationInfo oi = defineOperation(method,methodOperationAnnotation);
305 if (oi != null)
306 {
307 operations.add(oi);
308 }
309 }
310 }
311
312 }
313
314 _info = new MBeanInfo(o_class.getName(),
315 desc,
316 (MBeanAttributeInfo[])attributes.toArray(new MBeanAttributeInfo[attributes.size()]),
317 (MBeanConstructorInfo[])constructors.toArray(new MBeanConstructorInfo[constructors.size()]),
318 (MBeanOperationInfo[])operations.toArray(new MBeanOperationInfo[operations.size()]),
319 (MBeanNotificationInfo[])notifications.toArray(new MBeanNotificationInfo[notifications.size()]));
320 }
321 }
322 catch(RuntimeException e)
323 {
324 LOG.warn(e);
325 throw e;
326 }
327 return _info;
328 }
329
330
331
332 public Object getAttribute(String name) throws AttributeNotFoundException, MBeanException, ReflectionException
333 {
334 Method getter = (Method) _getters.get(name);
335 if (getter == null)
336 {
337 throw new AttributeNotFoundException(name);
338 }
339
340 try
341 {
342 Object o = _managed;
343 if (getter.getDeclaringClass().isInstance(this))
344 o = this;
345
346
347 Object r=getter.invoke(o, (java.lang.Object[]) null);
348
349
350 if (r!=null )
351 {
352 if (r.getClass().isArray())
353 {
354 if (r.getClass().getComponentType().isAnnotationPresent(ManagedObject.class))
355 {
356 ObjectName[] on = new ObjectName[Array.getLength(r)];
357 for (int i = 0; i < on.length; i++)
358 {
359 on[i] = _mbeanContainer.findMBean(Array.get(r,i));
360 }
361 r = on;
362 }
363 }
364 else if (r instanceof Collection<?>)
365 {
366 @SuppressWarnings("unchecked")
367 Collection<Object> c = (Collection<Object>)r;
368
369 if (!c.isEmpty() && c.iterator().next().getClass().isAnnotationPresent(ManagedObject.class))
370 {
371
372
373 ObjectName[] on = new ObjectName[c.size()];
374 int i = 0;
375 for (Object obj : c)
376 {
377 on[i++] = _mbeanContainer.findMBean(obj);
378 }
379 r = on;
380 }
381 }
382 else
383 {
384 Class<?> clazz = r.getClass();
385
386 while (clazz != null)
387 {
388 if (clazz.isAnnotationPresent(ManagedObject.class))
389 {
390 ObjectName mbean = _mbeanContainer.findMBean(r);
391
392 if (mbean != null)
393 {
394 return mbean;
395 }
396 else
397 {
398 return null;
399 }
400 }
401 clazz = clazz.getSuperclass();
402 }
403 }
404 }
405
406 return r;
407 }
408 catch (IllegalAccessException e)
409 {
410 LOG.warn(Log.EXCEPTION, e);
411 throw new AttributeNotFoundException(e.toString());
412 }
413 catch (InvocationTargetException e)
414 {
415 LOG.warn(Log.EXCEPTION, e);
416 throw new ReflectionException(new Exception(e.getCause()));
417 }
418 }
419
420
421 public AttributeList getAttributes(String[] names)
422 {
423 AttributeList results = new AttributeList(names.length);
424 for (int i = 0; i < names.length; i++)
425 {
426 try
427 {
428 results.add(new Attribute(names[i], getAttribute(names[i])));
429 }
430 catch (Exception e)
431 {
432 LOG.warn(Log.EXCEPTION, e);
433 }
434 }
435 return results;
436 }
437
438
439 public void setAttribute(Attribute attr) throws AttributeNotFoundException, InvalidAttributeValueException, MBeanException, ReflectionException
440 {
441 if (attr == null)
442 return;
443
444 if (LOG.isDebugEnabled())
445 LOG.debug("setAttribute " + _managed + ":" +attr.getName() + "=" + attr.getValue());
446 Method setter = (Method) _setters.get(attr.getName());
447 if (setter == null)
448 throw new AttributeNotFoundException(attr.getName());
449 try
450 {
451 Object o = _managed;
452 if (setter.getDeclaringClass().isInstance(this))
453 o = this;
454
455
456 Object value = attr.getValue();
457
458
459 if (value!=null && _convert.contains(attr.getName()))
460 {
461 if (value.getClass().isArray())
462 {
463 Class<?> t=setter.getParameterTypes()[0].getComponentType();
464 Object na = Array.newInstance(t,Array.getLength(value));
465 for (int i=Array.getLength(value);i-->0;)
466 Array.set(na, i, _mbeanContainer.findBean((ObjectName)Array.get(value, i)));
467 value=na;
468 }
469 else
470 value=_mbeanContainer.findBean((ObjectName)value);
471 }
472
473
474 setter.invoke(o, new Object[]{ value });
475 }
476 catch (IllegalAccessException e)
477 {
478 LOG.warn(Log.EXCEPTION, e);
479 throw new AttributeNotFoundException(e.toString());
480 }
481 catch (InvocationTargetException e)
482 {
483 LOG.warn(Log.EXCEPTION, e);
484 throw new ReflectionException(new Exception(e.getCause()));
485 }
486 }
487
488
489 public AttributeList setAttributes(AttributeList attrs)
490 {
491 if (LOG.isDebugEnabled())
492 LOG.debug("setAttributes");
493
494 AttributeList results = new AttributeList(attrs.size());
495 Iterator<Object> iter = attrs.iterator();
496 while (iter.hasNext())
497 {
498 try
499 {
500 Attribute attr = (Attribute) iter.next();
501 setAttribute(attr);
502 results.add(new Attribute(attr.getName(), getAttribute(attr.getName())));
503 }
504 catch (Exception e)
505 {
506 LOG.warn(Log.EXCEPTION, e);
507 }
508 }
509 return results;
510 }
511
512
513 public Object invoke(String name, Object[] params, String[] signature) throws MBeanException, ReflectionException
514 {
515 if (LOG.isDebugEnabled())
516 LOG.debug("ObjectMBean:invoke " + name);
517
518 String methodKey = name + "(";
519 if (signature != null)
520 for (int i = 0; i < signature.length; i++)
521 methodKey += (i > 0 ? "," : "") + signature[i];
522 methodKey += ")";
523
524 ClassLoader old_loader=Thread.currentThread().getContextClassLoader();
525 try
526 {
527 Thread.currentThread().setContextClassLoader(_loader);
528 Method method = (Method) _methods.get(methodKey);
529 if (method == null)
530 throw new NoSuchMethodException(methodKey);
531
532 Object o = _managed;
533
534 if (method.getDeclaringClass().isInstance(this))
535 {
536 o = this;
537 }
538 return method.invoke(o, params);
539 }
540 catch (NoSuchMethodException e)
541 {
542 LOG.warn(Log.EXCEPTION, e);
543 throw new ReflectionException(e);
544 }
545 catch (IllegalAccessException e)
546 {
547 LOG.warn(Log.EXCEPTION, e);
548 throw new MBeanException(e);
549 }
550 catch (InvocationTargetException e)
551 {
552 LOG.warn(Log.EXCEPTION, e);
553 throw new ReflectionException(new Exception(e.getCause()));
554 }
555 finally
556 {
557 Thread.currentThread().setContextClassLoader(old_loader);
558 }
559 }
560
561 private static List<Class<?>> findInfluences(List<Class<?>> influences, Class<?> aClass)
562 {
563 if (aClass!=null)
564 {
565
566 influences.add(aClass);
567
568 String pName = aClass.getPackage().getName();
569 String cName = aClass.getName().substring(pName.length() + 1);
570 String mName = pName + ".jmx." + cName + "MBean";
571
572 try
573 {
574 Class<?> mbeanClazz = Class.forName(mName);
575 if (LOG.isDebugEnabled())
576 LOG.debug("MBean Influence found for " + aClass.getSimpleName());
577 influences.add(mbeanClazz);
578 }
579 catch (ClassNotFoundException cnfe)
580 {
581 if (LOG.isDebugEnabled())
582 LOG.debug("No MBean Influence for " + aClass.getSimpleName());
583 }
584
585
586 influences=findInfluences(influences,aClass.getSuperclass());
587
588
589 Class<?>[] ifs = aClass.getInterfaces();
590 for (int i=0;ifs!=null && i<ifs.length;i++)
591 influences=findInfluences(influences,ifs[i]);
592 }
593
594 return influences;
595 }
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615 public MBeanAttributeInfo defineAttribute(Method method, ManagedAttribute attributeAnnotation)
616 {
617
618 String name = attributeAnnotation.name();
619
620 if ("".equals(name))
621 {
622 name = toVariableName(method.getName());
623 }
624
625 if ( _attributes.contains(name))
626 {
627 return null;
628 }
629
630 String description = attributeAnnotation.value();
631 boolean readonly = attributeAnnotation.readonly();
632 boolean onMBean = attributeAnnotation.proxied();
633
634 boolean convert = false;
635
636
637 Class<?> return_type = method.getReturnType();
638
639
640 Class<?> component_type = return_type;
641 while ( component_type.isArray() )
642 {
643 component_type = component_type.getComponentType();
644 }
645
646
647 convert = isAnnotationPresent(component_type, ManagedObject.class);
648
649 String uName = name.substring(0, 1).toUpperCase(Locale.ENGLISH) + name.substring(1);
650 Class<?> oClass = onMBean ? this.getClass() : _managed.getClass();
651
652 if (LOG.isDebugEnabled())
653 LOG.debug("defineAttribute {} {}:{}:{}:{}",name,onMBean,readonly,oClass,description);
654
655 Method setter = null;
656
657
658 if (!readonly)
659 {
660 String declaredSetter = attributeAnnotation.setter();
661
662 if (LOG.isDebugEnabled())
663 LOG.debug("DeclaredSetter: {}", declaredSetter);
664
665 Method[] methods = oClass.getMethods();
666 for (int m = 0; m < methods.length; m++)
667 {
668 if ((methods[m].getModifiers() & Modifier.PUBLIC) == 0)
669 continue;
670
671 if (!"".equals(declaredSetter))
672 {
673
674
675 if (methods[m].getName().equals(declaredSetter) && methods[m].getParameterTypes().length == 1)
676 {
677 if (setter != null)
678 {
679 LOG.warn("Multiple setters for mbean attr {} in {}", name, oClass);
680 continue;
681 }
682 setter = methods[m];
683 if ( !component_type.equals(methods[m].getParameterTypes()[0]))
684 {
685 LOG.warn("Type conflict for mbean attr {} in {}", name, oClass);
686 continue;
687 }
688 if (LOG.isDebugEnabled())
689 LOG.debug("Declared Setter: " + declaredSetter);
690 }
691 }
692
693
694 if ( methods[m].getName().equals("set" + uName) && methods[m].getParameterTypes().length == 1)
695 {
696 if (setter != null)
697 {
698 LOG.warn("Multiple setters for mbean attr {} in {}", name, oClass);
699 continue;
700 }
701 setter = methods[m];
702 if ( !return_type.equals(methods[m].getParameterTypes()[0]))
703 {
704 LOG.warn("Type conflict for mbean attr {} in {}", name, oClass);
705 continue;
706 }
707 }
708 }
709 }
710
711 if (convert)
712 {
713 if (component_type==null)
714 {
715 LOG.warn("No mbean type for {} on {}", name, _managed.getClass());
716 return null;
717 }
718
719 if (component_type.isPrimitive() && !component_type.isArray())
720 {
721 LOG.warn("Cannot convert mbean primative {}", name);
722 return null;
723 }
724 if (LOG.isDebugEnabled())
725 LOG.debug("passed convert checks {} for type {}", name, component_type);
726 }
727
728 try
729 {
730
731 _getters.put(name, method);
732 _setters.put(name, setter);
733
734 MBeanAttributeInfo info=null;
735 if (convert)
736 {
737 _convert.add(name);
738
739 if (component_type.isArray())
740 {
741 info= new MBeanAttributeInfo(name,OBJECT_NAME_ARRAY_CLASS,description,true,setter!=null,method.getName().startsWith("is"));
742 }
743 else
744 {
745 info= new MBeanAttributeInfo(name,OBJECT_NAME_CLASS,description,true,setter!=null,method.getName().startsWith("is"));
746 }
747 }
748 else
749 {
750 info= new MBeanAttributeInfo(name,description,method,setter);
751 }
752
753 _attributes.add(name);
754
755 return info;
756 }
757 catch (Exception e)
758 {
759 LOG.warn(e);
760 throw new IllegalArgumentException(e.toString());
761 }
762 }
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780 private MBeanOperationInfo defineOperation(Method method, ManagedOperation methodAnnotation)
781 {
782 String description = methodAnnotation.value();
783 boolean onMBean = methodAnnotation.proxied();
784
785 boolean convert = false;
786
787
788 Class<?> returnType = method.getReturnType();
789
790 if ( returnType.isArray() )
791 {
792 if (LOG.isDebugEnabled())
793 LOG.debug("returnType is array, get component type");
794 returnType = returnType.getComponentType();
795 }
796
797 if ( returnType.isAnnotationPresent(ManagedObject.class))
798 {
799 convert = true;
800 }
801
802 String impactName = methodAnnotation.impact();
803
804 if (LOG.isDebugEnabled())
805 LOG.debug("defineOperation {} {}:{}:{}", method.getName(), onMBean, impactName, description);
806
807 String signature = method.getName();
808
809 try
810 {
811
812 int impact=MBeanOperationInfo.UNKNOWN;
813 if (impactName==null || impactName.equals("UNKNOWN"))
814 impact=MBeanOperationInfo.UNKNOWN;
815 else if (impactName.equals("ACTION"))
816 impact=MBeanOperationInfo.ACTION;
817 else if (impactName.equals("INFO"))
818 impact=MBeanOperationInfo.INFO;
819 else if (impactName.equals("ACTION_INFO"))
820 impact=MBeanOperationInfo.ACTION_INFO;
821 else
822 LOG.warn("Unknown impact '"+impactName+"' for "+signature);
823
824
825 Annotation[][] allParameterAnnotations = method.getParameterAnnotations();
826 Class<?>[] methodTypes = method.getParameterTypes();
827 MBeanParameterInfo[] pInfo = new MBeanParameterInfo[allParameterAnnotations.length];
828
829 for ( int i = 0 ; i < allParameterAnnotations.length ; ++i )
830 {
831 Annotation[] parameterAnnotations = allParameterAnnotations[i];
832
833 for ( Annotation anno : parameterAnnotations )
834 {
835 if ( anno instanceof Name )
836 {
837 Name nameAnnotation = (Name) anno;
838
839 pInfo[i] = new MBeanParameterInfo(nameAnnotation.value(),methodTypes[i].getName(),nameAnnotation.description());
840 }
841 }
842 }
843
844 signature += "(";
845 for ( int i = 0 ; i < methodTypes.length ; ++i )
846 {
847 signature += methodTypes[i].getName();
848
849 if ( i != methodTypes.length - 1 )
850 {
851 signature += ",";
852 }
853 }
854 signature += ")";
855
856 Class<?> returnClass = method.getReturnType();
857
858 if (LOG.isDebugEnabled())
859 LOG.debug("Method Cache: " + signature );
860
861 if ( _methods.containsKey(signature) )
862 {
863 return null;
864 }
865
866 _methods.put(signature, method);
867 if (convert)
868 _convert.add(signature);
869
870 return new MBeanOperationInfo(method.getName(), description, pInfo, returnClass.isPrimitive() ? TypeUtil.toName(returnClass) : (returnClass.getName()), impact);
871 }
872 catch (Exception e)
873 {
874 LOG.warn("Operation '"+signature+"'", e);
875 throw new IllegalArgumentException(e.toString());
876 }
877
878 }
879
880 protected String toVariableName( String methodName )
881 {
882 String variableName = methodName;
883
884 if ( methodName.startsWith("get") || methodName.startsWith("set") )
885 {
886 variableName = variableName.substring(3);
887 }
888 else if ( methodName.startsWith("is") )
889 {
890 variableName = variableName.substring(2);
891 }
892
893 variableName = variableName.substring(0,1).toLowerCase(Locale.ENGLISH) + variableName.substring(1);
894
895 return variableName;
896 }
897
898 protected boolean isAnnotationPresent(Class<?> clazz, Class<? extends Annotation> annotation)
899 {
900 Class<?> test = clazz;
901
902 while (test != null )
903 {
904 if ( test.isAnnotationPresent(annotation))
905 {
906 return true;
907 }
908 else
909 {
910 test = test.getSuperclass();
911 }
912 }
913 return false;
914 }
915 }