0N/A/*
2362N/A * Copyright (c) 1999, 2008, Oracle and/or its affiliates. All rights reserved.
0N/A * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
0N/A *
0N/A * This code is free software; you can redistribute it and/or modify it
0N/A * under the terms of the GNU General Public License version 2 only, as
2362N/A * published by the Free Software Foundation. Oracle designates this
0N/A * particular file as subject to the "Classpath" exception as provided
2362N/A * by Oracle in the LICENSE file that accompanied this code.
0N/A *
0N/A * This code is distributed in the hope that it will be useful, but WITHOUT
0N/A * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
0N/A * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
0N/A * version 2 for more details (a copy is included in the LICENSE file that
0N/A * accompanied this code).
0N/A *
0N/A * You should have received a copy of the GNU General Public License version
0N/A * 2 along with this work; if not, write to the Free Software Foundation,
0N/A * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
0N/A *
2362N/A * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
2362N/A * or visit www.oracle.com if you need additional information or have any
2362N/A * questions.
0N/A */
0N/A
0N/Apackage com.sun.jmx.mbeanserver;
0N/A
0N/Aimport java.lang.annotation.Annotation;
1933N/Aimport java.lang.ref.SoftReference;
0N/Aimport java.lang.reflect.AnnotatedElement;
0N/Aimport java.lang.reflect.Constructor;
0N/Aimport java.lang.reflect.Method;
0N/Aimport java.lang.reflect.Modifier;
406N/Aimport java.lang.reflect.Proxy;
0N/Aimport java.lang.reflect.UndeclaredThrowableException;
0N/Aimport java.util.Arrays;
1933N/Aimport java.util.Collections;
0N/Aimport java.util.HashMap;
1933N/Aimport java.util.List;
1933N/Aimport java.util.LinkedList;
1933N/Aimport java.util.Locale;
0N/Aimport java.util.Map;
1933N/Aimport java.util.WeakHashMap;
0N/A
0N/Aimport javax.management.Descriptor;
0N/Aimport javax.management.DescriptorKey;
0N/Aimport javax.management.DynamicMBean;
0N/Aimport javax.management.ImmutableDescriptor;
0N/Aimport javax.management.MBeanInfo;
0N/Aimport javax.management.NotCompliantMBeanException;
0N/A
11N/Aimport com.sun.jmx.remote.util.EnvHelp;
11N/Aimport java.beans.BeanInfo;
11N/Aimport java.beans.PropertyDescriptor;
11N/Aimport java.lang.reflect.Array;
11N/Aimport java.lang.reflect.InvocationTargetException;
11N/Aimport javax.management.AttributeNotFoundException;
11N/Aimport javax.management.openmbean.CompositeData;
5698N/Aimport sun.reflect.misc.MethodUtil;
5698N/Aimport sun.reflect.misc.ReflectUtil;
0N/A
0N/A/**
0N/A * This class contains the methods for performing all the tests needed to verify
0N/A * that a class represents a JMX compliant MBean.
0N/A *
0N/A * @since 1.5
0N/A */
0N/Apublic class Introspector {
0N/A
1790N/A
0N/A /*
0N/A * ------------------------------------------
0N/A * PRIVATE CONSTRUCTORS
0N/A * ------------------------------------------
0N/A */
0N/A
0N/A // private constructor defined to "hide" the default public constructor
0N/A private Introspector() {
0N/A
0N/A // ------------------------------
0N/A // ------------------------------
0N/A
0N/A }
0N/A
0N/A /*
0N/A * ------------------------------------------
0N/A * PUBLIC METHODS
0N/A * ------------------------------------------
0N/A */
0N/A
0N/A /**
0N/A * Tell whether a MBean of the given class is a Dynamic MBean.
0N/A * This method does nothing more than returning
0N/A * <pre>
0N/A * javax.management.DynamicMBean.class.isAssignableFrom(c)
0N/A * </pre>
0N/A * This method does not check for any JMX MBean compliance:
0N/A * <ul><li>If <code>true</code> is returned, then instances of
0N/A * <code>c</code> are DynamicMBean.</li>
0N/A * <li>If <code>false</code> is returned, then no further
0N/A * assumption can be made on instances of <code>c</code>.
0N/A * In particular, instances of <code>c</code> may, or may not
0N/A * be JMX standard MBeans.</li>
0N/A * </ul>
0N/A * @param c The class of the MBean under examination.
0N/A * @return <code>true</code> if instances of <code>c</code> are
0N/A * Dynamic MBeans, <code>false</code> otherwise.
0N/A *
0N/A **/
686N/A public static final boolean isDynamic(final Class<?> c) {
0N/A // Check if the MBean implements the DynamicMBean interface
0N/A return javax.management.DynamicMBean.class.isAssignableFrom(c);
0N/A }
0N/A
0N/A /**
0N/A * Basic method for testing that a MBean of a given class can be
0N/A * instantiated by the MBean server.<p>
0N/A * This method checks that:
0N/A * <ul><li>The given class is a concrete class.</li>
0N/A * <li>The given class exposes at least one public constructor.</li>
0N/A * </ul>
0N/A * If these conditions are not met, throws a NotCompliantMBeanException.
0N/A * @param c The class of the MBean we want to create.
0N/A * @exception NotCompliantMBeanException if the MBean class makes it
0N/A * impossible to instantiate the MBean from within the
0N/A * MBeanServer.
0N/A *
0N/A **/
686N/A public static void testCreation(Class<?> c)
0N/A throws NotCompliantMBeanException {
0N/A // Check if the class is a concrete class
0N/A final int mods = c.getModifiers();
0N/A if (Modifier.isAbstract(mods) || Modifier.isInterface(mods)) {
0N/A throw new NotCompliantMBeanException("MBean class must be concrete");
0N/A }
0N/A
0N/A // Check if the MBean has a public constructor
686N/A final Constructor<?>[] consList = c.getConstructors();
0N/A if (consList.length == 0) {
0N/A throw new NotCompliantMBeanException("MBean class must have public constructor");
0N/A }
0N/A }
0N/A
406N/A public static void checkCompliance(Class<?> mbeanClass)
406N/A throws NotCompliantMBeanException {
0N/A // Is DynamicMBean?
0N/A //
0N/A if (DynamicMBean.class.isAssignableFrom(mbeanClass))
0N/A return;
0N/A // Is Standard MBean?
0N/A //
0N/A final Exception mbeanException;
0N/A try {
0N/A getStandardMBeanInterface(mbeanClass);
0N/A return;
0N/A } catch (NotCompliantMBeanException e) {
0N/A mbeanException = e;
0N/A }
0N/A // Is MXBean?
0N/A //
0N/A final Exception mxbeanException;
0N/A try {
0N/A getMXBeanInterface(mbeanClass);
0N/A return;
0N/A } catch (NotCompliantMBeanException e) {
0N/A mxbeanException = e;
0N/A }
0N/A final String msg =
0N/A "MBean class " + mbeanClass.getName() + " does not implement " +
1790N/A "DynamicMBean, and neither follows the Standard MBean conventions (" +
1790N/A mbeanException.toString() + ") nor the MXBean conventions (" +
1790N/A mxbeanException.toString() + ")";
0N/A throw new NotCompliantMBeanException(msg);
0N/A }
0N/A
353N/A public static <T> DynamicMBean makeDynamicMBean(T mbean)
1790N/A throws NotCompliantMBeanException {
0N/A if (mbean instanceof DynamicMBean)
0N/A return (DynamicMBean) mbean;
406N/A final Class<?> mbeanClass = mbean.getClass();
353N/A Class<? super T> c = null;
0N/A try {
353N/A c = Util.cast(getStandardMBeanInterface(mbeanClass));
0N/A } catch (NotCompliantMBeanException e) {
0N/A // Ignore exception - we need to check whether
0N/A // mbean is an MXBean first.
0N/A }
0N/A if (c != null)
353N/A return new StandardMBeanSupport(mbean, c);
0N/A
0N/A try {
353N/A c = Util.cast(getMXBeanInterface(mbeanClass));
0N/A } catch (NotCompliantMBeanException e) {
0N/A // Ignore exception - we cannot decide whether mbean was supposed
0N/A // to be an MBean or an MXBean. We will call checkCompliance()
0N/A // to generate the appropriate exception.
0N/A }
1790N/A if (c != null)
1790N/A return new MXBeanSupport(mbean, c);
0N/A checkCompliance(mbeanClass);
0N/A throw new NotCompliantMBeanException("Not compliant"); // not reached
0N/A }
0N/A
0N/A /**
0N/A * Basic method for testing if a given class is a JMX compliant MBean.
0N/A *
0N/A * @param baseClass The class to be tested
0N/A *
0N/A * @return <code>null</code> if the MBean is a DynamicMBean,
0N/A * the computed {@link javax.management.MBeanInfo} otherwise.
0N/A * @exception NotCompliantMBeanException The specified class is not a
0N/A * JMX compliant MBean
0N/A */
686N/A public static MBeanInfo testCompliance(Class<?> baseClass)
0N/A throws NotCompliantMBeanException {
0N/A
0N/A // ------------------------------
0N/A // ------------------------------
0N/A
0N/A // Check if the MBean implements the MBean or the Dynamic
0N/A // MBean interface
0N/A if (isDynamic(baseClass))
0N/A return null;
0N/A
0N/A return testCompliance(baseClass, null);
0N/A }
0N/A
1790N/A public static void testComplianceMXBeanInterface(Class<?> interfaceClass)
0N/A throws NotCompliantMBeanException {
1790N/A MXBeanIntrospector.getInstance().getAnalyzer(interfaceClass);
0N/A }
0N/A
6315N/A public static void testComplianceMBeanInterface(Class<?> interfaceClass)
6315N/A throws NotCompliantMBeanException{
6315N/A StandardMBeanIntrospector.getInstance().getAnalyzer(interfaceClass);
6315N/A }
6315N/A
0N/A /**
0N/A * Basic method for testing if a given class is a JMX compliant
0N/A * Standard MBean. This method is only called by the legacy code
0N/A * in com.sun.management.jmx.
0N/A *
0N/A * @param baseClass The class to be tested.
0N/A *
0N/A * @param mbeanInterface the MBean interface that the class implements,
0N/A * or null if the interface must be determined by introspection.
0N/A *
0N/A * @return the computed {@link javax.management.MBeanInfo}.
0N/A * @exception NotCompliantMBeanException The specified class is not a
0N/A * JMX compliant Standard MBean
0N/A */
0N/A public static synchronized MBeanInfo
0N/A testCompliance(final Class<?> baseClass,
0N/A Class<?> mbeanInterface)
0N/A throws NotCompliantMBeanException {
0N/A if (mbeanInterface == null)
0N/A mbeanInterface = getStandardMBeanInterface(baseClass);
6318N/A ReflectUtil.checkPackageAccess(mbeanInterface);
0N/A MBeanIntrospector<?> introspector = StandardMBeanIntrospector.getInstance();
0N/A return getClassMBeanInfo(introspector, baseClass, mbeanInterface);
0N/A }
0N/A
0N/A private static <M> MBeanInfo
0N/A getClassMBeanInfo(MBeanIntrospector<M> introspector,
0N/A Class<?> baseClass, Class<?> mbeanInterface)
0N/A throws NotCompliantMBeanException {
0N/A PerInterface<M> perInterface = introspector.getPerInterface(mbeanInterface);
0N/A return introspector.getClassMBeanInfo(baseClass, perInterface);
0N/A }
0N/A
0N/A /**
0N/A * Get the MBean interface implemented by a JMX Standard
0N/A * MBean class. This method is only called by the legacy
0N/A * code in "com.sun.management.jmx".
0N/A *
0N/A * @param baseClass The class to be tested.
0N/A *
0N/A * @return The MBean interface implemented by the MBean.
0N/A * Return <code>null</code> if the MBean is a DynamicMBean,
0N/A * or if no MBean interface is found.
0N/A */
406N/A public static Class<?> getMBeanInterface(Class<?> baseClass) {
0N/A // Check if the given class implements the MBean interface
0N/A // or the Dynamic MBean interface
0N/A if (isDynamic(baseClass)) return null;
0N/A try {
0N/A return getStandardMBeanInterface(baseClass);
0N/A } catch (NotCompliantMBeanException e) {
0N/A return null;
0N/A }
0N/A }
0N/A
0N/A /**
0N/A * Get the MBean interface implemented by a JMX Standard MBean class.
0N/A *
0N/A * @param baseClass The class to be tested.
0N/A *
0N/A * @return The MBean interface implemented by the Standard MBean.
0N/A *
0N/A * @throws NotCompliantMBeanException The specified class is
0N/A * not a JMX compliant Standard MBean.
0N/A */
406N/A public static <T> Class<? super T> getStandardMBeanInterface(Class<T> baseClass)
406N/A throws NotCompliantMBeanException {
406N/A Class<? super T> current = baseClass;
406N/A Class<? super T> mbeanInterface = null;
0N/A while (current != null) {
0N/A mbeanInterface =
0N/A findMBeanInterface(current, current.getName());
0N/A if (mbeanInterface != null) break;
0N/A current = current.getSuperclass();
0N/A }
0N/A if (mbeanInterface != null) {
0N/A return mbeanInterface;
0N/A } else {
0N/A final String msg =
0N/A "Class " + baseClass.getName() +
0N/A " is not a JMX compliant Standard MBean";
0N/A throw new NotCompliantMBeanException(msg);
0N/A }
0N/A }
0N/A
0N/A /**
0N/A * Get the MXBean interface implemented by a JMX MXBean class.
0N/A *
0N/A * @param baseClass The class to be tested.
0N/A *
0N/A * @return The MXBean interface implemented by the MXBean.
0N/A *
0N/A * @throws NotCompliantMBeanException The specified class is
0N/A * not a JMX compliant MXBean.
0N/A */
406N/A public static <T> Class<? super T> getMXBeanInterface(Class<T> baseClass)
0N/A throws NotCompliantMBeanException {
0N/A try {
0N/A return MXBeanSupport.findMXBeanInterface(baseClass);
0N/A } catch (Exception e) {
0N/A throw throwException(baseClass,e);
0N/A }
0N/A }
0N/A
0N/A /*
0N/A * ------------------------------------------
0N/A * PRIVATE METHODS
0N/A * ------------------------------------------
0N/A */
0N/A
0N/A
0N/A /**
0N/A * Try to find the MBean interface corresponding to the class aName
0N/A * - i.e. <i>aName</i>MBean, from within aClass and its superclasses.
0N/A **/
406N/A private static <T> Class<? super T> findMBeanInterface(
406N/A Class<T> aClass, String aName) {
406N/A Class<? super T> current = aClass;
0N/A while (current != null) {
406N/A final Class<?>[] interfaces = current.getInterfaces();
0N/A final int len = interfaces.length;
0N/A for (int i=0;i<len;i++) {
406N/A Class<? super T> inter = Util.cast(interfaces[i]);
406N/A inter = implementsMBean(inter, aName);
0N/A if (inter != null) return inter;
0N/A }
0N/A current = current.getSuperclass();
0N/A }
0N/A return null;
0N/A }
0N/A
1790N/A public static Descriptor descriptorForElement(final AnnotatedElement elmt) {
0N/A if (elmt == null)
0N/A return ImmutableDescriptor.EMPTY_DESCRIPTOR;
0N/A final Annotation[] annots = elmt.getAnnotations();
1790N/A return descriptorForAnnotations(annots);
406N/A }
406N/A
0N/A public static Descriptor descriptorForAnnotations(Annotation[] annots) {
0N/A if (annots.length == 0)
0N/A return ImmutableDescriptor.EMPTY_DESCRIPTOR;
0N/A Map<String, Object> descriptorMap = new HashMap<String, Object>();
0N/A for (Annotation a : annots) {
1790N/A Class<? extends Annotation> c = a.annotationType();
1790N/A Method[] elements = c.getMethods();
6296N/A boolean packageAccess = false;
1790N/A for (Method element : elements) {
1790N/A DescriptorKey key = element.getAnnotation(DescriptorKey.class);
1790N/A if (key != null) {
1790N/A String name = key.value();
1790N/A Object value;
1790N/A try {
6296N/A // Avoid checking access more than once per annotation
6296N/A if (!packageAccess) {
6296N/A ReflectUtil.checkPackageAccess(c);
6296N/A packageAccess = true;
6296N/A }
6296N/A value = MethodUtil.invoke(element, a, null);
1790N/A } catch (RuntimeException e) {
1790N/A // we don't expect this - except for possibly
1790N/A // security exceptions?
1790N/A // RuntimeExceptions shouldn't be "UndeclaredThrowable".
1790N/A // anyway...
1790N/A //
1790N/A throw e;
1790N/A } catch (Exception e) {
1790N/A // we don't expect this
1790N/A throw new UndeclaredThrowableException(e);
1790N/A }
1790N/A value = annotationToField(value);
1790N/A Object oldValue = descriptorMap.put(name, value);
1790N/A if (oldValue != null && !equals(oldValue, value)) {
1790N/A final String msg =
1790N/A "Inconsistent values for descriptor field " + name +
1790N/A " from annotations: " + value + " :: " + oldValue;
1790N/A throw new IllegalArgumentException(msg);
1790N/A }
1790N/A }
1790N/A }
0N/A }
0N/A
0N/A if (descriptorMap.isEmpty())
0N/A return ImmutableDescriptor.EMPTY_DESCRIPTOR;
0N/A else
0N/A return new ImmutableDescriptor(descriptorMap);
0N/A }
0N/A
735N/A /**
0N/A * Throws a NotCompliantMBeanException or a SecurityException.
0N/A * @param notCompliant the class which was under examination
0N/A * @param cause the raeson why NotCompliantMBeanException should
0N/A * be thrown.
0N/A * @return nothing - this method always throw an exception.
0N/A * The return type makes it possible to write
0N/A * <pre> throw throwException(clazz,cause); </pre>
0N/A * @throws SecurityException - if cause is a SecurityException
0N/A * @throws NotCompliantMBeanException otherwise.
0N/A **/
0N/A static NotCompliantMBeanException throwException(Class<?> notCompliant,
0N/A Throwable cause)
0N/A throws NotCompliantMBeanException, SecurityException {
0N/A if (cause instanceof SecurityException)
0N/A throw (SecurityException) cause;
0N/A if (cause instanceof NotCompliantMBeanException)
0N/A throw (NotCompliantMBeanException)cause;
0N/A final String classname =
0N/A (notCompliant==null)?"null class":notCompliant.getName();
0N/A final String reason =
0N/A (cause==null)?"Not compliant":cause.getMessage();
0N/A final NotCompliantMBeanException res =
0N/A new NotCompliantMBeanException(classname+": "+reason);
0N/A res.initCause(cause);
0N/A throw res;
0N/A }
0N/A
0N/A // Convert a value from an annotation element to a descriptor field value
0N/A // E.g. with @interface Foo {class value()} an annotation @Foo(String.class)
0N/A // will produce a Descriptor field value "java.lang.String"
0N/A private static Object annotationToField(Object x) {
0N/A // An annotation element cannot have a null value but never mind
0N/A if (x == null)
0N/A return null;
0N/A if (x instanceof Number || x instanceof String ||
0N/A x instanceof Character || x instanceof Boolean ||
0N/A x instanceof String[])
0N/A return x;
0N/A // Remaining possibilities: array of primitive (e.g. int[]),
0N/A // enum, class, array of enum or class.
0N/A Class<?> c = x.getClass();
0N/A if (c.isArray()) {
0N/A if (c.getComponentType().isPrimitive())
0N/A return x;
0N/A Object[] xx = (Object[]) x;
0N/A String[] ss = new String[xx.length];
0N/A for (int i = 0; i < xx.length; i++)
0N/A ss[i] = (String) annotationToField(xx[i]);
0N/A return ss;
0N/A }
686N/A if (x instanceof Class<?>)
0N/A return ((Class<?>) x).getName();
686N/A if (x instanceof Enum<?>)
686N/A return ((Enum<?>) x).name();
0N/A // The only other possibility is that the value is another
0N/A // annotation, or that the language has evolved since this code
0N/A // was written. We don't allow for either of those currently.
406N/A // If it is indeed another annotation, then x will be a proxy
406N/A // with an unhelpful name like $Proxy2. So we extract the
406N/A // proxy's interface to use that in the exception message.
406N/A if (Proxy.isProxyClass(c))
406N/A c = c.getInterfaces()[0]; // array "can't be empty"
0N/A throw new IllegalArgumentException("Illegal type for annotation " +
406N/A "element using @DescriptorKey: " + c.getName());
0N/A }
0N/A
0N/A // This must be consistent with the check for duplicate field values in
0N/A // ImmutableDescriptor.union. But we don't expect to be called very
0N/A // often so this inefficient check should be enough.
0N/A private static boolean equals(Object x, Object y) {
0N/A return Arrays.deepEquals(new Object[] {x}, new Object[] {y});
0N/A }
0N/A
0N/A /**
0N/A * Returns the XXMBean interface or null if no such interface exists
0N/A *
0N/A * @param c The interface to be tested
0N/A * @param clName The name of the class implementing this interface
0N/A */
406N/A private static <T> Class<? super T> implementsMBean(Class<T> c, String clName) {
0N/A String clMBeanName = clName + "MBean";
0N/A if (c.getName().equals(clMBeanName)) {
0N/A return c;
0N/A }
406N/A Class<?>[] interfaces = c.getInterfaces();
0N/A for (int i = 0;i < interfaces.length; i++) {
0N/A if (interfaces[i].getName().equals(clMBeanName))
406N/A return Util.cast(interfaces[i]);
0N/A }
0N/A
0N/A return null;
0N/A }
11N/A
11N/A public static Object elementFromComplex(Object complex, String element)
11N/A throws AttributeNotFoundException {
11N/A try {
11N/A if (complex.getClass().isArray() && element.equals("length")) {
11N/A return Array.getLength(complex);
11N/A } else if (complex instanceof CompositeData) {
11N/A return ((CompositeData) complex).get(element);
11N/A } else {
11N/A // Java Beans introspection
11N/A //
1933N/A Class<?> clazz = complex.getClass();
1933N/A Method readMethod = null;
1933N/A if (BeansHelper.isAvailable()) {
1933N/A Object bi = BeansHelper.getBeanInfo(clazz);
1933N/A Object[] pds = BeansHelper.getPropertyDescriptors(bi);
1933N/A for (Object pd: pds) {
1933N/A if (BeansHelper.getPropertyName(pd).equals(element)) {
1933N/A readMethod = BeansHelper.getReadMethod(pd);
1933N/A break;
1933N/A }
1933N/A }
1933N/A } else {
1933N/A // Java Beans not available so use simple introspection
1933N/A // to locate method
1933N/A readMethod = SimpleIntrospector.getReadMethod(clazz, element);
1933N/A }
5698N/A if (readMethod != null) {
5698N/A ReflectUtil.checkPackageAccess(readMethod.getDeclaringClass());
5698N/A return MethodUtil.invoke(readMethod, complex, new Class[0]);
5698N/A }
1933N/A
11N/A throw new AttributeNotFoundException(
11N/A "Could not find the getter method for the property " +
11N/A element + " using the Java Beans introspector");
11N/A }
11N/A } catch (InvocationTargetException e) {
11N/A throw new IllegalArgumentException(e);
11N/A } catch (AttributeNotFoundException e) {
11N/A throw e;
11N/A } catch (Exception e) {
11N/A throw EnvHelp.initCause(
11N/A new AttributeNotFoundException(e.getMessage()), e);
11N/A }
11N/A }
1933N/A
1933N/A /**
1933N/A * A simple introspector that uses reflection to analyze a class and
1933N/A * identify its "getter" methods. This class is intended for use only when
1933N/A * Java Beans is not present (which implies that there isn't explicit
1933N/A * information about the bean available).
1933N/A */
1933N/A private static class SimpleIntrospector {
1933N/A private SimpleIntrospector() { }
1933N/A
1933N/A private static final String GET_METHOD_PREFIX = "get";
1933N/A private static final String IS_METHOD_PREFIX = "is";
1933N/A
1933N/A // cache to avoid repeated lookups
1933N/A private static final Map<Class<?>,SoftReference<List<Method>>> cache =
1933N/A Collections.synchronizedMap(
1933N/A new WeakHashMap<Class<?>,SoftReference<List<Method>>> ());
1933N/A
1933N/A /**
1933N/A * Returns the list of methods cached for the given class, or {@code null}
1933N/A * if not cached.
1933N/A */
1933N/A private static List<Method> getCachedMethods(Class<?> clazz) {
1933N/A // return cached methods if possible
1933N/A SoftReference<List<Method>> ref = cache.get(clazz);
1933N/A if (ref != null) {
1933N/A List<Method> cached = ref.get();
1933N/A if (cached != null)
1933N/A return cached;
1933N/A }
1933N/A return null;
1933N/A }
1933N/A
1933N/A /**
1933N/A * Returns {@code true} if the given method is a "getter" method (where
1933N/A * "getter" method is a public method of the form getXXX or "boolean
1933N/A * isXXX")
1933N/A */
1933N/A static boolean isReadMethod(Method method) {
1933N/A // ignore static methods
1933N/A int modifiers = method.getModifiers();
1933N/A if (Modifier.isStatic(modifiers))
1933N/A return false;
1933N/A
1933N/A String name = method.getName();
1933N/A Class<?>[] paramTypes = method.getParameterTypes();
1933N/A int paramCount = paramTypes.length;
1933N/A
1933N/A if (paramCount == 0 && name.length() > 2) {
1933N/A // boolean isXXX()
1933N/A if (name.startsWith(IS_METHOD_PREFIX))
1933N/A return (method.getReturnType() == boolean.class);
1933N/A // getXXX()
1933N/A if (name.length() > 3 && name.startsWith(GET_METHOD_PREFIX))
1933N/A return (method.getReturnType() != void.class);
1933N/A }
1933N/A return false;
1933N/A }
1933N/A
1933N/A /**
1933N/A * Returns the list of "getter" methods for the given class. The list
1933N/A * is ordered so that isXXX methods appear before getXXX methods - this
1933N/A * is for compatability with the JavaBeans Introspector.
1933N/A */
1933N/A static List<Method> getReadMethods(Class<?> clazz) {
1933N/A // return cached result if available
1933N/A List<Method> cachedResult = getCachedMethods(clazz);
1933N/A if (cachedResult != null)
1933N/A return cachedResult;
1933N/A
1933N/A // get list of public methods, filtering out methods that have
1933N/A // been overridden to return a more specific type.
1933N/A List<Method> methods =
1933N/A StandardMBeanIntrospector.getInstance().getMethods(clazz);
1933N/A methods = MBeanAnalyzer.eliminateCovariantMethods(methods);
1933N/A
1933N/A // filter out the non-getter methods
1933N/A List<Method> result = new LinkedList<Method>();
1933N/A for (Method m: methods) {
1933N/A if (isReadMethod(m)) {
1933N/A // favor isXXX over getXXX
1933N/A if (m.getName().startsWith(IS_METHOD_PREFIX)) {
1933N/A result.add(0, m);
1933N/A } else {
1933N/A result.add(m);
1933N/A }
1933N/A }
1933N/A }
1933N/A
1933N/A // add result to cache
1933N/A cache.put(clazz, new SoftReference<List<Method>>(result));
1933N/A
1933N/A return result;
1933N/A }
1933N/A
1933N/A /**
1933N/A * Returns the "getter" to read the given property from the given class or
1933N/A * {@code null} if no method is found.
1933N/A */
1933N/A static Method getReadMethod(Class<?> clazz, String property) {
1933N/A // first character in uppercase (compatability with JavaBeans)
1933N/A property = property.substring(0, 1).toUpperCase(Locale.ENGLISH) +
1933N/A property.substring(1);
1933N/A String getMethod = GET_METHOD_PREFIX + property;
1933N/A String isMethod = IS_METHOD_PREFIX + property;
1933N/A for (Method m: getReadMethods(clazz)) {
1933N/A String name = m.getName();
1933N/A if (name.equals(isMethod) || name.equals(getMethod)) {
1933N/A return m;
1933N/A }
1933N/A }
1933N/A return null;
1933N/A }
1933N/A }
1933N/A
1933N/A /**
1933N/A * A class that provides access to the JavaBeans Introspector and
1933N/A * PropertyDescriptors without creating a static dependency on java.beans.
1933N/A */
1933N/A private static class BeansHelper {
1933N/A private static final Class<?> introspectorClass =
1933N/A getClass("java.beans.Introspector");
1933N/A private static final Class<?> beanInfoClass =
1933N/A (introspectorClass == null) ? null : getClass("java.beans.BeanInfo");
1933N/A private static final Class<?> getPropertyDescriptorClass =
1933N/A (beanInfoClass == null) ? null : getClass("java.beans.PropertyDescriptor");
1933N/A
1933N/A private static final Method getBeanInfo =
1933N/A getMethod(introspectorClass, "getBeanInfo", Class.class);
1933N/A private static final Method getPropertyDescriptors =
1933N/A getMethod(beanInfoClass, "getPropertyDescriptors");
1933N/A private static final Method getPropertyName =
1933N/A getMethod(getPropertyDescriptorClass, "getName");
1933N/A private static final Method getReadMethod =
1933N/A getMethod(getPropertyDescriptorClass, "getReadMethod");
1933N/A
1933N/A private static Class<?> getClass(String name) {
1933N/A try {
1933N/A return Class.forName(name, true, null);
1933N/A } catch (ClassNotFoundException e) {
1933N/A return null;
1933N/A }
1933N/A }
1933N/A private static Method getMethod(Class<?> clazz,
1933N/A String name,
1933N/A Class<?>... paramTypes)
1933N/A {
1933N/A if (clazz != null) {
1933N/A try {
1933N/A return clazz.getMethod(name, paramTypes);
1933N/A } catch (NoSuchMethodException e) {
1933N/A throw new AssertionError(e);
1933N/A }
1933N/A } else {
1933N/A return null;
1933N/A }
1933N/A }
1933N/A
1933N/A private BeansHelper() { }
1933N/A
1933N/A /**
1933N/A * Returns {@code true} if java.beans is available.
1933N/A */
1933N/A static boolean isAvailable() {
1933N/A return introspectorClass != null;
1933N/A }
1933N/A
1933N/A /**
1933N/A * Invokes java.beans.Introspector.getBeanInfo(Class)
1933N/A */
1933N/A static Object getBeanInfo(Class<?> clazz) throws Exception {
1933N/A try {
1933N/A return getBeanInfo.invoke(null, clazz);
1933N/A } catch (InvocationTargetException e) {
1933N/A Throwable cause = e.getCause();
1933N/A if (cause instanceof Exception)
1933N/A throw (Exception)cause;
1933N/A throw new AssertionError(e);
1933N/A } catch (IllegalAccessException iae) {
1933N/A throw new AssertionError(iae);
1933N/A }
1933N/A }
1933N/A
1933N/A /**
1933N/A * Invokes java.beans.BeanInfo.getPropertyDescriptors()
1933N/A */
1933N/A static Object[] getPropertyDescriptors(Object bi) {
1933N/A try {
1933N/A return (Object[])getPropertyDescriptors.invoke(bi);
1933N/A } catch (InvocationTargetException e) {
1933N/A Throwable cause = e.getCause();
1933N/A if (cause instanceof RuntimeException)
1933N/A throw (RuntimeException)cause;
1933N/A throw new AssertionError(e);
1933N/A } catch (IllegalAccessException iae) {
1933N/A throw new AssertionError(iae);
1933N/A }
1933N/A }
1933N/A
1933N/A /**
1933N/A * Invokes java.beans.PropertyDescriptor.getName()
1933N/A */
1933N/A static String getPropertyName(Object pd) {
1933N/A try {
1933N/A return (String)getPropertyName.invoke(pd);
1933N/A } catch (InvocationTargetException e) {
1933N/A Throwable cause = e.getCause();
1933N/A if (cause instanceof RuntimeException)
1933N/A throw (RuntimeException)cause;
1933N/A throw new AssertionError(e);
1933N/A } catch (IllegalAccessException iae) {
1933N/A throw new AssertionError(iae);
1933N/A }
1933N/A }
1933N/A
1933N/A /**
1933N/A * Invokes java.beans.PropertyDescriptor.getReadMethod()
1933N/A */
1933N/A static Method getReadMethod(Object pd) {
1933N/A try {
1933N/A return (Method)getReadMethod.invoke(pd);
1933N/A } catch (InvocationTargetException e) {
1933N/A Throwable cause = e.getCause();
1933N/A if (cause instanceof RuntimeException)
1933N/A throw (RuntimeException)cause;
1933N/A throw new AssertionError(e);
1933N/A } catch (IllegalAccessException iae) {
1933N/A throw new AssertionError(iae);
1933N/A }
1933N/A }
1933N/A }
0N/A}