User: squirest Date: 02/01/28 19:33:22 Added: src/main/org/jboss/mx/capability StandardMBeanAdapter.java OperationProvider.java MBeanCapability.java MBeanAdapter.java AttributeProvider.java Log: first integration of new metadata and interceptor chain for core DynamicMBean capabilities Revision Changes Path 1.1 jmx/src/main/org/jboss/mx/capability/StandardMBeanAdapter.java Index: StandardMBeanAdapter.java =================================================================== /* * JBoss, the OpenSource J2EE webOS * * Distributable under LGPL license. * See terms of license at gnu.org. */ package org.jboss.mx.capability; import javax.management.IntrospectionException; import javax.management.MBeanAttributeInfo; import javax.management.MBeanConstructorInfo; import javax.management.MBeanNotificationInfo; import javax.management.MBeanOperationInfo; import javax.management.NotCompliantMBeanException; import javax.management.NotificationBroadcaster; import java.lang.reflect.Constructor; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.HashMap; import java.util.Iterator; import java.util.List; /** * An MBeanAdapter that uses a interface as a descriptor to populate AttributeProvider * and OperationProvider maps. * * @author <a href="mailto:[EMAIL PROTECTED]">Trevor Squires</a>. */ public class StandardMBeanAdapter extends MBeanAdapter { private MBeanConstructorInfo[] constructorInfo = null; private MBeanAttributeInfo[] attributeInfo = null; private MBeanOperationInfo[] operationInfo = null; public static StandardMBeanAdapter create(Class mbeanClass, Class mbeanInterface) throws NotCompliantMBeanException { return new StandardMBeanAdapter(mbeanClass, mbeanInterface); } public StandardMBeanAdapter(Class mbeanClass, Class mbeanInterface) throws NotCompliantMBeanException { setMBeanClassName(mbeanClass.getName()); setMBeanDescription("something I cooked up by introspecting"); build(mbeanClass, mbeanInterface); } protected void build(Class mbeanClass, Class mbeanInterface) throws NotCompliantMBeanException { // First build the constructors Constructor[] constructors = mbeanClass.getConstructors(); constructorInfo = new MBeanConstructorInfo[constructors.length]; for (int i = 0; i < constructors.length; ++i) { constructorInfo[i] = new MBeanConstructorInfo("MBean Constructor.", constructors[i]); } // Next we have to figure out how the methods in the mbean class map // to attributes and operations Method[] methods = mbeanInterface.getMethods(); HashMap getters = new HashMap(); HashMap setters = new HashMap(); List operInfo = new ArrayList(); List attrInfo = new ArrayList(); try { for (int i = 0; i < methods.length; ++i) { String methodName = methods[i].getName(); Class[] signature = methods[i].getParameterTypes(); Class returnType = methods[i].getReturnType(); if (methodName.startsWith("set") && signature.length == 1 && returnType == Void.TYPE) { String key = methodName.substring(3, methodName.length()); if (setters.get(key) != null) { throw new IntrospectionException("overloaded type for attribute: " + key); } setters.put(key, methods[i]); } else if (methodName.startsWith("get") && signature.length == 0 && returnType != Void.TYPE) { getters.put(methodName.substring(3, methodName.length()), methods[i]); } else if (methodName.startsWith("is") && signature.length == 0 && (returnType == Boolean.class || returnType == Boolean.TYPE)) { getters.put(methodName.substring(2, methodName.length()), methods[i]); } else { OperationProvider p = OperationProvider.create("MBeanOperation", methods[i]); operationProviders.put(operationKey(methods[i]), p); operInfo.add(p.getInfo()); } } Object[] keys = getters.keySet().toArray(); for (int i = 0; i < keys.length; ++i) { String attrName = (String) keys[i]; Method getter = (Method) getters.remove(attrName); Method setter = (Method) setters.remove(attrName); AttributeProvider p = AttributeProvider.create(attrName, "MBean Attribute", getter, setter); attributeProviders.put(attrName, p); attrInfo.add(p.getInfo()); } Iterator it = setters.keySet().iterator(); while (it.hasNext()) { String attrName = (String) it.next(); Method setter = (Method) setters.get(attrName); AttributeProvider p = AttributeProvider.create(attrName, "MBean Attribute", null, setter); attributeProviders.put(attrName, p); attrInfo.add(p.getInfo()); } // save away the attribute and operation info objects attributeInfo = (MBeanAttributeInfo[]) attrInfo.toArray(new MBeanAttributeInfo[0]); operationInfo = (MBeanOperationInfo[]) operInfo.toArray(new MBeanOperationInfo[0]); } catch (IntrospectionException e) { throw new NotCompliantMBeanException(e.getMessage()); } } protected MBeanConstructorInfo[] getConstructorInfo() { return constructorInfo; } protected MBeanAttributeInfo[] getAttributeInfo() { return attributeInfo; } protected MBeanOperationInfo[] getOperationInfo() { return operationInfo; } /** * what a frigging ugly hack. this is the only info * that you must have an instance to delegate the call to * on a standard MBean. * Thank god instanceof is false when the object is null... */ protected MBeanNotificationInfo[] getNotificationInfo() { if (defaultTarget instanceof NotificationBroadcaster) { return ((NotificationBroadcaster) defaultTarget).getNotificationInfo(); } else { return new MBeanNotificationInfo[0]; } } } 1.1 jmx/src/main/org/jboss/mx/capability/OperationProvider.java Index: OperationProvider.java =================================================================== /* * JBoss, the OpenSource J2EE webOS * * Distributable under LGPL license. * See terms of license at gnu.org. */ package org.jboss.mx.capability; import javax.management.IntrospectionException; import javax.management.MBeanException; import javax.management.MBeanOperationInfo; import javax.management.ReflectionException; import javax.management.RuntimeErrorException; import javax.management.RuntimeMBeanException; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; /** * This class dispatches invoke calls to a relevant target object * using the relevant Method. * * TODO WRT ModelMBeans - this class may serve as a good chokepoint * for resolving any ClassCast issues resulting from the fact that * we have <b>absolutely</b> no way of controlling differences between * the classloader used by target objects and the classloader used by * clients. * * @author <a href="mailto:[EMAIL PROTECTED]">Trevor Squires</a>. */ public class OperationProvider { private Method method = null; private Object target = null; private MBeanOperationInfo info = null; /** * create used by standard mbeans */ public static OperationProvider create(String description, Method method) throws IntrospectionException { if (null == method) { throw new IllegalArgumentException("method cannot be null"); } return new OperationProvider(method, new MBeanOperationInfo(description, method)); } public OperationProvider(Method method, MBeanOperationInfo info) { this.method = method; this.info = info; } public MBeanOperationInfo getInfo() { return info; } public Object invoke(Object defaultTarget, Object[] args) throws ReflectionException, MBeanException, RuntimeMBeanException, RuntimeErrorException { try { return method.invoke(null == target ? defaultTarget : target, args); } catch (InvocationTargetException e) { Throwable t = e.getTargetException(); if (t instanceof RuntimeException) { throw new RuntimeMBeanException((RuntimeException) t, "RuntimeException in MBean operation '" + info.getName() + "'"); } else if (t instanceof Exception) { throw new MBeanException((Exception) t, "Exception in MBean operation '" + info.getName() + "'"); } else // it's an error { throw new RuntimeErrorException((Error) t, "Error in MBean operation '" + info.getName() + "'"); } } catch (Exception e) // assume all other exceptions are reflection related - FIXME do we care if it's NullPointerException? { throw new ReflectionException(e, "Exception in OperationProvider for '" + info.getName() + "'"); } catch (Error e) { throw new RuntimeErrorException(e, "Error in OperationProvider for '" + info.getName() + "'"); } } } 1.1 jmx/src/main/org/jboss/mx/capability/MBeanCapability.java Index: MBeanCapability.java =================================================================== /* * JBoss, the OpenSource J2EE webOS * * Distributable under LGPL license. * See terms of license at gnu.org. */ package org.jboss.mx.capability; import javax.management.DynamicMBean; import javax.management.MBeanRegistration; import javax.management.NotCompliantMBeanException; import javax.management.NotificationBroadcaster; import javax.management.NotificationListener; import javax.management.PersistentMBean; import java.lang.reflect.Modifier; /** * The thrust what I do here is that in order to plug into a JMX MBeanServer, an * object needs a DynamicMBean interface and optionally an MBeanRegistration, * NotificationBroadcaster, NotificationListener etc. * * All of those interfaces are JMX capabilities. This class helps to figure out * what capabilties are present and whether they are compliant. * * Obviously, the most important capabiltiy is DynamicMBean. This is the capability * of being able to describe a management interface (MBeanInfo and related classes) and * act on that interface (invoke(), [set|get]Attribute() etc). * * The DynamicMBean capability is a lot like a mini MBeanServer without the registry. * With a DynamicMBean you're left to do all the work yourself do describe and act on * a management interface. * * So, as a convenience you have standard MBeans and model MBeans. * * I tend to think of the DynamicMBean capability of standard and model MBeans as two * different implementations of the same thing. They dispatch calls received via the * DynamicMBean interface to methods on a target object. The decision as to what * methods to call on the target object is based on a descriptor. * * In the case of a model MBean, the descriptor is constructed using ModelMBeanInfo and * Descriptor classes and is completely runtime-dynamic. I.e. the JMX implementation is * told exactly how to dispatch the DynamicMBean calls. * * For standard MBeans the descriptor is compiled into the supplied XXXMBean interface * that a target object implements. I.e. the JMX implementation is left to figure out * how to dispatch the DynamicMBean calls. * * @author <a href="mailto:[EMAIL PROTECTED]">Trevor Squires</a>. */ public class MBeanCapability { public static final int DYNAMIC_MBEAN = 0x321; public static final int STANDARD_MBEAN = 0x123; public static final int NOT_AN_MBEAN = 0xc0de; protected boolean isMBeanRegistration = false; protected boolean isNotificationBroadcaster = false; protected boolean isNotificationListener = false; protected boolean isPersistentMBean = false; protected int mbeanType = NOT_AN_MBEAN; protected String mbeanClassName = null; protected MBeanAdapter adapter = null; public static MBeanCapability of(Class mbeanClass) throws NotCompliantMBeanException { if (null == mbeanClass) { throw new IllegalArgumentException("Class cannot be null"); } if (Modifier.isAbstract(mbeanClass.getModifiers())) { throw new NotCompliantMBeanException("Class is abstract: " + mbeanClass.getName()); } if (mbeanClass.getConstructors().length == 0) { throw new NotCompliantMBeanException("Class has no public constructors: " + mbeanClass.getName()); } if (DynamicMBean.class.isAssignableFrom(mbeanClass)) { // Compliance check (this ought to disappear in next rev of JMX spec). // You can't implement both Standard and DynamicMBean. So, if this class // is a DynamicMBean and it directly implements a standard MBean interface // then it's not compliant. if (findStandardInterface(mbeanClass, mbeanClass.getInterfaces()) != null) { throw new NotCompliantMBeanException("Class supplies a standard MBean interface and is a DynamicMBean: " + mbeanClass.getName()); } return new MBeanCapability(mbeanClass); } else { Class concrete = mbeanClass; Class stdInterface = null; while (null != concrete) { stdInterface = findStandardInterface(concrete, concrete.getInterfaces()); if (null != stdInterface) { return new MBeanCapability(mbeanClass, StandardMBeanAdapter.create(mbeanClass, stdInterface)); } concrete = concrete.getSuperclass(); } } throw new NotCompliantMBeanException("Class does not expose a management interface: " + mbeanClass.getName()); } protected MBeanCapability(Class mbeanClass) { mbeanType = DYNAMIC_MBEAN; initBasics(mbeanClass); } // this is a bit too goofy... protected MBeanCapability(Class mbeanClass, MBeanAdapter adapter) { mbeanType = STANDARD_MBEAN; initBasics(mbeanClass); this.adapter = adapter; } protected void initBasics(Class mbeanClass) { mbeanClassName = mbeanClass.getName(); isMBeanRegistration = MBeanRegistration.class.isAssignableFrom(mbeanClass); isNotificationBroadcaster = NotificationBroadcaster.class.isAssignableFrom(mbeanClass); isNotificationListener = NotificationListener.class.isAssignableFrom(mbeanClass); isPersistentMBean = PersistentMBean.class.isAssignableFrom(mbeanClass); } public int getMBeanType() { return mbeanType; } public DynamicMBean getMBean(Object target) { switch (mbeanType) { case DYNAMIC_MBEAN: return (DynamicMBean) target; case STANDARD_MBEAN: adapter.setDefaultTarget(target); return adapter; default: throw new Error("FIXME: pathological sucky condition"); } } public String getMBeanClassName() { return mbeanClassName; } public boolean hasMBeanRegistration() { return isMBeanRegistration; } public MBeanRegistration getMBeanRegistration(Object target) { if (hasMBeanRegistration()) return (MBeanRegistration) target; else return null; } public boolean hasNotificationBroadcaster() { return isNotificationBroadcaster; } public NotificationBroadcaster getNotificationBroadcaster(Object target) { if (hasNotificationBroadcaster()) return (NotificationBroadcaster) target; else return null; } public boolean hasNotificationListener() { return isNotificationListener; } public NotificationListener getNotificationListener(Object target) { if (hasNotificationListener()) return (NotificationListener) target; else return null; } public boolean hasPersistentMBean() { return isPersistentMBean; } public PersistentMBean getPersistentMBean(Object target) { if (hasPersistentMBean()) return (PersistentMBean) target; else return null; } private static Class findStandardInterface(Class concrete, Class[] interfaces) { String stdName = concrete.getName() + "MBean"; Class retval = null; // look to see if this class implements MBean std interface for (int i = 0; i < interfaces.length; ++i) { if (interfaces[i].getName().equals(stdName)) { retval = interfaces[i]; break; } } return retval; } } 1.1 jmx/src/main/org/jboss/mx/capability/MBeanAdapter.java Index: MBeanAdapter.java =================================================================== /* * JBoss, the OpenSource J2EE webOS * * Distributable under LGPL license. * See terms of license at gnu.org. */ package org.jboss.mx.capability; import javax.management.Attribute; import javax.management.AttributeList; import javax.management.AttributeNotFoundException; import javax.management.DynamicMBean; import javax.management.InvalidAttributeValueException; import javax.management.MBeanAttributeInfo; import javax.management.MBeanConstructorInfo; import javax.management.MBeanException; import javax.management.MBeanInfo; import javax.management.MBeanNotificationInfo; import javax.management.MBeanOperationInfo; import javax.management.ReflectionException; import javax.management.RuntimeOperationsException; import java.lang.reflect.Method; import java.util.HashMap; import java.util.Map; import java.util.Iterator; /** * Base class for MBeanAdapters. Subclasses are expected to populate * AttributeProvider and OperationProvider maps. * * @author <a href="mailto:[EMAIL PROTECTED]">Trevor Squires</a>. */ public abstract class MBeanAdapter implements DynamicMBean { protected Object defaultTarget = null; protected Map attributeProviders = new HashMap(); protected Map operationProviders = new HashMap(); protected String mbeanClassName = null; protected String mbeanDescription = null; protected abstract MBeanConstructorInfo[] getConstructorInfo(); protected abstract MBeanAttributeInfo[] getAttributeInfo(); protected abstract MBeanOperationInfo[] getOperationInfo(); protected abstract MBeanNotificationInfo[] getNotificationInfo(); // bind an instance to this adapter. public void setDefaultTarget(Object target) { this.defaultTarget = target; } // needed to build mbeaninfo protected String getMBeanClassName() { return mbeanClassName; } // needed to build mbeaninfo protected void setMBeanClassName(String mbeanClassName) { this.mbeanClassName = mbeanClassName; } // needed to build mbeaninfo protected String getMBeanDescription() { return mbeanDescription; } // needed to build mbeaninfo protected void setMBeanDescription(String mbeanDescription) { this.mbeanDescription = mbeanDescription; } // standard way to build keys against operation providers protected final String operationKey(Method m) { Class[] sigTypes = m.getParameterTypes(); String[] signature = new String[sigTypes.length]; for (int i = 0; i < sigTypes.length; i++) { signature[i] = sigTypes[i].getName(); } return operationKey(m.getName(), signature); } // standard way to build keys against operation providers protected final String operationKey(String name, String[] signature) { StringBuffer buf = new StringBuffer(name); if (null == signature) { return buf.toString(); } for (int i = 0; i < signature.length; i++) { buf.append(';').append(signature[i]); // the ; char ensures uniqueness } return buf.toString(); } // -------------------------------------------------------------------------------- // DynamicMBean implementation public final Object getAttribute(String attribute) throws AttributeNotFoundException, MBeanException, ReflectionException { if (null == attribute) { throw new RuntimeOperationsException(new IllegalArgumentException("attribute cannot be null")); } try { AttributeProvider p = (AttributeProvider) attributeProviders.get(attribute); return p.get(defaultTarget); } catch (NullPointerException e) { throw new AttributeNotFoundException("Unable to locate provider for attribute: '" + attribute + "'"); } } public final void setAttribute(Attribute attribute) throws AttributeNotFoundException, InvalidAttributeValueException, MBeanException, ReflectionException { if (null == attribute) { throw new RuntimeOperationsException(new IllegalArgumentException("attribute cannot be null")); } try { AttributeProvider p = (AttributeProvider) attributeProviders.get(attribute.getName()); p.set(defaultTarget, attribute.getValue()); } catch (NullPointerException e) { throw new AttributeNotFoundException("Unable to locate provider for attribute: '" + attribute.getName() + "'"); } } public final AttributeList getAttributes(String[] attributes) { if (null == attributes) { throw new RuntimeOperationsException(new IllegalArgumentException("attributes array cannot be null")); } AttributeList list = new AttributeList(); for (int i = 0; i < attributes.length; i++) { try { list.add(new Attribute(attributes[i], getAttribute(attributes[i]))); } catch (Throwable e) { // QUERY - do we *really* just ignore all problems? } } return list; } public final AttributeList setAttributes(AttributeList attributes) { if (null == attributes) { throw new RuntimeOperationsException(new IllegalArgumentException("attribute list cannot be null")); } AttributeList list = new AttributeList(); for (Iterator iterator = attributes.iterator(); iterator.hasNext();) { Attribute toSet = (Attribute) iterator.next(); try { setAttribute(toSet); list.add(toSet); } catch (Throwable e) { // QUERY - do we *really* just ignore all problems? } } return list; } public final Object invoke(String operationName, Object[] params, String[] signature) throws MBeanException, ReflectionException { if (null == operationName) { throw new RuntimeOperationsException(new IllegalArgumentException("operation name cannot be null")); } try { OperationProvider p = (OperationProvider) operationProviders.get(operationKey(operationName, signature)); return p.invoke(defaultTarget, params); } catch (NullPointerException e) { throw new ReflectionException(new NoSuchMethodException("Unable to locate provider for method key: " + operationKey(operationName, signature))); } } public final MBeanInfo getMBeanInfo() { return new MBeanInfo(this.getMBeanClassName(), this.getMBeanDescription(), this.getAttributeInfo(), this.getConstructorInfo(), this.getOperationInfo(), this.getNotificationInfo()); } } 1.1 jmx/src/main/org/jboss/mx/capability/AttributeProvider.java Index: AttributeProvider.java =================================================================== /* * JBoss, the OpenSource J2EE webOS * * Distributable under LGPL license. * See terms of license at gnu.org. */ package org.jboss.mx.capability; import javax.management.AttributeNotFoundException; import javax.management.IntrospectionException; import javax.management.InvalidAttributeValueException; import javax.management.MBeanAttributeInfo; import javax.management.MBeanException; import javax.management.ReflectionException; import javax.management.RuntimeErrorException; import javax.management.RuntimeMBeanException; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; /** * This class dispatches get and set calls to a relevant target object * using the relevant Method. * * TODO WRT ModelMBeans - this class may serve as a good chokepoint * for resolving any ClassCast issues resulting from the fact that * we have <b>absolutely</b> no way of controlling differences between * the classloader used by target objects and the classloader used by * clients. * * @author <a href="mailto:[EMAIL PROTECTED]">Trevor Squires</a>. */ public class AttributeProvider { private Method setter = null; private Method getter = null; private Object setterTarget = null; private Object getterTarget = null; private MBeanAttributeInfo info = null; public static AttributeProvider create(String name, String description, Method getter, Method setter) throws IntrospectionException { return new AttributeProvider(name, getter, setter, new MBeanAttributeInfo(name, description, getter, setter)); } protected AttributeProvider(String name, Method getter, Method setter, MBeanAttributeInfo info) { if (null == info || (null == getter && null == setter)) { throw new IllegalArgumentException("MBeanAttributeInfo and at least one of getter and setter must be present"); } this.getter = getter; this.setter = setter; this.info = info; } public void set(Object defaultTarget, Object value) throws ReflectionException, AttributeNotFoundException, InvalidAttributeValueException, MBeanException, RuntimeMBeanException, RuntimeErrorException { try { setter.invoke(null == setterTarget ? defaultTarget : setterTarget, new Object[]{value}); } catch (InvocationTargetException e) { Throwable t = e.getTargetException(); if (t instanceof RuntimeException) { throw new RuntimeMBeanException((RuntimeException) t, "RuntimeException in MBean when setting attribute '" + info.getName() + "'"); } else if (t instanceof Exception) { throw new MBeanException((Exception) t, "Exception in MBean when setting attribute '" + info.getName() + "'"); } else // it's an error { throw new RuntimeErrorException((Error) t, "Error in MBean when setting attribute '" + info.getName() + "'"); } } catch (IllegalArgumentException e) { String valueType = (null == value) ? "<null value>" : value.getClass().getName(); throw new InvalidAttributeValueException("Attribute value mismatch while setting '" + info.getName() + "': " + info.getType() + " != " + valueType); } catch (NullPointerException e) { throw new AttributeNotFoundException("Writable attribute '" + info.getName() + "' not found"); } catch (Exception e) // assume all other exceptions are reflection related { throw new ReflectionException(e, "Exception in AttributeProvider for setting '" + info.getName() + "'"); } catch (Error e) { throw new RuntimeErrorException(e, "Error in AttributeProvider for setting '" + info.getName() + "'"); } } public Object get(Object defaultTarget) throws ReflectionException, AttributeNotFoundException, MBeanException, RuntimeMBeanException, RuntimeErrorException { try { return getter.invoke(null == getterTarget ? defaultTarget : getterTarget, new Object[0]); } catch (InvocationTargetException e) { Throwable t = e.getTargetException(); if (t instanceof RuntimeException) { throw new RuntimeMBeanException((RuntimeException) t, "RuntimeException in MBean when getting attribute '" + info.getName() + "'"); } else if (t instanceof Exception) { throw new MBeanException((Exception) t, "Exception in MBean when getting attribute '" + info.getName() + "'"); } else // it's an error { throw new RuntimeErrorException((Error) t, "Error in MBean when getting attribute '" + info.getName() + "'"); } } catch (NullPointerException e) { throw new AttributeNotFoundException("Readable attribute '" + info.getName() + "' not found"); } catch (Exception e) // assume all other exceptions are reflection related { throw new ReflectionException(e, "Exception in AttributeProvider for getting '" + info.getName() + "'"); } catch (Error e) { throw new RuntimeErrorException(e, "Error in AttributeProvider for getting '" + info.getName() + "'"); } } public MBeanAttributeInfo getInfo() { return info; } }
_______________________________________________ Jboss-development mailing list [EMAIL PROTECTED] https://lists.sourceforge.net/lists/listinfo/jboss-development