jdk/src/share/classes/com/sun/jmx/mbeanserver/MBeanIntrospector.java
changeset 4156 acaa49a2768a
parent 1702 50e12093123c
child 4328 9591511c1d88
equal deleted inserted replaced
4155:460e37d40f12 4156:acaa49a2768a
    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;
    77  * which we could call getName, getGenericReturnType, etc.  A simpler approach
    69  * which we could call getName, getGenericReturnType, etc.  A simpler approach
    78  * would be to wrap every Method in an object that does have a common
    70  * would be to wrap every Method in an object that does have a common
    79  * ancestor with ConvertingMethod.  But that would mean an extra object
    71  * ancestor with ConvertingMethod.  But that would mean an extra object
    80  * for every Method in every Standard MBean interface.
    72  * for every Method in every Standard MBean interface.
    81  */
    73  */
    82 public abstract class MBeanIntrospector<M> {
    74 abstract class MBeanIntrospector<M> {
    83     static final class PerInterfaceMap<M>
    75     static final class PerInterfaceMap<M>
    84             extends WeakHashMap<Class<?>, WeakReference<PerInterface<M>>> {}
    76             extends WeakHashMap<Class<?>, WeakReference<PerInterface<M>>> {}
    85 
    77 
    86     /** The map from interface to PerInterface for this type of MBean. */
    78     /** The map from interface to PerInterface for this type of MBean. */
    87     abstract PerInterfaceMap<M> getPerInterfaceMap();
    79     abstract PerInterfaceMap<M> getPerInterfaceMap();
   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) {
   368                     (t == null ? null : t.toString()));
   311                     (t == null ? null : t.toString()));
   369     }
   312     }
   370 
   313 
   371     /** A visitor that constructs the per-interface MBeanInfo. */
   314     /** A visitor that constructs the per-interface MBeanInfo. */
   372     private class MBeanInfoMaker
   315     private class MBeanInfoMaker
   373             implements MBeanAnalyzer.MBeanVisitor<M, IntrospectionException> {
   316             implements MBeanAnalyzer.MBeanVisitor<M> {
   374 
   317 
   375         public void visitAttribute(String attributeName,
   318         public void visitAttribute(String attributeName,
   376                 M getter,
   319                 M getter,
   377                 M setter) throws IntrospectionException {
   320                 M setter) {
   378             MBeanAttributeInfo mbai =
   321             MBeanAttributeInfo mbai =
   379                     getMBeanAttributeInfo(attributeName, getter, setter);
   322                     getMBeanAttributeInfo(attributeName, getter, setter);
   380 
   323 
   381             attrs.add(mbai);
   324             attrs.add(mbai);
   382         }
   325         }
   401                     "interfaceClassName=" + mbeanInterface.getName();
   344                     "interfaceClassName=" + mbeanInterface.getName();
   402             final Descriptor classNameDescriptor =
   345             final Descriptor classNameDescriptor =
   403                     new ImmutableDescriptor(interfaceClassName);
   346                     new ImmutableDescriptor(interfaceClassName);
   404             final Descriptor mbeanDescriptor = getBasicMBeanDescriptor();
   347             final Descriptor mbeanDescriptor = getBasicMBeanDescriptor();
   405             final Descriptor annotatedDescriptor =
   348             final Descriptor annotatedDescriptor =
   406                     Introspector.descriptorForElement(mbeanInterface, false);
   349                     Introspector.descriptorForElement(mbeanInterface);
   407             final Descriptor descriptor =
   350             final Descriptor descriptor =
   408                 DescriptorCache.getInstance().union(
   351                 DescriptorCache.getInstance().union(
   409                     classNameDescriptor,
   352                     classNameDescriptor,
   410                     mbeanDescriptor,
   353                     mbeanDescriptor,
   411                     annotatedDescriptor);
   354                     annotatedDescriptor);
   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