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