/* * @(#)Introspector.java 1.68 03/12/19 * * Copyright 2004 Sun Microsystems, Inc. All rights reserved. * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms. */ package com.sun.jmx.mbeanserver; // Java import import java.lang.reflect.Constructor; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.util.ArrayList; import java.util.List; import java.util.Iterator; // RI Import import javax.management.IntrospectionException; import javax.management.MBeanAttributeInfo; import javax.management.MBeanConstructorInfo; import javax.management.MBeanInfo; import javax.management.MBeanOperationInfo; import javax.management.MBeanParameterInfo; import javax.management.NotCompliantMBeanException; /** * 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 VARIABLES * ------------------------------------------ */ private static final String attributeDescription = "Attribute exposed for management"; private static final String operationDescription = "Operation exposed for management"; private static final String constructorDescription = "Public constructor of the MBean"; private static final String mbeanInfoDescription = "Information on the management interface of the MBean"; /* * ------------------------------------------ * 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 *
     * javax.management.DynamicMBean.class.isAssignableFrom(c)
     * 
* This method does not check for any JMX MBean compliance: * * @param c The class of the MBean under examination. * @return true if instances of c are * Dynamic MBeans, false otherwise. * * @since.unbundled JMX RI 1.2 **/ public static final boolean isDynamic(final Class c) { // Check if the MBean implements the DynamicMBean interface return javax.management.DynamicMBean.class.isAssignableFrom(c); } /** * Basic method for testing that a MBean of a given class can be * instantiated by the MBean server.

* This method checks that: *

* 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. * * @since.unbundled JMX RI 1.2 **/ public static void testCreation(Class c) throws NotCompliantMBeanException { // Check if the class is a concrete class final int mods = c.getModifiers(); if (Modifier.isAbstract(mods) || Modifier.isInterface(mods)) { throw new NotCompliantMBeanException("MBean class must be concrete"); } // Check if the MBean has a public constructor final Constructor[] consList = c.getConstructors(); if (consList.length == 0) { throw new NotCompliantMBeanException("MBean class must have public constructor"); } } /** * Basic method for testing if a given class is a JMX compliant MBean. * * @param baseClass The class to be tested * * @return null if the MBean is a DynamicMBean, * the computed {@link javax.management.MBeanInfo} otherwise. * @exception NotCompliantMBeanException The specified class is not a * JMX compliant MBean */ public static MBeanInfo testCompliance(Class baseClass) throws NotCompliantMBeanException { // ------------------------------ // ------------------------------ // Check if the MBean implements the MBean or the Dynamic // MBean interface if (isDynamic(baseClass)) return null; return testCompliance(baseClass, null); } /** * Basic method for testing if a given class is a JMX compliant MBean. * * @param baseClass The class to be tested * * @return null if the MBean is a DynamicMBean, * the computed {@link javax.management.MBeanInfo} otherwise. * @exception NotCompliantMBeanException The specified class is not a * JMX compliant MBean */ static MBeanInfo testCompliance(final Class baseClass, Class mbeanInterface) throws NotCompliantMBeanException { if (baseClass.isInterface()) throw new NotCompliantMBeanException(baseClass.getName() + " must be a class."); // ------------------------------ // ------------------------------ if (mbeanInterface == null) // No interface specified: look for default MBean interface. mbeanInterface = getStandardMBeanInterface(baseClass); else if (! mbeanInterface.isAssignableFrom(baseClass)) { // specified interface not implemented by given class final String msg = baseClass.getName() + " does not implement the " + mbeanInterface.getName() + " interface"; throw new NotCompliantMBeanException(msg); } else if (! mbeanInterface.isInterface()) { // Base class X, but XMBean is not an interface final String msg = baseClass.getName() + ": " + mbeanInterface.getName() + " is not an interface"; throw new NotCompliantMBeanException(msg); } if (mbeanInterface == null) { // Error: MBean does not implement javax.management.DynamicMBean // nor MBean interface final String baseClassName = baseClass.getName(); final String msg = baseClassName + " does not implement the " + baseClassName + "MBean interface or the DynamicMBean interface"; throw new NotCompliantMBeanException(msg); } final int mods = mbeanInterface.getModifiers(); if (!Modifier.isPublic(mods)) throw new NotCompliantMBeanException(mbeanInterface.getName() + " implemented by " + baseClass.getName() + " must be public"); return (introspect(baseClass, mbeanInterface)); } /** * 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 MBean. * Return null if the MBean is a DynamicMBean, * or if no MBean interface is found. * */ public static Class getMBeanInterface(Class baseClass) { // ------------------------------ // ------------------------------ // Check if the MBean implements the MBean or the Dynamic // MBean interface if (isDynamic(baseClass)) return null; return getStandardMBeanInterface(baseClass); } /** * 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 MBean. * Return null if no MBean interface is found. * Does not check whether the MBean is a DynamicMBean. * */ static Class getStandardMBeanInterface(Class baseClass) { // ------------------------------ // ------------------------------ Class current = baseClass; Class mbeanInterface = null; while (current != null) { mbeanInterface = findMBeanInterface(current, current.getName()); if (mbeanInterface != null) break; current = current.getSuperclass(); } return mbeanInterface; } /* * ------------------------------------------ * PRIVATE METHODS * ------------------------------------------ */ /** * Try to find the MBean interface corresponding to the class aName * - i.e. aNameMBean, from within aClass and its superclasses. **/ private static Class findMBeanInterface(Class aClass, String aName) { Class current = aClass; while (current != null) { final Class[] interfaces = current.getInterfaces(); final int len = interfaces.length; for (int i=0;i*/ attributes = new ArrayList/**/(); List/**/ operations = new ArrayList/**/(); Method methodList[] = beanClass.getMethods(); // Now analyze each method. for (int i = 0; i < methodList.length; i++) { Method method = methodList[i]; String name = method.getName(); Class argTypes[] = method.getParameterTypes(); Class resultType = method.getReturnType(); int argCount = argTypes.length; try { final MBeanAttributeInfo attr; if (name.startsWith("get") && !name.equals("get") && argCount == 0 && !resultType.equals(void.class)) { // if the method is "T getX()" it is a getter attr = new MBeanAttributeInfo(name.substring(3), attributeDescription, method, null); } else if (name.startsWith("set") && !name.equals("set") && argCount == 1 && resultType.equals(void.class)) { // if the method is "void setX(T x)" it is a setter attr = new MBeanAttributeInfo(name.substring(3), attributeDescription, null, method); } else if (name.startsWith("is") && !name.equals("is") && argCount == 0 && resultType.equals(boolean.class)) { // if the method is "boolean isX()" it is a getter attr = new MBeanAttributeInfo(name.substring(2), attributeDescription, method, null); } else { // in all other cases it is an operation attr = null; } if (attr != null) { if (testConsistency(attributes, attr)) attributes.add(attr); } else { final MBeanOperationInfo oper = new MBeanOperationInfo(operationDescription, method); operations.add(oper); } } catch (IntrospectionException e) { // Should not happen (MBeanAttributeInfo constructor) error("introspect", e); } } return constructResult(baseClass, attributes, operations); } /** * Checks if the types and the signatures of * getters/setters/operations are conform to the MBean design * patterns. * * Error cases: * - It exposes a method void Y getXX() AND a method void setXX(Z) * (parameter type mismatch) * - It exposes a method void setXX(Y) AND a method void setXX(Z) * (parameter type mismatch) * - It exposes a boolean isXX() method AND a YY getXX() or a void setXX(Y). * Returns false if the attribute is already in attributes List */ private static boolean testConsistency(List/**/attributes, MBeanAttributeInfo attr) throws NotCompliantMBeanException { for (Iterator it = attributes.iterator(); it.hasNext(); ) { MBeanAttributeInfo mb = (MBeanAttributeInfo) it.next(); if (mb.getName().equals(attr.getName())) { if ((attr.isReadable() && mb.isReadable()) && (attr.isIs() != mb.isIs())) { final String msg = "Conflicting getters for attribute " + mb.getName(); throw new NotCompliantMBeanException(msg); } if (!mb.getType().equals(attr.getType())) { if (mb.isWritable() && attr.isWritable()) { final String msg = "Type mismatch between parameters of set" + mb.getName() + " methods"; throw new NotCompliantMBeanException(msg); } else { final String msg = "Type mismatch between parameters of get or is" + mb.getName() + ", set" + mb.getName() + " methods"; throw new NotCompliantMBeanException(msg); } } if (attr.isReadable() && mb.isReadable()) { return false; } if (attr.isWritable() && mb.isWritable()) { return false; } } } return true; } /** * Discovers the constructors of the MBean */ static MBeanConstructorInfo[] getConstructors(Class baseClass) { Constructor[] consList = baseClass.getConstructors(); List constructors = new ArrayList(); // Now analyze each Constructor. for (int i = 0; i < consList.length; i++) { Constructor constructor = consList[i]; MBeanConstructorInfo mc = null; try { mc = new MBeanConstructorInfo(constructorDescription, constructor); } catch (Exception ex) { mc = null; } if (mc != null) { constructors.add(mc); } } // Allocate and populate the result array. MBeanConstructorInfo[] resultConstructors = new MBeanConstructorInfo[constructors.size()]; constructors.toArray(resultConstructors); return resultConstructors; } /** * Constructs the MBeanInfo of the MBean. */ private static MBeanInfo constructResult(Class baseClass, List/**/ attributes, List/**/ operations) { final int len = attributes.size(); final MBeanAttributeInfo[] attrlist = new MBeanAttributeInfo[len]; attributes.toArray(attrlist); final ArrayList mergedAttributes = new ArrayList(); for (int i=0;i