34 import java.lang.reflect.InvocationTargetException; |
34 import java.lang.reflect.InvocationTargetException; |
35 import java.lang.reflect.Method; |
35 import java.lang.reflect.Method; |
36 import java.lang.reflect.Type; |
36 import java.lang.reflect.Type; |
37 import java.util.Arrays; |
37 import java.util.Arrays; |
38 import java.util.List; |
38 import java.util.List; |
|
39 import java.util.Map; |
39 import java.util.WeakHashMap; |
40 import java.util.WeakHashMap; |
|
41 import javax.management.Description; |
40 |
42 |
41 import javax.management.Descriptor; |
43 import javax.management.Descriptor; |
42 import javax.management.ImmutableDescriptor; |
44 import javax.management.ImmutableDescriptor; |
43 import javax.management.IntrospectionException; |
45 import javax.management.IntrospectionException; |
44 import javax.management.InvalidAttributeValueException; |
46 import javax.management.InvalidAttributeValueException; |
|
47 import javax.management.MBean; |
45 import javax.management.MBeanAttributeInfo; |
48 import javax.management.MBeanAttributeInfo; |
46 import javax.management.MBeanConstructorInfo; |
49 import javax.management.MBeanConstructorInfo; |
47 import javax.management.MBeanException; |
50 import javax.management.MBeanException; |
48 import javax.management.MBeanInfo; |
51 import javax.management.MBeanInfo; |
49 import javax.management.MBeanNotificationInfo; |
52 import javax.management.MBeanNotificationInfo; |
50 import javax.management.MBeanOperationInfo; |
53 import javax.management.MBeanOperationInfo; |
|
54 import javax.management.MXBean; |
|
55 import javax.management.ManagedAttribute; |
|
56 import javax.management.ManagedOperation; |
51 import javax.management.NotCompliantMBeanException; |
57 import javax.management.NotCompliantMBeanException; |
52 import javax.management.NotificationBroadcaster; |
58 import javax.management.NotificationBroadcaster; |
|
59 import javax.management.NotificationInfo; |
|
60 import javax.management.NotificationInfos; |
53 import javax.management.ReflectionException; |
61 import javax.management.ReflectionException; |
54 |
62 |
55 /** |
63 /** |
56 * An introspector for MBeans of a certain type. There is one instance |
64 * An introspector for MBeans of a certain type. There is one instance |
57 * of this class for Standard MBeans, and one for every MXBeanMappingFactory; |
65 * of this class for Standard MBeans, and one for every MXBeanMappingFactory; |
151 * may be null. |
159 * may be null. |
152 */ |
160 */ |
153 abstract MBeanAttributeInfo getMBeanAttributeInfo(String attributeName, |
161 abstract MBeanAttributeInfo getMBeanAttributeInfo(String attributeName, |
154 M getter, M setter) throws IntrospectionException; |
162 M getter, M setter) throws IntrospectionException; |
155 |
163 |
|
164 final String getAttributeDescription( |
|
165 String attributeName, String defaultDescription, |
|
166 Method getter, Method setter) throws IntrospectionException { |
|
167 String g = Introspector.descriptionForElement(getter); |
|
168 String s = Introspector.descriptionForElement(setter); |
|
169 if (g == null) { |
|
170 if (s == null) |
|
171 return defaultDescription; |
|
172 else |
|
173 return s; |
|
174 } else if (s == null || g.equals(s)) { |
|
175 return g; |
|
176 } else { |
|
177 throw new IntrospectionException( |
|
178 "Inconsistent @Description on getter and setter for " + |
|
179 "attribute " + attributeName); |
|
180 } |
|
181 } |
|
182 |
156 /** |
183 /** |
157 * Construct an MBeanOperationInfo for the given operation based on |
184 * Construct an MBeanOperationInfo for the given operation based on |
158 * the M it was derived from. |
185 * the M it was derived from. |
159 */ |
186 */ |
160 abstract MBeanOperationInfo getMBeanOperationInfo(String operationName, |
187 abstract MBeanOperationInfo getMBeanOperationInfo(String operationName, |
182 Descriptor getSpecificMBeanDescriptor() { |
209 Descriptor getSpecificMBeanDescriptor() { |
183 return ImmutableDescriptor.EMPTY_DESCRIPTOR; |
210 return ImmutableDescriptor.EMPTY_DESCRIPTOR; |
184 } |
211 } |
185 |
212 |
186 void checkCompliance(Class<?> mbeanType) throws NotCompliantMBeanException { |
213 void checkCompliance(Class<?> mbeanType) throws NotCompliantMBeanException { |
187 if (!mbeanType.isInterface()) { |
214 if (!mbeanType.isInterface() && |
188 throw new NotCompliantMBeanException("Not an interface: " + |
215 !mbeanType.isAnnotationPresent(MBean.class) && |
|
216 !Introspector.hasMXBeanAnnotation(mbeanType)) { |
|
217 throw new NotCompliantMBeanException("Not an interface and " + |
|
218 "does not have @" + MBean.class.getSimpleName() + |
|
219 " or @" + MXBean.class.getSimpleName() + " annotation: " + |
189 mbeanType.getName()); |
220 mbeanType.getName()); |
190 } |
221 } |
191 } |
222 } |
192 |
223 |
193 /** |
224 /** |
194 * Get the methods to be analyzed to build the MBean interface. |
225 * Get the methods to be analyzed to build the MBean interface. |
195 */ |
226 */ |
196 List<Method> getMethods(final Class<?> mbeanType) throws Exception { |
227 List<Method> getMethods(final Class<?> mbeanType) throws Exception { |
197 return Arrays.asList(mbeanType.getMethods()); |
228 if (mbeanType.isInterface()) |
|
229 return Arrays.asList(mbeanType.getMethods()); |
|
230 |
|
231 final List<Method> methods = newList(); |
|
232 getAnnotatedMethods(mbeanType, methods); |
|
233 return methods; |
198 } |
234 } |
199 |
235 |
200 final PerInterface<M> getPerInterface(Class<?> mbeanInterface) |
236 final PerInterface<M> getPerInterface(Class<?> mbeanInterface) |
201 throws NotCompliantMBeanException { |
237 throws NotCompliantMBeanException { |
202 PerInterfaceMap<M> map = getPerInterfaceMap(); |
238 PerInterfaceMap<M> map = getPerInterfaceMap(); |
230 */ |
266 */ |
231 private MBeanInfo makeInterfaceMBeanInfo(Class<?> mbeanInterface, |
267 private MBeanInfo makeInterfaceMBeanInfo(Class<?> mbeanInterface, |
232 MBeanAnalyzer<M> analyzer) throws IntrospectionException { |
268 MBeanAnalyzer<M> analyzer) throws IntrospectionException { |
233 final MBeanInfoMaker maker = new MBeanInfoMaker(); |
269 final MBeanInfoMaker maker = new MBeanInfoMaker(); |
234 analyzer.visit(maker); |
270 analyzer.visit(maker); |
235 final String description = |
271 final String defaultDescription = |
236 "Information on the management interface of the MBean"; |
272 "Information on the management interface of the MBean"; |
|
273 String description = Introspector.descriptionForElement(mbeanInterface); |
|
274 if (description == null) |
|
275 description = defaultDescription; |
237 return maker.makeMBeanInfo(mbeanInterface, description); |
276 return maker.makeMBeanInfo(mbeanInterface, description); |
238 } |
277 } |
239 |
278 |
240 /** True if the given getter and setter are consistent. */ |
279 /** True if the given getter and setter are consistent. */ |
241 final boolean consistent(M getter, M setter) { |
280 final boolean consistent(M getter, M setter) { |
405 */ |
444 */ |
406 final MBeanInfo getMBeanInfo(Object resource, PerInterface<M> perInterface) |
445 final MBeanInfo getMBeanInfo(Object resource, PerInterface<M> perInterface) |
407 throws NotCompliantMBeanException { |
446 throws NotCompliantMBeanException { |
408 MBeanInfo mbi = |
447 MBeanInfo mbi = |
409 getClassMBeanInfo(resource.getClass(), perInterface); |
448 getClassMBeanInfo(resource.getClass(), perInterface); |
410 MBeanNotificationInfo[] notifs = findNotifications(resource); |
449 MBeanNotificationInfo[] notifs; |
|
450 try { |
|
451 notifs = findNotifications(resource); |
|
452 } catch (RuntimeException e) { |
|
453 NotCompliantMBeanException x = |
|
454 new NotCompliantMBeanException(e.getMessage()); |
|
455 x.initCause(e); |
|
456 throw x; |
|
457 } |
411 Descriptor d = getSpecificMBeanDescriptor(); |
458 Descriptor d = getSpecificMBeanDescriptor(); |
412 boolean anyNotifs = (notifs != null && notifs.length > 0); |
459 boolean anyNotifs = (notifs != null && notifs.length > 0); |
413 if (!anyNotifs && ImmutableDescriptor.EMPTY_DESCRIPTOR.equals(d)) |
460 if (!anyNotifs && ImmutableDescriptor.EMPTY_DESCRIPTOR.equals(d)) |
414 return mbi; |
461 return mbi; |
415 else { |
462 else { |
458 } |
505 } |
459 return mbi; |
506 return mbi; |
460 } |
507 } |
461 } |
508 } |
462 |
509 |
|
510 /* |
|
511 * Add to "methods" every public method that has the @ManagedAttribute |
|
512 * or @ManagedOperation annotation, in the given class or any of |
|
513 * its superclasses or superinterfaces. |
|
514 * |
|
515 * We always add superclass or superinterface methods first, so that |
|
516 * the stable sort used by eliminateCovariantMethods will put the |
|
517 * method from the most-derived class last. This means that we will |
|
518 * see the version of the @ManagedAttribute (or ...Operation) annotation |
|
519 * from that method, which might have a different description or whatever. |
|
520 */ |
|
521 private static void getAnnotatedMethods(Class<?> c, List<Method> methods) |
|
522 throws Exception { |
|
523 Class<?> sup = c.getSuperclass(); |
|
524 if (sup != null) |
|
525 getAnnotatedMethods(sup, methods); |
|
526 Class<?>[] intfs = c.getInterfaces(); |
|
527 for (Class<?> intf : intfs) |
|
528 getAnnotatedMethods(intf, methods); |
|
529 for (Method m : c.getMethods()) { |
|
530 // We are careful not to add m if it is inherited from a parent |
|
531 // class or interface, because duplicate methods lead to nasty |
|
532 // behaviour in eliminateCovariantMethods. |
|
533 if (m.getDeclaringClass() == c && |
|
534 (m.isAnnotationPresent(ManagedAttribute.class) || |
|
535 m.isAnnotationPresent(ManagedOperation.class))) |
|
536 methods.add(m); |
|
537 } |
|
538 } |
|
539 |
463 static MBeanNotificationInfo[] findNotifications(Object moi) { |
540 static MBeanNotificationInfo[] findNotifications(Object moi) { |
464 if (!(moi instanceof NotificationBroadcaster)) |
541 if (!(moi instanceof NotificationBroadcaster)) |
465 return null; |
542 return null; |
466 MBeanNotificationInfo[] mbn = |
543 MBeanNotificationInfo[] mbn = |
467 ((NotificationBroadcaster) moi).getNotificationInfo(); |
544 ((NotificationBroadcaster) moi).getNotificationInfo(); |
468 if (mbn == null || mbn.length == 0) |
545 if (mbn == null || mbn.length == 0) |
469 return null; |
546 return findNotificationsFromAnnotations(moi.getClass()); |
470 MBeanNotificationInfo[] result = |
547 MBeanNotificationInfo[] result = |
471 new MBeanNotificationInfo[mbn.length]; |
548 new MBeanNotificationInfo[mbn.length]; |
472 for (int i = 0; i < mbn.length; i++) { |
549 for (int i = 0; i < mbn.length; i++) { |
473 MBeanNotificationInfo ni = mbn[i]; |
550 MBeanNotificationInfo ni = mbn[i]; |
474 if (ni.getClass() != MBeanNotificationInfo.class) |
551 if (ni.getClass() != MBeanNotificationInfo.class) |
476 result[i] = ni; |
553 result[i] = ni; |
477 } |
554 } |
478 return result; |
555 return result; |
479 } |
556 } |
480 |
557 |
|
558 private static MBeanNotificationInfo[] findNotificationsFromAnnotations( |
|
559 Class<?> mbeanClass) { |
|
560 Class<?> c = getAnnotatedNotificationInfoClass(mbeanClass); |
|
561 if (c == null) |
|
562 return null; |
|
563 NotificationInfo ni = c.getAnnotation(NotificationInfo.class); |
|
564 NotificationInfos nis = c.getAnnotation(NotificationInfos.class); |
|
565 List<NotificationInfo> list = newList(); |
|
566 if (ni != null) |
|
567 list.add(ni); |
|
568 if (nis != null) |
|
569 list.addAll(Arrays.asList(nis.value())); |
|
570 if (list.isEmpty()) |
|
571 return null; |
|
572 List<MBeanNotificationInfo> mbnis = newList(); |
|
573 for (NotificationInfo x : list) { |
|
574 // The Descriptor includes any fields explicitly specified by |
|
575 // x.descriptorFields(), plus any fields from the contained |
|
576 // @Description annotation. |
|
577 Descriptor d = new ImmutableDescriptor(x.descriptorFields()); |
|
578 d = ImmutableDescriptor.union( |
|
579 d, Introspector.descriptorForAnnotation(x.description())); |
|
580 MBeanNotificationInfo mbni = new MBeanNotificationInfo( |
|
581 x.types(), x.notificationClass().getName(), |
|
582 x.description().value(), d); |
|
583 mbnis.add(mbni); |
|
584 } |
|
585 return mbnis.toArray(new MBeanNotificationInfo[mbnis.size()]); |
|
586 } |
|
587 |
|
588 private static final Map<Class<?>, WeakReference<Class<?>>> |
|
589 annotatedNotificationInfoClasses = newWeakHashMap(); |
|
590 |
|
591 private static Class<?> getAnnotatedNotificationInfoClass(Class<?> baseClass) { |
|
592 synchronized (annotatedNotificationInfoClasses) { |
|
593 WeakReference<Class<?>> wr = |
|
594 annotatedNotificationInfoClasses.get(baseClass); |
|
595 if (wr != null) |
|
596 return wr.get(); |
|
597 Class<?> c = null; |
|
598 if (baseClass.isAnnotationPresent(NotificationInfo.class) || |
|
599 baseClass.isAnnotationPresent(NotificationInfos.class)) { |
|
600 c = baseClass; |
|
601 } else { |
|
602 Class<?>[] intfs = baseClass.getInterfaces(); |
|
603 for (Class<?> intf : intfs) { |
|
604 Class<?> c1 = getAnnotatedNotificationInfoClass(intf); |
|
605 if (c1 != null) { |
|
606 if (c != null) { |
|
607 throw new IllegalArgumentException( |
|
608 "Class " + baseClass.getName() + " inherits " + |
|
609 "@NotificationInfo(s) from both " + |
|
610 c.getName() + " and " + c1.getName()); |
|
611 } |
|
612 c = c1; |
|
613 } |
|
614 } |
|
615 } |
|
616 // Record the result of the search. If no @NotificationInfo(s) |
|
617 // were found, c is null, and we store a WeakReference(null). |
|
618 // This prevents us from having to search again and fail again. |
|
619 annotatedNotificationInfoClasses.put(baseClass, |
|
620 new WeakReference<Class<?>>(c)); |
|
621 return c; |
|
622 } |
|
623 } |
|
624 |
481 private static MBeanConstructorInfo[] findConstructors(Class<?> c) { |
625 private static MBeanConstructorInfo[] findConstructors(Class<?> c) { |
482 Constructor[] cons = c.getConstructors(); |
626 Constructor[] cons = c.getConstructors(); |
483 MBeanConstructorInfo[] mbc = new MBeanConstructorInfo[cons.length]; |
627 MBeanConstructorInfo[] mbc = new MBeanConstructorInfo[cons.length]; |
484 for (int i = 0; i < cons.length; i++) { |
628 for (int i = 0; i < cons.length; i++) { |
485 final String descr = "Public constructor of the MBean"; |
629 String descr = "Public constructor of the MBean"; |
|
630 Description d = cons[i].getAnnotation(Description.class); |
|
631 if (d != null) |
|
632 descr = d.value(); |
486 mbc[i] = new MBeanConstructorInfo(descr, cons[i]); |
633 mbc[i] = new MBeanConstructorInfo(descr, cons[i]); |
487 } |
634 } |
488 return mbc; |
635 return mbc; |
489 } |
636 } |
490 |
637 |