jdk/src/share/classes/com/sun/jmx/mbeanserver/Introspector.java
changeset 4328 9591511c1d88
parent 4156 acaa49a2768a
child 5506 202f599c92aa
equal deleted inserted replaced
4327:7c352d15334c 4328:9591511c1d88
    24  */
    24  */
    25 
    25 
    26 package com.sun.jmx.mbeanserver;
    26 package com.sun.jmx.mbeanserver;
    27 
    27 
    28 import java.lang.annotation.Annotation;
    28 import java.lang.annotation.Annotation;
       
    29 import java.lang.ref.SoftReference;
    29 import java.lang.reflect.AnnotatedElement;
    30 import java.lang.reflect.AnnotatedElement;
    30 import java.lang.reflect.Constructor;
    31 import java.lang.reflect.Constructor;
    31 import java.lang.reflect.Method;
    32 import java.lang.reflect.Method;
    32 import java.lang.reflect.Modifier;
    33 import java.lang.reflect.Modifier;
    33 import java.lang.reflect.Proxy;
    34 import java.lang.reflect.Proxy;
    34 import java.lang.reflect.UndeclaredThrowableException;
    35 import java.lang.reflect.UndeclaredThrowableException;
    35 import java.util.Arrays;
    36 import java.util.Arrays;
       
    37 import java.util.Collections;
    36 import java.util.HashMap;
    38 import java.util.HashMap;
       
    39 import java.util.List;
       
    40 import java.util.LinkedList;
       
    41 import java.util.Locale;
    37 import java.util.Map;
    42 import java.util.Map;
       
    43 import java.util.WeakHashMap;
    38 
    44 
    39 import javax.management.Descriptor;
    45 import javax.management.Descriptor;
    40 import javax.management.DescriptorKey;
    46 import javax.management.DescriptorKey;
    41 import javax.management.DynamicMBean;
    47 import javax.management.DynamicMBean;
    42 import javax.management.ImmutableDescriptor;
    48 import javax.management.ImmutableDescriptor;
   504             } else if (complex instanceof CompositeData) {
   510             } else if (complex instanceof CompositeData) {
   505                 return ((CompositeData) complex).get(element);
   511                 return ((CompositeData) complex).get(element);
   506             } else {
   512             } else {
   507                 // Java Beans introspection
   513                 // Java Beans introspection
   508                 //
   514                 //
   509                 BeanInfo bi = java.beans.Introspector.getBeanInfo(complex.getClass());
   515                 Class<?> clazz = complex.getClass();
   510                 PropertyDescriptor[] pds = bi.getPropertyDescriptors();
   516                 Method readMethod = null;
   511                 for (PropertyDescriptor pd : pds)
   517                 if (BeansHelper.isAvailable()) {
   512                     if (pd.getName().equals(element))
   518                     Object bi = BeansHelper.getBeanInfo(clazz);
   513                         return pd.getReadMethod().invoke(complex);
   519                     Object[] pds = BeansHelper.getPropertyDescriptors(bi);
       
   520                     for (Object pd: pds) {
       
   521                         if (BeansHelper.getPropertyName(pd).equals(element)) {
       
   522                             readMethod = BeansHelper.getReadMethod(pd);
       
   523                             break;
       
   524                         }
       
   525                     }
       
   526                 } else {
       
   527                     // Java Beans not available so use simple introspection
       
   528                     // to locate method
       
   529                     readMethod = SimpleIntrospector.getReadMethod(clazz, element);
       
   530                 }
       
   531                 if (readMethod != null)
       
   532                     return readMethod.invoke(complex);
       
   533 
   514                 throw new AttributeNotFoundException(
   534                 throw new AttributeNotFoundException(
   515                     "Could not find the getter method for the property " +
   535                     "Could not find the getter method for the property " +
   516                     element + " using the Java Beans introspector");
   536                     element + " using the Java Beans introspector");
   517             }
   537             }
   518         } catch (InvocationTargetException e) {
   538         } catch (InvocationTargetException e) {
   522         } catch (Exception e) {
   542         } catch (Exception e) {
   523             throw EnvHelp.initCause(
   543             throw EnvHelp.initCause(
   524                 new AttributeNotFoundException(e.getMessage()), e);
   544                 new AttributeNotFoundException(e.getMessage()), e);
   525         }
   545         }
   526     }
   546     }
       
   547 
       
   548     /**
       
   549      * A simple introspector that uses reflection to analyze a class and
       
   550      * identify its "getter" methods. This class is intended for use only when
       
   551      * Java Beans is not present (which implies that there isn't explicit
       
   552      * information about the bean available).
       
   553      */
       
   554     private static class SimpleIntrospector {
       
   555         private SimpleIntrospector() { }
       
   556 
       
   557         private static final String GET_METHOD_PREFIX = "get";
       
   558         private static final String IS_METHOD_PREFIX = "is";
       
   559 
       
   560         // cache to avoid repeated lookups
       
   561         private static final Map<Class<?>,SoftReference<List<Method>>> cache =
       
   562             Collections.synchronizedMap(
       
   563                 new WeakHashMap<Class<?>,SoftReference<List<Method>>> ());
       
   564 
       
   565         /**
       
   566          * Returns the list of methods cached for the given class, or {@code null}
       
   567          * if not cached.
       
   568          */
       
   569         private static List<Method> getCachedMethods(Class<?> clazz) {
       
   570             // return cached methods if possible
       
   571             SoftReference<List<Method>> ref = cache.get(clazz);
       
   572             if (ref != null) {
       
   573                 List<Method> cached = ref.get();
       
   574                 if (cached != null)
       
   575                     return cached;
       
   576             }
       
   577             return null;
       
   578         }
       
   579 
       
   580         /**
       
   581          * Returns {@code true} if the given method is a "getter" method (where
       
   582          * "getter" method is a public method of the form getXXX or "boolean
       
   583          * isXXX")
       
   584          */
       
   585         static boolean isReadMethod(Method method) {
       
   586             // ignore static methods
       
   587             int modifiers = method.getModifiers();
       
   588             if (Modifier.isStatic(modifiers))
       
   589                 return false;
       
   590 
       
   591             String name = method.getName();
       
   592             Class<?>[] paramTypes = method.getParameterTypes();
       
   593             int paramCount = paramTypes.length;
       
   594 
       
   595             if (paramCount == 0 && name.length() > 2) {
       
   596                 // boolean isXXX()
       
   597                 if (name.startsWith(IS_METHOD_PREFIX))
       
   598                     return (method.getReturnType() == boolean.class);
       
   599                 // getXXX()
       
   600                 if (name.length() > 3 && name.startsWith(GET_METHOD_PREFIX))
       
   601                     return (method.getReturnType() != void.class);
       
   602             }
       
   603             return false;
       
   604         }
       
   605 
       
   606         /**
       
   607          * Returns the list of "getter" methods for the given class. The list
       
   608          * is ordered so that isXXX methods appear before getXXX methods - this
       
   609          * is for compatability with the JavaBeans Introspector.
       
   610          */
       
   611         static List<Method> getReadMethods(Class<?> clazz) {
       
   612             // return cached result if available
       
   613             List<Method> cachedResult = getCachedMethods(clazz);
       
   614             if (cachedResult != null)
       
   615                 return cachedResult;
       
   616 
       
   617             // get list of public methods, filtering out methods that have
       
   618             // been overridden to return a more specific type.
       
   619             List<Method> methods =
       
   620                 StandardMBeanIntrospector.getInstance().getMethods(clazz);
       
   621             methods = MBeanAnalyzer.eliminateCovariantMethods(methods);
       
   622 
       
   623             // filter out the non-getter methods
       
   624             List<Method> result = new LinkedList<Method>();
       
   625             for (Method m: methods) {
       
   626                 if (isReadMethod(m)) {
       
   627                     // favor isXXX over getXXX
       
   628                     if (m.getName().startsWith(IS_METHOD_PREFIX)) {
       
   629                         result.add(0, m);
       
   630                     } else {
       
   631                         result.add(m);
       
   632                     }
       
   633                 }
       
   634             }
       
   635 
       
   636             // add result to cache
       
   637             cache.put(clazz, new SoftReference<List<Method>>(result));
       
   638 
       
   639             return result;
       
   640         }
       
   641 
       
   642         /**
       
   643          * Returns the "getter" to read the given property from the given class or
       
   644          * {@code null} if no method is found.
       
   645          */
       
   646         static Method getReadMethod(Class<?> clazz, String property) {
       
   647             // first character in uppercase (compatability with JavaBeans)
       
   648             property = property.substring(0, 1).toUpperCase(Locale.ENGLISH) +
       
   649                 property.substring(1);
       
   650             String getMethod = GET_METHOD_PREFIX + property;
       
   651             String isMethod = IS_METHOD_PREFIX + property;
       
   652             for (Method m: getReadMethods(clazz)) {
       
   653                 String name = m.getName();
       
   654                 if (name.equals(isMethod) || name.equals(getMethod)) {
       
   655                     return m;
       
   656                 }
       
   657             }
       
   658             return null;
       
   659         }
       
   660     }
       
   661 
       
   662     /**
       
   663      * A class that provides access to the JavaBeans Introspector and
       
   664      * PropertyDescriptors without creating a static dependency on java.beans.
       
   665      */
       
   666     private static class BeansHelper {
       
   667         private static final Class<?> introspectorClass =
       
   668             getClass("java.beans.Introspector");
       
   669         private static final Class<?> beanInfoClass =
       
   670             (introspectorClass == null) ? null : getClass("java.beans.BeanInfo");
       
   671         private static final Class<?> getPropertyDescriptorClass =
       
   672             (beanInfoClass == null) ? null : getClass("java.beans.PropertyDescriptor");
       
   673 
       
   674         private static final Method getBeanInfo =
       
   675             getMethod(introspectorClass, "getBeanInfo", Class.class);
       
   676         private static final Method getPropertyDescriptors =
       
   677             getMethod(beanInfoClass, "getPropertyDescriptors");
       
   678         private static final Method getPropertyName =
       
   679             getMethod(getPropertyDescriptorClass, "getName");
       
   680         private static final Method getReadMethod =
       
   681             getMethod(getPropertyDescriptorClass, "getReadMethod");
       
   682 
       
   683         private static Class<?> getClass(String name) {
       
   684             try {
       
   685                 return Class.forName(name, true, null);
       
   686             } catch (ClassNotFoundException e) {
       
   687                 return null;
       
   688             }
       
   689         }
       
   690         private static Method getMethod(Class<?> clazz,
       
   691                                         String name,
       
   692                                         Class<?>... paramTypes)
       
   693         {
       
   694             if (clazz != null) {
       
   695                 try {
       
   696                     return clazz.getMethod(name, paramTypes);
       
   697                 } catch (NoSuchMethodException e) {
       
   698                     throw new AssertionError(e);
       
   699                 }
       
   700             } else {
       
   701                 return null;
       
   702             }
       
   703         }
       
   704 
       
   705         private BeansHelper() { }
       
   706 
       
   707         /**
       
   708          * Returns {@code true} if java.beans is available.
       
   709          */
       
   710         static boolean isAvailable() {
       
   711             return introspectorClass != null;
       
   712         }
       
   713 
       
   714         /**
       
   715          * Invokes java.beans.Introspector.getBeanInfo(Class)
       
   716          */
       
   717         static Object getBeanInfo(Class<?> clazz) throws Exception {
       
   718             try {
       
   719                 return getBeanInfo.invoke(null, clazz);
       
   720             } catch (InvocationTargetException e) {
       
   721                 Throwable cause = e.getCause();
       
   722                 if (cause instanceof Exception)
       
   723                     throw (Exception)cause;
       
   724                 throw new AssertionError(e);
       
   725             } catch (IllegalAccessException iae) {
       
   726                 throw new AssertionError(iae);
       
   727             }
       
   728         }
       
   729 
       
   730         /**
       
   731          * Invokes java.beans.BeanInfo.getPropertyDescriptors()
       
   732          */
       
   733         static Object[] getPropertyDescriptors(Object bi) {
       
   734             try {
       
   735                 return (Object[])getPropertyDescriptors.invoke(bi);
       
   736             } catch (InvocationTargetException e) {
       
   737                 Throwable cause = e.getCause();
       
   738                 if (cause instanceof RuntimeException)
       
   739                     throw (RuntimeException)cause;
       
   740                 throw new AssertionError(e);
       
   741             } catch (IllegalAccessException iae) {
       
   742                 throw new AssertionError(iae);
       
   743             }
       
   744         }
       
   745 
       
   746         /**
       
   747          * Invokes java.beans.PropertyDescriptor.getName()
       
   748          */
       
   749         static String getPropertyName(Object pd) {
       
   750             try {
       
   751                 return (String)getPropertyName.invoke(pd);
       
   752             } catch (InvocationTargetException e) {
       
   753                 Throwable cause = e.getCause();
       
   754                 if (cause instanceof RuntimeException)
       
   755                     throw (RuntimeException)cause;
       
   756                 throw new AssertionError(e);
       
   757             } catch (IllegalAccessException iae) {
       
   758                 throw new AssertionError(iae);
       
   759             }
       
   760         }
       
   761 
       
   762         /**
       
   763          * Invokes java.beans.PropertyDescriptor.getReadMethod()
       
   764          */
       
   765         static Method getReadMethod(Object pd) {
       
   766             try {
       
   767                 return (Method)getReadMethod.invoke(pd);
       
   768             } catch (InvocationTargetException e) {
       
   769                 Throwable cause = e.getCause();
       
   770                 if (cause instanceof RuntimeException)
       
   771                     throw (RuntimeException)cause;
       
   772                 throw new AssertionError(e);
       
   773             } catch (IllegalAccessException iae) {
       
   774                 throw new AssertionError(iae);
       
   775             }
       
   776         }
       
   777     }
   527 }
   778 }