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<?> returnType = method.getReturnType();
626
627 if ( returnType.isArray() )
628 {
629 returnType = returnType.getComponentType();
630 }
631
632
633 convert = isAnnotationPresent(returnType, ManagedObject.class);
634
635 String uName = name.substring(0, 1).toUpperCase(Locale.ENGLISH) + name.substring(1);
636 Class<?> oClass = onMBean ? this.getClass() : _managed.getClass();
637
638 LOG.debug("defineAttribute {} {}:{}:{}:{}",name,onMBean,readonly,oClass,description);
639
640 Class<?> type = null;
641 Method setter = null;
642
643 type = returnType;
644
645
646
647 if (!readonly)
648 {
649 String declaredSetter = attributeAnnotation.setter();
650
651 LOG.debug("DeclaredSetter: {}", declaredSetter);
652 Method[] methods = oClass.getMethods();
653 for (int m = 0; m < methods.length; m++)
654 {
655 if ((methods[m].getModifiers() & Modifier.PUBLIC) == 0)
656 continue;
657
658 if (!"".equals(declaredSetter))
659 {
660
661
662 if (methods[m].getName().equals(declaredSetter) && methods[m].getParameterTypes().length == 1)
663 {
664 if (setter != null)
665 {
666 LOG.warn("Multiple setters for mbean attr {} in {}", name, oClass);
667 continue;
668 }
669 setter = methods[m];
670 if ( !type.equals(methods[m].getParameterTypes()[0]))
671 {
672 LOG.warn("Type conflict for mbean attr {} in {}", name, oClass);
673 continue;
674 }
675 LOG.debug("Declared Setter: " + declaredSetter);
676 }
677 }
678
679
680 if ( methods[m].getName().equals("set" + uName) && methods[m].getParameterTypes().length == 1)
681 {
682 if (setter != null)
683 {
684 LOG.warn("Multiple setters for mbean attr {} in {}", name, oClass);
685 continue;
686 }
687 setter = methods[m];
688 if ( !type.equals(methods[m].getParameterTypes()[0]))
689 {
690 LOG.warn("Type conflict for mbean attr {} in {}", name, oClass);
691 continue;
692 }
693 }
694 }
695 }
696
697 if (convert)
698 {
699 if (type==null)
700 {
701 LOG.warn("No mbean type for {} on {}", name, _managed.getClass());
702 return null;
703 }
704
705 if (type.isPrimitive() && !type.isArray())
706 {
707 LOG.warn("Cannot convert mbean primative {}", name);
708 return null;
709 }
710 LOG.debug("passed convert checks {} for type {}", name, type);
711 }
712
713 try
714 {
715
716 _getters.put(name, method);
717 _setters.put(name, setter);
718
719 MBeanAttributeInfo info=null;
720 if (convert)
721 {
722 _convert.add(name);
723
724 if (type.isArray())
725 {
726 info= new MBeanAttributeInfo(name,OBJECT_NAME_ARRAY_CLASS,description,true,setter!=null,method.getName().startsWith("is"));
727 }
728 else
729 {
730 info= new MBeanAttributeInfo(name,OBJECT_NAME_CLASS,description,true,setter!=null,method.getName().startsWith("is"));
731 }
732 }
733 else
734 {
735 info= new MBeanAttributeInfo(name,description,method,setter);
736 }
737
738 _attributes.add(name);
739
740 return info;
741 }
742 catch (Exception e)
743 {
744 LOG.warn(e);
745 throw new IllegalArgumentException(e.toString());
746 }
747 }
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765 private MBeanOperationInfo defineOperation(Method method, ManagedOperation methodAnnotation)
766 {
767 String description = methodAnnotation.value();
768 boolean onMBean = methodAnnotation.proxied();
769
770 boolean convert = false;
771
772
773 Class<?> returnType = method.getReturnType();
774
775 if ( returnType.isArray() )
776 {
777 LOG.debug("returnType is array, get component type");
778 returnType = returnType.getComponentType();
779 }
780
781 if ( returnType.isAnnotationPresent(ManagedObject.class))
782 {
783 convert = true;
784 }
785
786 String impactName = methodAnnotation.impact();
787
788
789 LOG.debug("defineOperation {} {}:{}:{}", method.getName(), onMBean, impactName, description);
790
791 String signature = method.getName();
792
793 try
794 {
795
796 int impact=MBeanOperationInfo.UNKNOWN;
797 if (impactName==null || impactName.equals("UNKNOWN"))
798 impact=MBeanOperationInfo.UNKNOWN;
799 else if (impactName.equals("ACTION"))
800 impact=MBeanOperationInfo.ACTION;
801 else if (impactName.equals("INFO"))
802 impact=MBeanOperationInfo.INFO;
803 else if (impactName.equals("ACTION_INFO"))
804 impact=MBeanOperationInfo.ACTION_INFO;
805 else
806 LOG.warn("Unknown impact '"+impactName+"' for "+signature);
807
808
809 Annotation[][] allParameterAnnotations = method.getParameterAnnotations();
810 Class<?>[] methodTypes = method.getParameterTypes();
811 MBeanParameterInfo[] pInfo = new MBeanParameterInfo[allParameterAnnotations.length];
812
813 for ( int i = 0 ; i < allParameterAnnotations.length ; ++i )
814 {
815 Annotation[] parameterAnnotations = allParameterAnnotations[i];
816
817 for ( Annotation anno : parameterAnnotations )
818 {
819 if ( anno instanceof Name )
820 {
821 Name nameAnnotation = (Name) anno;
822
823 pInfo[i] = new MBeanParameterInfo(nameAnnotation.value(),methodTypes[i].getName(),nameAnnotation.description());
824 }
825 }
826 }
827
828 signature += "(";
829 for ( int i = 0 ; i < methodTypes.length ; ++i )
830 {
831 signature += methodTypes[i].getName();
832
833 if ( i != methodTypes.length - 1 )
834 {
835 signature += ",";
836 }
837 }
838 signature += ")";
839
840 Class<?> returnClass = method.getReturnType();
841 LOG.debug("Method Cache: " + signature );
842
843 if ( _methods.containsKey(signature) )
844 {
845 return null;
846 }
847
848 _methods.put(signature, method);
849 if (convert)
850 _convert.add(signature);
851
852 return new MBeanOperationInfo(method.getName(), description, pInfo, returnClass.isPrimitive() ? TypeUtil.toName(returnClass) : (returnClass.getName()), impact);
853 }
854 catch (Exception e)
855 {
856 LOG.warn("Operation '"+signature+"'", e);
857 throw new IllegalArgumentException(e.toString());
858 }
859
860 }
861
862 protected String toVariableName( String methodName )
863 {
864 String variableName = methodName;
865
866 if ( methodName.startsWith("get") || methodName.startsWith("set") )
867 {
868 variableName = variableName.substring(3);
869 }
870 else if ( methodName.startsWith("is") )
871 {
872 variableName = variableName.substring(2);
873 }
874
875 variableName = variableName.substring(0,1).toLowerCase(Locale.ENGLISH) + variableName.substring(1);
876
877 return variableName;
878 }
879
880 protected boolean isAnnotationPresent(Class<?> clazz, Class<? extends Annotation> annotation)
881 {
882 Class<?> test = clazz;
883
884 while (test != null )
885 {
886 if ( test.isAnnotationPresent(annotation))
887 {
888 return true;
889 }
890 else
891 {
892 test = test.getSuperclass();
893 }
894 }
895 return false;
896 }
897 }