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