Introspector.java revision 5698
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/**
* This class contains the methods for performing all the tests needed to verify
* that a class represents a JMX compliant MBean.
*
* @since 1.5
*/
public class Introspector {
/*
* ------------------------------------------
* PRIVATE CONSTRUCTORS
* ------------------------------------------
*/
// private constructor defined to "hide" the default public constructor
private Introspector() {
// ------------------------------
// ------------------------------
}
/*
* ------------------------------------------
* PUBLIC METHODS
* ------------------------------------------
*/
/**
* Tell whether a MBean of the given class is a Dynamic MBean.
* This method does nothing more than returning
* <pre>
* javax.management.DynamicMBean.class.isAssignableFrom(c)
* </pre>
* This method does not check for any JMX MBean compliance:
* <ul><li>If <code>true</code> is returned, then instances of
* <code>c</code> are DynamicMBean.</li>
* <li>If <code>false</code> is returned, then no further
* assumption can be made on instances of <code>c</code>.
* In particular, instances of <code>c</code> may, or may not
* be JMX standard MBeans.</li>
* </ul>
* @param c The class of the MBean under examination.
* @return <code>true</code> if instances of <code>c</code> are
* Dynamic MBeans, <code>false</code> otherwise.
*
**/
// Check if the MBean implements the DynamicMBean interface
}
/**
* Basic method for testing that a MBean of a given class can be
* instantiated by the MBean server.<p>
* This method checks that:
* <ul><li>The given class is a concrete class.</li>
* <li>The given class exposes at least one public constructor.</li>
* </ul>
* If these conditions are not met, throws a NotCompliantMBeanException.
* @param c The class of the MBean we want to create.
* @exception NotCompliantMBeanException if the MBean class makes it
* impossible to instantiate the MBean from within the
* MBeanServer.
*
**/
public static void testCreation(Class<?> c)
throws NotCompliantMBeanException {
// Check if the class is a concrete class
final int mods = c.getModifiers();
throw new NotCompliantMBeanException("MBean class must be concrete");
}
// Check if the MBean has a public constructor
throw new NotCompliantMBeanException("MBean class must have public constructor");
}
}
throws NotCompliantMBeanException {
// Is DynamicMBean?
//
return;
// Is Standard MBean?
//
final Exception mbeanException;
try {
return;
} catch (NotCompliantMBeanException e) {
mbeanException = e;
}
// Is MXBean?
//
final Exception mxbeanException;
try {
return;
} catch (NotCompliantMBeanException e) {
mxbeanException = e;
}
"DynamicMBean, and neither follows the Standard MBean conventions (" +
throw new NotCompliantMBeanException(msg);
}
throws NotCompliantMBeanException {
if (mbean instanceof DynamicMBean)
return (DynamicMBean) mbean;
try {
} catch (NotCompliantMBeanException e) {
// Ignore exception - we need to check whether
// mbean is an MXBean first.
}
if (c != null)
return new StandardMBeanSupport(mbean, c);
try {
} catch (NotCompliantMBeanException e) {
// Ignore exception - we cannot decide whether mbean was supposed
// to be an MBean or an MXBean. We will call checkCompliance()
// to generate the appropriate exception.
}
if (c != null)
return new MXBeanSupport(mbean, c);
}
/**
* Basic method for testing if a given class is a JMX compliant MBean.
*
* @param baseClass The class to be tested
*
* @return <code>null</code> if the MBean is a DynamicMBean,
* the computed {@link javax.management.MBeanInfo} otherwise.
* @exception NotCompliantMBeanException The specified class is not a
* JMX compliant MBean
*/
throws NotCompliantMBeanException {
// ------------------------------
// ------------------------------
// Check if the MBean implements the MBean or the Dynamic
// MBean interface
return null;
}
throws NotCompliantMBeanException {
}
/**
* Basic method for testing if a given class is a JMX compliant
* Standard MBean. This method is only called by the legacy code
* in com.sun.management.jmx.
*
* @param baseClass The class to be tested.
*
* @param mbeanInterface the MBean interface that the class implements,
* or null if the interface must be determined by introspection.
*
* @return the computed {@link javax.management.MBeanInfo}.
* @exception NotCompliantMBeanException The specified class is not a
* JMX compliant Standard MBean
*/
public static synchronized MBeanInfo
Class<?> mbeanInterface)
throws NotCompliantMBeanException {
if (mbeanInterface == null)
}
private static <M> MBeanInfo
throws NotCompliantMBeanException {
}
/**
* Get the MBean interface implemented by a JMX Standard
* MBean class. This method is only called by the legacy
* code in "com.sun.management.jmx".
*
* @param baseClass The class to be tested.
*
* @return The MBean interface implemented by the MBean.
* Return <code>null</code> if the MBean is a DynamicMBean,
* or if no MBean interface is found.
*/
// Check if the given class implements the MBean interface
// or the Dynamic MBean interface
try {
return getStandardMBeanInterface(baseClass);
} catch (NotCompliantMBeanException e) {
return null;
}
}
/**
* Get the MBean interface implemented by a JMX Standard MBean class.
*
* @param baseClass The class to be tested.
*
* @return The MBean interface implemented by the Standard MBean.
*
* @throws NotCompliantMBeanException The specified class is
* not a JMX compliant Standard MBean.
*/
throws NotCompliantMBeanException {
if (mbeanInterface != null) break;
}
if (mbeanInterface != null) {
return mbeanInterface;
} else {
" is not a JMX compliant Standard MBean";
throw new NotCompliantMBeanException(msg);
}
}
/**
* Get the MXBean interface implemented by a JMX MXBean class.
*
* @param baseClass The class to be tested.
*
* @return The MXBean interface implemented by the MXBean.
*
* @throws NotCompliantMBeanException The specified class is
* not a JMX compliant MXBean.
*/
throws NotCompliantMBeanException {
try {
} catch (Exception e) {
throw throwException(baseClass,e);
}
}
/*
* ------------------------------------------
* PRIVATE METHODS
* ------------------------------------------
*/
/**
* Try to find the MBean interface corresponding to the class aName
* - i.e. <i>aName</i>MBean, from within aClass and its superclasses.
**/
private static <T> Class<? super T> findMBeanInterface(
for (int i=0;i<len;i++) {
}
}
return null;
}
return ImmutableDescriptor.EMPTY_DESCRIPTOR;
return descriptorForAnnotations(annots);
}
return ImmutableDescriptor.EMPTY_DESCRIPTOR;
for (Annotation a : annots) {
try {
} catch (RuntimeException e) {
// we don't expect this - except for possibly
// security exceptions?
// RuntimeExceptions shouldn't be "UndeclaredThrowable".
// anyway...
//
throw e;
} catch (Exception e) {
// we don't expect this
throw new UndeclaredThrowableException(e);
}
"Inconsistent values for descriptor field " + name +
throw new IllegalArgumentException(msg);
}
}
}
}
if (descriptorMap.isEmpty())
return ImmutableDescriptor.EMPTY_DESCRIPTOR;
else
return new ImmutableDescriptor(descriptorMap);
}
/**
* Throws a NotCompliantMBeanException or a SecurityException.
* @param notCompliant the class which was under examination
* @param cause the raeson why NotCompliantMBeanException should
* be thrown.
* @return nothing - this method always throw an exception.
* The return type makes it possible to write
* <pre> throw throwException(clazz,cause); </pre>
* @throws SecurityException - if cause is a SecurityException
* @throws NotCompliantMBeanException otherwise.
**/
throws NotCompliantMBeanException, SecurityException {
if (cause instanceof SecurityException)
throw (SecurityException) cause;
if (cause instanceof NotCompliantMBeanException)
throw (NotCompliantMBeanException)cause;
final NotCompliantMBeanException res =
throw res;
}
// Convert a value from an annotation element to a descriptor field value
// E.g. with @interface Foo {class value()} an annotation @Foo(String.class)
// will produce a Descriptor field value "java.lang.String"
// An annotation element cannot have a null value but never mind
if (x == null)
return null;
x instanceof String[])
return x;
// Remaining possibilities: array of primitive (e.g. int[]),
// enum, class, array of enum or class.
if (c.isArray()) {
if (c.getComponentType().isPrimitive())
return x;
return ss;
}
if (x instanceof Class<?>)
if (x instanceof Enum<?>)
// The only other possibility is that the value is another
// annotation, or that the language has evolved since this code
// was written. We don't allow for either of those currently.
// If it is indeed another annotation, then x will be a proxy
// with an unhelpful name like $Proxy2. So we extract the
// proxy's interface to use that in the exception message.
if (Proxy.isProxyClass(c))
throw new IllegalArgumentException("Illegal type for annotation " +
"element using @DescriptorKey: " + c.getName());
}
// This must be consistent with the check for duplicate field values in
// ImmutableDescriptor.union. But we don't expect to be called very
// often so this inefficient check should be enough.
}
/**
* Returns the XXMBean interface or null if no such interface exists
*
* @param c The interface to be tested
* @param clName The name of the class implementing this interface
*/
return c;
}
}
return null;
}
throws AttributeNotFoundException {
try {
} else if (complex instanceof CompositeData) {
} else {
// Java Beans introspection
//
if (BeansHelper.isAvailable()) {
break;
}
}
} else {
// Java Beans not available so use simple introspection
// to locate method
}
if (readMethod != null) {
}
throw new AttributeNotFoundException(
"Could not find the getter method for the property " +
element + " using the Java Beans introspector");
}
} catch (InvocationTargetException e) {
throw new IllegalArgumentException(e);
} catch (AttributeNotFoundException e) {
throw e;
} catch (Exception e) {
new AttributeNotFoundException(e.getMessage()), e);
}
}
/**
* A simple introspector that uses reflection to analyze a class and
* identify its "getter" methods. This class is intended for use only when
* Java Beans is not present (which implies that there isn't explicit
* information about the bean available).
*/
private static class SimpleIntrospector {
private SimpleIntrospector() { }
// cache to avoid repeated lookups
/**
* Returns the list of methods cached for the given class, or {@code null}
* if not cached.
*/
// return cached methods if possible
return cached;
}
return null;
}
/**
* Returns {@code true} if the given method is a "getter" method (where
* "getter" method is a public method of the form getXXX or "boolean
* isXXX")
*/
// ignore static methods
return false;
// boolean isXXX()
return (method.getReturnType() == boolean.class);
// getXXX()
return (method.getReturnType() != void.class);
}
return false;
}
/**
* Returns the list of "getter" methods for the given class. The list
* is ordered so that isXXX methods appear before getXXX methods - this
* is for compatability with the JavaBeans Introspector.
*/
// return cached result if available
if (cachedResult != null)
return cachedResult;
// get list of public methods, filtering out methods that have
// been overridden to return a more specific type.
// filter out the non-getter methods
if (isReadMethod(m)) {
// favor isXXX over getXXX
} else {
}
}
}
// add result to cache
return result;
}
/**
* Returns the "getter" to read the given property from the given class or
* {@code null} if no method is found.
*/
// first character in uppercase (compatability with JavaBeans)
return m;
}
}
return null;
}
}
/**
* A class that provides access to the JavaBeans Introspector and
* PropertyDescriptors without creating a static dependency on java.beans.
*/
private static class BeansHelper {
private static final Class<?> introspectorClass =
getClass("java.beans.Introspector");
private static final Class<?> beanInfoClass =
private static final Class<?> getPropertyDescriptorClass =
private static final Method getBeanInfo =
private static final Method getPropertyDescriptors =
private static final Method getPropertyName =
private static final Method getReadMethod =
try {
} catch (ClassNotFoundException e) {
return null;
}
}
Class<?>... paramTypes)
{
try {
} catch (NoSuchMethodException e) {
throw new AssertionError(e);
}
} else {
return null;
}
}
private BeansHelper() { }
/**
* Returns {@code true} if java.beans is available.
*/
static boolean isAvailable() {
return introspectorClass != null;
}
/**
* Invokes java.beans.Introspector.getBeanInfo(Class)
*/
try {
} catch (InvocationTargetException e) {
throw new AssertionError(e);
} catch (IllegalAccessException iae) {
throw new AssertionError(iae);
}
}
/**
* Invokes java.beans.BeanInfo.getPropertyDescriptors()
*/
try {
} catch (InvocationTargetException e) {
if (cause instanceof RuntimeException)
throw (RuntimeException)cause;
throw new AssertionError(e);
} catch (IllegalAccessException iae) {
throw new AssertionError(iae);
}
}
/**
* Invokes java.beans.PropertyDescriptor.getName()
*/
try {
} catch (InvocationTargetException e) {
if (cause instanceof RuntimeException)
throw (RuntimeException)cause;
throw new AssertionError(e);
} catch (IllegalAccessException iae) {
throw new AssertionError(iae);
}
}
/**
* Invokes java.beans.PropertyDescriptor.getReadMethod()
*/
try {
} catch (InvocationTargetException e) {
if (cause instanceof RuntimeException)
throw (RuntimeException)cause;
throw new AssertionError(e);
} catch (IllegalAccessException iae) {
throw new AssertionError(iae);
}
}
}
}