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; |
|
40 import java.util.WeakHashMap; |
39 import java.util.WeakHashMap; |
41 import javax.management.Description; |
|
42 |
40 |
43 import javax.management.Descriptor; |
41 import javax.management.Descriptor; |
44 import javax.management.ImmutableDescriptor; |
42 import javax.management.ImmutableDescriptor; |
45 import javax.management.IntrospectionException; |
43 import javax.management.IntrospectionException; |
46 import javax.management.InvalidAttributeValueException; |
44 import javax.management.InvalidAttributeValueException; |
47 import javax.management.MBean; |
|
48 import javax.management.MBeanAttributeInfo; |
45 import javax.management.MBeanAttributeInfo; |
49 import javax.management.MBeanConstructorInfo; |
46 import javax.management.MBeanConstructorInfo; |
50 import javax.management.MBeanException; |
47 import javax.management.MBeanException; |
51 import javax.management.MBeanInfo; |
48 import javax.management.MBeanInfo; |
52 import javax.management.MBeanNotificationInfo; |
49 import javax.management.MBeanNotificationInfo; |
53 import javax.management.MBeanOperationInfo; |
50 import javax.management.MBeanOperationInfo; |
54 import javax.management.MXBean; |
|
55 import javax.management.ManagedAttribute; |
|
56 import javax.management.ManagedOperation; |
|
57 import javax.management.NotCompliantMBeanException; |
51 import javax.management.NotCompliantMBeanException; |
58 import javax.management.NotificationBroadcaster; |
52 import javax.management.NotificationBroadcaster; |
59 import javax.management.NotificationInfo; |
|
60 import javax.management.NotificationInfos; |
|
61 import javax.management.ReflectionException; |
53 import javax.management.ReflectionException; |
62 |
54 |
63 /** |
55 /** |
64 * An introspector for MBeans of a certain type. There is one instance |
56 * An introspector for MBeans of a certain type. There is one instance |
65 * of this class for Standard MBeans, and one for every MXBeanMappingFactory; |
57 * of this class for Standard MBeans, and one for every MXBeanMappingFactory; |
157 * Construct an MBeanAttributeInfo for the given attribute based on the |
149 * Construct an MBeanAttributeInfo for the given attribute based on the |
158 * given getter and setter. One but not both of the getter and setter |
150 * given getter and setter. One but not both of the getter and setter |
159 * may be null. |
151 * may be null. |
160 */ |
152 */ |
161 abstract MBeanAttributeInfo getMBeanAttributeInfo(String attributeName, |
153 abstract MBeanAttributeInfo getMBeanAttributeInfo(String attributeName, |
162 M getter, M setter) throws IntrospectionException; |
154 M getter, M setter); |
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 |
|
183 /** |
155 /** |
184 * Construct an MBeanOperationInfo for the given operation based on |
156 * Construct an MBeanOperationInfo for the given operation based on |
185 * the M it was derived from. |
157 * the M it was derived from. |
186 */ |
158 */ |
187 abstract MBeanOperationInfo getMBeanOperationInfo(String operationName, |
159 abstract MBeanOperationInfo getMBeanOperationInfo(String operationName, |
199 * is resourceClass will always have. |
171 * is resourceClass will always have. |
200 */ |
172 */ |
201 abstract Descriptor getMBeanDescriptor(Class<?> resourceClass); |
173 abstract Descriptor getMBeanDescriptor(Class<?> resourceClass); |
202 |
174 |
203 /** |
175 /** |
204 * Get any additional Descriptor entries for this introspector instance. |
|
205 * If there is a non-default MXBeanMappingFactory, it will appear in |
|
206 * this Descriptor. |
|
207 * @return Additional Descriptor entries, or an empty Descriptor if none. |
|
208 */ |
|
209 Descriptor getSpecificMBeanDescriptor() { |
|
210 return ImmutableDescriptor.EMPTY_DESCRIPTOR; |
|
211 } |
|
212 |
|
213 void checkCompliance(Class<?> mbeanType) throws NotCompliantMBeanException { |
|
214 if (!mbeanType.isInterface() && |
|
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: " + |
|
220 mbeanType.getName()); |
|
221 } |
|
222 } |
|
223 |
|
224 /** |
|
225 * Get the methods to be analyzed to build the MBean interface. |
176 * Get the methods to be analyzed to build the MBean interface. |
226 */ |
177 */ |
227 List<Method> getMethods(final Class<?> mbeanType) throws Exception { |
178 List<Method> getMethods(final Class<?> mbeanType) throws Exception { |
228 if (mbeanType.isInterface()) |
179 return Arrays.asList(mbeanType.getMethods()); |
229 return Arrays.asList(mbeanType.getMethods()); |
|
230 |
|
231 final List<Method> methods = newList(); |
|
232 getAnnotatedMethods(mbeanType, methods); |
|
233 return methods; |
|
234 } |
180 } |
235 |
181 |
236 final PerInterface<M> getPerInterface(Class<?> mbeanInterface) |
182 final PerInterface<M> getPerInterface(Class<?> mbeanInterface) |
237 throws NotCompliantMBeanException { |
183 throws NotCompliantMBeanException { |
238 PerInterfaceMap<M> map = getPerInterfaceMap(); |
184 PerInterfaceMap<M> map = getPerInterfaceMap(); |
263 * its MBeanAttributeInfo[] and MBeanOperationInfo[] can be inserted |
209 * its MBeanAttributeInfo[] and MBeanOperationInfo[] can be inserted |
264 * into such an MBeanInfo, and its Descriptor can be the basis for |
210 * into such an MBeanInfo, and its Descriptor can be the basis for |
265 * the MBeanInfo's Descriptor. |
211 * the MBeanInfo's Descriptor. |
266 */ |
212 */ |
267 private MBeanInfo makeInterfaceMBeanInfo(Class<?> mbeanInterface, |
213 private MBeanInfo makeInterfaceMBeanInfo(Class<?> mbeanInterface, |
268 MBeanAnalyzer<M> analyzer) throws IntrospectionException { |
214 MBeanAnalyzer<M> analyzer) { |
269 final MBeanInfoMaker maker = new MBeanInfoMaker(); |
215 final MBeanInfoMaker maker = new MBeanInfoMaker(); |
270 analyzer.visit(maker); |
216 analyzer.visit(maker); |
271 final String defaultDescription = |
217 final String description = |
272 "Information on the management interface of the MBean"; |
218 "Information on the management interface of the MBean"; |
273 String description = Introspector.descriptionForElement(mbeanInterface); |
|
274 if (description == null) |
|
275 description = defaultDescription; |
|
276 return maker.makeMBeanInfo(mbeanInterface, description); |
219 return maker.makeMBeanInfo(mbeanInterface, description); |
277 } |
220 } |
278 |
221 |
279 /** True if the given getter and setter are consistent. */ |
222 /** True if the given getter and setter are consistent. */ |
280 final boolean consistent(M getter, M setter) { |
223 final boolean consistent(M getter, M setter) { |
440 |
383 |
441 /** |
384 /** |
442 * Return the MBeanInfo for the given resource, based on the given |
385 * Return the MBeanInfo for the given resource, based on the given |
443 * per-interface data. |
386 * per-interface data. |
444 */ |
387 */ |
445 final MBeanInfo getMBeanInfo(Object resource, PerInterface<M> perInterface) |
388 final MBeanInfo getMBeanInfo(Object resource, PerInterface<M> perInterface) { |
446 throws NotCompliantMBeanException { |
|
447 MBeanInfo mbi = |
389 MBeanInfo mbi = |
448 getClassMBeanInfo(resource.getClass(), perInterface); |
390 getClassMBeanInfo(resource.getClass(), perInterface); |
449 MBeanNotificationInfo[] notifs; |
391 MBeanNotificationInfo[] notifs = findNotifications(resource); |
450 try { |
392 if (notifs == null || notifs.length == 0) |
451 notifs = findNotifications(resource); |
|
452 } catch (RuntimeException e) { |
|
453 NotCompliantMBeanException x = |
|
454 new NotCompliantMBeanException(e.getMessage()); |
|
455 x.initCause(e); |
|
456 throw x; |
|
457 } |
|
458 Descriptor d = getSpecificMBeanDescriptor(); |
|
459 boolean anyNotifs = (notifs != null && notifs.length > 0); |
|
460 if (!anyNotifs && ImmutableDescriptor.EMPTY_DESCRIPTOR.equals(d)) |
|
461 return mbi; |
393 return mbi; |
462 else { |
394 else { |
463 d = ImmutableDescriptor.union(d, mbi.getDescriptor()); |
|
464 return new MBeanInfo(mbi.getClassName(), |
395 return new MBeanInfo(mbi.getClassName(), |
465 mbi.getDescription(), |
396 mbi.getDescription(), |
466 mbi.getAttributes(), |
397 mbi.getAttributes(), |
467 mbi.getConstructors(), |
398 mbi.getConstructors(), |
468 mbi.getOperations(), |
399 mbi.getOperations(), |
469 notifs, |
400 notifs, |
470 d); |
401 mbi.getDescriptor()); |
471 } |
402 } |
472 } |
403 } |
473 |
404 |
474 /** |
405 /** |
475 * Return the basic MBeanInfo for resources of the given class and |
406 * Return the basic MBeanInfo for resources of the given class and |
505 } |
436 } |
506 return mbi; |
437 return mbi; |
507 } |
438 } |
508 } |
439 } |
509 |
440 |
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 public 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 |
|
540 /* |
|
541 * Return the array of MBeanNotificationInfo for the given MBean object. |
|
542 * If the object implements NotificationBroadcaster and its |
|
543 * getNotificationInfo() method returns a non-empty array, then that |
|
544 * is the result. Otherwise, if the object has a @NotificationInfo |
|
545 * or @NotificationInfos annotation, then its contents form the result. |
|
546 * Otherwise, the result is null. |
|
547 */ |
|
548 static MBeanNotificationInfo[] findNotifications(Object moi) { |
441 static MBeanNotificationInfo[] findNotifications(Object moi) { |
549 if (moi instanceof NotificationBroadcaster) { |
442 if (!(moi instanceof NotificationBroadcaster)) |
550 MBeanNotificationInfo[] mbn = |
|
551 ((NotificationBroadcaster) moi).getNotificationInfo(); |
|
552 if (mbn != null && mbn.length > 0) { |
|
553 MBeanNotificationInfo[] result = |
|
554 new MBeanNotificationInfo[mbn.length]; |
|
555 for (int i = 0; i < mbn.length; i++) { |
|
556 MBeanNotificationInfo ni = mbn[i]; |
|
557 if (ni.getClass() != MBeanNotificationInfo.class) |
|
558 ni = (MBeanNotificationInfo) ni.clone(); |
|
559 result[i] = ni; |
|
560 } |
|
561 return result; |
|
562 } |
|
563 } else { |
|
564 try { |
|
565 if (!MBeanInjector.injectsSendNotification(moi)) |
|
566 return null; |
|
567 } catch (NotCompliantMBeanException e) { |
|
568 throw new RuntimeException(e); |
|
569 } |
|
570 } |
|
571 return findNotificationsFromAnnotations(moi.getClass()); |
|
572 } |
|
573 |
|
574 public static MBeanNotificationInfo[] findNotificationsFromAnnotations( |
|
575 Class<?> mbeanClass) { |
|
576 Class<?> c = getAnnotatedNotificationInfoClass(mbeanClass); |
|
577 if (c == null) |
|
578 return null; |
443 return null; |
579 NotificationInfo ni = c.getAnnotation(NotificationInfo.class); |
444 MBeanNotificationInfo[] mbn = |
580 NotificationInfos nis = c.getAnnotation(NotificationInfos.class); |
445 ((NotificationBroadcaster) moi).getNotificationInfo(); |
581 List<NotificationInfo> list = newList(); |
446 if (mbn == null) |
582 if (ni != null) |
|
583 list.add(ni); |
|
584 if (nis != null) |
|
585 list.addAll(Arrays.asList(nis.value())); |
|
586 if (list.isEmpty()) |
|
587 return null; |
447 return null; |
588 List<MBeanNotificationInfo> mbnis = newList(); |
448 MBeanNotificationInfo[] result = |
589 for (NotificationInfo x : list) { |
449 new MBeanNotificationInfo[mbn.length]; |
590 // The Descriptor includes any fields explicitly specified by |
450 for (int i = 0; i < mbn.length; i++) { |
591 // x.descriptorFields(), plus any fields from the contained |
451 MBeanNotificationInfo ni = mbn[i]; |
592 // @Description annotation. |
452 if (ni.getClass() != MBeanNotificationInfo.class) |
593 Descriptor d = new ImmutableDescriptor(x.descriptorFields()); |
453 ni = (MBeanNotificationInfo) ni.clone(); |
594 d = ImmutableDescriptor.union( |
454 result[i] = ni; |
595 d, Introspector.descriptorForAnnotation(x.description())); |
455 } |
596 MBeanNotificationInfo mbni = new MBeanNotificationInfo( |
456 return result; |
597 x.types(), x.notificationClass().getName(), |
|
598 x.description().value(), d); |
|
599 mbnis.add(mbni); |
|
600 } |
|
601 return mbnis.toArray(new MBeanNotificationInfo[mbnis.size()]); |
|
602 } |
|
603 |
|
604 private static final Map<Class<?>, WeakReference<Class<?>>> |
|
605 annotatedNotificationInfoClasses = newWeakHashMap(); |
|
606 |
|
607 private static Class<?> getAnnotatedNotificationInfoClass(Class<?> baseClass) { |
|
608 synchronized (annotatedNotificationInfoClasses) { |
|
609 WeakReference<Class<?>> wr = |
|
610 annotatedNotificationInfoClasses.get(baseClass); |
|
611 if (wr != null) |
|
612 return wr.get(); |
|
613 Class<?> c = null; |
|
614 if (baseClass.isAnnotationPresent(NotificationInfo.class) || |
|
615 baseClass.isAnnotationPresent(NotificationInfos.class)) { |
|
616 c = baseClass; |
|
617 } else { |
|
618 Class<?>[] intfs = baseClass.getInterfaces(); |
|
619 for (Class<?> intf : intfs) { |
|
620 Class<?> c1 = getAnnotatedNotificationInfoClass(intf); |
|
621 if (c1 != null) { |
|
622 if (c != null) { |
|
623 throw new IllegalArgumentException( |
|
624 "Class " + baseClass.getName() + " inherits " + |
|
625 "@NotificationInfo(s) from both " + |
|
626 c.getName() + " and " + c1.getName()); |
|
627 } |
|
628 c = c1; |
|
629 } |
|
630 } |
|
631 } |
|
632 // Record the result of the search. If no @NotificationInfo(s) |
|
633 // were found, c is null, and we store a WeakReference(null). |
|
634 // This prevents us from having to search again and fail again. |
|
635 annotatedNotificationInfoClasses.put(baseClass, |
|
636 new WeakReference<Class<?>>(c)); |
|
637 return c; |
|
638 } |
|
639 } |
457 } |
640 |
458 |
641 private static MBeanConstructorInfo[] findConstructors(Class<?> c) { |
459 private static MBeanConstructorInfo[] findConstructors(Class<?> c) { |
642 Constructor<?>[] cons = c.getConstructors(); |
460 Constructor<?>[] cons = c.getConstructors(); |
643 MBeanConstructorInfo[] mbc = new MBeanConstructorInfo[cons.length]; |
461 MBeanConstructorInfo[] mbc = new MBeanConstructorInfo[cons.length]; |
644 for (int i = 0; i < cons.length; i++) { |
462 for (int i = 0; i < cons.length; i++) { |
645 String descr = "Public constructor of the MBean"; |
463 final String descr = "Public constructor of the MBean"; |
646 Description d = cons[i].getAnnotation(Description.class); |
|
647 if (d != null) |
|
648 descr = d.value(); |
|
649 mbc[i] = new MBeanConstructorInfo(descr, cons[i]); |
464 mbc[i] = new MBeanConstructorInfo(descr, cons[i]); |
650 } |
465 } |
651 return mbc; |
466 return mbc; |
652 } |
467 } |
653 |
468 |