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