/* * JBoss, Home of Professional Open Source. * Copyright 2008, Red Hat Middleware LLC, and individual contributors * as indicated by the @author tags. See the copyright.txt file in the * distribution for a full listing of individual contributors. * * This is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * This software 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this software; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. */ package org.jboss.mx.server; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.util.ArrayList; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import javax.management.Attribute; import javax.management.AttributeList; import javax.management.AttributeNotFoundException; import javax.management.Descriptor; import javax.management.InvalidAttributeValueException; import javax.management.JMRuntimeException; import javax.management.ListenerNotFoundException; import javax.management.MBeanAttributeInfo; import javax.management.MBeanException; import javax.management.MBeanInfo; import javax.management.MBeanNotificationInfo; import javax.management.MBeanOperationInfo; import javax.management.MBeanParameterInfo; import javax.management.MBeanRegistration; import javax.management.MBeanServer; import javax.management.NotificationBroadcaster; import javax.management.NotificationEmitter; import javax.management.NotificationFilter; import javax.management.NotificationListener; import javax.management.ObjectName; import javax.management.ReflectionException; import javax.management.RuntimeErrorException; import javax.management.RuntimeMBeanException; import javax.management.RuntimeOperationsException; import javax.management.modelmbean.ModelMBeanInfo; import javax.management.modelmbean.ModelMBeanInfoSupport; import org.jboss.logging.Logger; import org.jboss.mx.interceptor.AttributeDispatcher; import org.jboss.mx.interceptor.Interceptor; import org.jboss.mx.interceptor.ReflectedDispatcher; import org.jboss.mx.metadata.StandardMetaData; import org.jboss.mx.modelmbean.ModelMBeanConstants; import org.jboss.mx.server.InvocationContext.NullDispatcher; import org.jboss.mx.server.registry.MBeanEntry; import org.jboss.util.Strings; /** * A base MBeanInvoker class that provides common state * * @author <a href="mailto:juha@jboss.org">Juha Lindfors</a>. * @author <a href="mailto:scott.stark@jboss.org">Scott Stark</a>. * @author <a href="mailto:dimitris@jboss.org">Dimitris Andreadis</a>. * @version $Revision: 81026 $ */ public abstract class AbstractMBeanInvoker implements MBeanInvoker { /** * Used to propagate the MBeanEntry during the preRegister callback */ static ThreadLocal preRegisterInfo = new ThreadLocal(); // Attributes ---------------------------------------------------- /** * The target object for this invoker. */ private Object resource = null; /** * The mbean server register entry used for the TCL */ protected MBeanEntry resourceEntry = null; /** * Whether this is a dynamic resource */ protected boolean dynamicResource = true; /** * The metadata describing this MBean. */ protected MBeanInfo info = null; protected Map attributeContextMap = new HashMap(); protected Map operationContextMap = new HashMap(); protected Map constructorContextMap = new HashMap(); protected InvocationContext getMBeanInfoCtx = null; protected InvocationContext preRegisterCtx = null; protected InvocationContext postRegisterCtx = null; protected InvocationContext preDeregisterCtx = null; protected InvocationContext postDeregisterCtx = null; // TODO: allow to config invoker specific logs // : multitarget mbean for invoker + log? protected Logger log = Logger.getLogger(AbstractMBeanInvoker.class); /** * The MBeanServer passed in to preRegister */ private MBeanServer server; /** * Set the MBeanEntry thread local value. * @param entry - the entry that will be used on successful registration */ public static void setMBeanEntry(MBeanEntry entry) { preRegisterInfo.set(entry); } /** * An accessor for the MBeanEntry thread local * @return */ public static MBeanEntry getMBeanEntry() { return (MBeanEntry) preRegisterInfo.get(); } // Constructors -------------------------------------------------- /** * Constructs a new invoker. */ public AbstractMBeanInvoker() { } /** * Constructs a new invoker with a given target resource. */ public AbstractMBeanInvoker(Object resource) { this.resource = resource; } /** * Constructs an invoker with the target resource entry. * @param resourceEntry */ public AbstractMBeanInvoker(MBeanEntry resourceEntry) { this.resourceEntry = resourceEntry; this.resource = resourceEntry.getResourceInstance(); } // DynamicMBean implementation ----------------------------------- /** * Invokes the target resource. The default invocation used by this invoker * implement sends the invocation through a stack of interceptors before * reaching the target method. * @param operationName name of the target method * @param args argumetns for the target method * @param signature signature of the target method * @throws MBeanException if the target method raised a hecked exception * @throws ReflectionException if there was an error trying to resolve or * invoke the target method * @throws RuntimeMBeanException if the target method raised an unchecked * exception */ public Object invoke(String operationName, Object[] args, String[] signature) throws MBeanException, ReflectionException { // TODO: __JBOSSMX_INVOCATION if (operationName == null) throw new ReflectionException(new IllegalArgumentException("Null operation name")); // If we have dynamic capability, check for a dynamic invocation String opName = operationName; if (dynamicResource) { int dot = operationName.lastIndexOf('.'); if (dot != -1) { if (dot < operationName.length() - 1) opName = operationName.substring(dot + 1); } } // get the server side invocation context OperationKey key = new OperationKey(opName, signature); InvocationContext ctx = (InvocationContext) operationContextMap.get(key); // if the server does not contain this context, we do not have the operation if (ctx == null) { // This is just stupid - the RI is fundamentally broken and hence the spec boolean operationExists = false; if (dynamicResource) { for (Iterator i = operationContextMap.keySet().iterator(); i.hasNext();) { OperationKey thisKey = (OperationKey) i.next(); if (opName.equals(thisKey.keys[0])) { operationExists = true; break; } } if (operationExists) throw new ReflectionException(new NoSuchMethodException("Unable to find operation " + operationName + getSignatureString(signature))); } throw new ReflectionException(new IllegalArgumentException("Unable to find operation " + operationName + getSignatureString(signature))); } // create the invocation object Invocation invocation = new Invocation(); // copy the server's invocation context to the invocation invocation.addContext(ctx); // set the invocation's entry point invocation.setType(InvocationContext.OP_INVOKE); // Use the passed operation invocation.setName(operationName); // set the args invocation.setArgs(args); override(invocation); ClassLoader mbeanTCL = resourceEntry.getClassLoader(); final ClassLoader ccl = TCLAction.UTIL.getContextClassLoader(); boolean setCl = ccl != mbeanTCL && mbeanTCL != null; if (setCl) { TCLAction.UTIL.setContextClassLoader(mbeanTCL); } try { // the default invocation implementation will invoke each interceptor // declared in the invocation context before invoking the target method return invocation.invoke(); } catch (MBeanException e) { throw e; } catch (ReflectionException e) { throw e; } catch (JMRuntimeException e) { throw e; } catch (Throwable t) { rethrowAsMBeanException(t); return null; } // TODO: should be fixed by adding invocation return value object finally { Descriptor descriptor = invocation.getDescriptor(); if (descriptor != null) { ctx.setDescriptor(descriptor); if (dynamicResource && ModelMBeanConstants.OPERATION_DESCRIPTOR.equals(descriptor.getFieldValue(ModelMBeanConstants.DESCRIPTOR_TYPE))) { ModelMBeanInfoSupport minfo = (ModelMBeanInfoSupport) info; minfo.setDescriptor(descriptor, ModelMBeanConstants.OPERATION_DESCRIPTOR); } } invocation.setArgs(null); invocation.setDescriptor(null); invocation.setDispatcher(null); if (setCl) { TCLAction.UTIL.setContextClassLoader(ccl); } } } /** * Returns an attribte value. The request for the value is forced through a * set of interceptors before the value is returned. * @param attribute attribute name * @return attribute value * @throws AttributeNotFoundException if the requested attribute is not part * of the MBean's management interface * @throws MBeanException if retrieving the attribute value causes an * application exception * @throws ReflectionException if there was an error trying to retrieve the * attribute value */ public Object getAttribute(String attribute) throws AttributeNotFoundException, MBeanException, ReflectionException { // TODO: __JBOSSMX_INVOCATION if (attribute == null) throw new RuntimeOperationsException(new IllegalArgumentException("Cannot get null attribute")); // lookup the server side invocation context InvocationContext ctx = (InvocationContext) attributeContextMap.get(attribute); // if we don't have a server side invocation context for the attribute, // it does not exist as far as we are concerned if (ctx == null) throw new AttributeNotFoundException("not found: " + attribute); if (ctx.isReadable() == false) throw new AttributeNotFoundException("Attribute '" + attribute + "' found, but it is not readable"); // create the invocation object Invocation invocation = new Invocation(); // copy the server's invocation context to the invocation invocation.addContext(ctx); // indicate the invocation access point was getAttribute() method invocation.setType(InvocationContext.OP_GETATTRIBUTE); invocation.setArgs(null); override(invocation); ClassLoader mbeanTCL = resourceEntry.getClassLoader(); final ClassLoader ccl = TCLAction.UTIL.getContextClassLoader(); boolean setCl = ccl != mbeanTCL && mbeanTCL != null; if (setCl) { TCLAction.UTIL.setContextClassLoader(mbeanTCL); } try { return invocation.invoke(); } catch (AttributeNotFoundException e) { throw e; } catch (MBeanException e) { throw e; } catch (ReflectionException e) { throw e; } catch (JMRuntimeException e) { throw e; } catch (Throwable t) { rethrowAsMBeanException(t); return null; } // TODO: should be fixed by adding invocation return value object finally { Descriptor attrDesc = invocation.getDescriptor(); ctx.setDescriptor(attrDesc); updateAttributeInfo(attrDesc); if (setCl) { TCLAction.UTIL.setContextClassLoader(ccl); } } } /** * Sets an attribute value. The operation is forced through a set of * interceptors before the new value for the attribute is set. * @param attribute new attribute value * @throws AttributeNotFoundException if the requested attribute is not part * of the MBean's management interface * @throws InvalidAttributeValueException if the attribute contains a value * not suitable for the attribute * @throws MBeanException if setting the attribute value causes an * application exception * @throws ReflectionException if there was an error trying to set the * attribute value. */ public void setAttribute(Attribute attribute) throws AttributeNotFoundException, InvalidAttributeValueException, MBeanException, ReflectionException { // TODO: __JBOSSMX_INVOCATION if (attribute == null) throw new InvalidAttributeValueException("null attribute"); // lookup the server side invocation context String name = attribute.getName(); InvocationContext ctx = (InvocationContext) attributeContextMap.get(name); // if we don't have a server side invocation context for the attribute, // it does not exist as far as we are concerned if (ctx == null) throw new AttributeNotFoundException("not found: " + name); else if (ctx.isWritable() == false) { throw new AttributeNotFoundException("Attribute '" + name + "' is not writable"); } // create the invocation object Invocation invocation = new Invocation(); // copy the server context to the invocation invocation.addContext(ctx); // indicate the access point as setAttribute() invocation.setType(InvocationContext.OP_SETATTRIBUTE); // set the attribute value as the argument invocation.setArgs(new Object[]{attribute.getValue()}); override(invocation); ClassLoader mbeanTCL = resourceEntry.getClassLoader(); final ClassLoader ccl = TCLAction.UTIL.getContextClassLoader(); boolean setCl = ccl != mbeanTCL && mbeanTCL != null; if (setCl) { TCLAction.UTIL.setContextClassLoader(mbeanTCL); } try { // the default invocation implementation will invoke each interceptor // declared in the invocation context before invoking the target method invocation.invoke(); } catch (AttributeNotFoundException e) { throw e; } catch (InvalidAttributeValueException e) { throw e; } catch (MBeanException e) { throw e; } catch (ReflectionException e) { throw e; } catch (JMRuntimeException e) { throw e; } catch (Throwable t) { rethrowAsMBeanException(t); } // TODO: should be fixed by adding invocation return value object finally { /* Obtain the updated attribute descriptor and propagate to the invocation context and ModelMBeanInfo. The latter is required in order for getMBeanInfo() to show an updated view. */ Descriptor attrDesc = invocation.getDescriptor(); ctx.setDescriptor(attrDesc); updateAttributeInfo(attrDesc); if (setCl) { TCLAction.UTIL.setContextClassLoader(ccl); } } } public MBeanInfo getMBeanInfo() { // create the invocation object Invocation invocation = new Invocation(getMBeanInfoCtx); // set the invocation's access point as getMBeanInfo() invocation.setType(InvocationContext.OP_GETMBEANINFO); if (resourceEntry == null) resourceEntry = getMBeanEntry(); ClassLoader mbeanTCL = resourceEntry.getClassLoader(); final ClassLoader ccl = TCLAction.UTIL.getContextClassLoader(); boolean setCl = ccl != mbeanTCL && mbeanTCL != null; if (setCl) { TCLAction.UTIL.setContextClassLoader(mbeanTCL); } try { MBeanInfo info = (MBeanInfo) invocation.invoke(); return info; } catch (JMRuntimeException e) { throw e; } catch (Throwable t) { rethrowAsRuntimeMBeanException(t); return null; } finally { if (setCl) { TCLAction.UTIL.setContextClassLoader(ccl); } } } public AttributeList getAttributes(java.lang.String[] attributes) { if (attributes == null) throw new IllegalArgumentException("null array"); AttributeList list = new AttributeList(); for (int i = 0; i < attributes.length; ++i) { try { list.add(new Attribute(attributes[i], getAttribute(attributes[i]))); } catch (Throwable ignored) { // if the attribute could not be retrieved, skip it } } return list; } public AttributeList setAttributes(AttributeList attributes) { if (attributes == null) throw new IllegalArgumentException("null list"); AttributeList results = new AttributeList(); Iterator it = attributes.iterator(); while (it.hasNext()) { Attribute attr = (Attribute) it.next(); try { setAttribute(attr); results.add(attr); } catch (Throwable ignored) { // if unable to set the attribute, skip it if (log.isTraceEnabled()) log.trace("Unhandled setAttribute() for attribute: " + attr.getName(), ignored); } } return results; } // MBeanRegistration implementation ------------------------------ /** * Initializes this invoker. At the registration time we can be sure that all * of the metadata is available and initialize the invoker and cache the data * accordingly. <p> * * Subclasses that override the <tt>preRegister</tt> method must make sure * they call <tt>super.preRegister()</tt> in their implementation to ensure * proper initialization of the invoker. */ public ObjectName preRegister(MBeanServer server, ObjectName name) throws Exception { this.resourceEntry = (MBeanEntry) preRegisterInfo.get(); this.server = server; ObjectName mbeanName = null; Descriptor mbeanDescriptor = null; if( info instanceof ModelMBeanInfo ) { ModelMBeanInfo minfo = (ModelMBeanInfo) info; try { mbeanDescriptor = minfo.getDescriptor("", ModelMBeanConstants.MBEAN_DESCRIPTOR); String type = (String) mbeanDescriptor.getFieldValue( ModelMBeanConstants.MBEAN_SERVER_INJECTION_TYPE); if( type != null ) { inject(ModelMBeanConstants.MBEAN_SERVER_INJECTION_TYPE, type, MBeanServer.class, getServer()); } } catch (MBeanException e) { log.warn("Failed to obtain descriptor: "+ModelMBeanConstants.MBEAN_DESCRIPTOR, e); } } ClassLoader mbeanTCL = resourceEntry.getClassLoader(); final ClassLoader ccl = TCLAction.UTIL.getContextClassLoader(); boolean setCl = ccl != mbeanTCL && mbeanTCL != null; if (setCl) { TCLAction.UTIL.setContextClassLoader(mbeanTCL); } try { initAttributeContexts(info.getAttributes()); initOperationContexts(info.getOperations()); if (resource != null) initDispatchers(); mbeanName = invokePreRegister(server, name); if( mbeanDescriptor != null ) { Object value = mbeanDescriptor.getFieldValue( ModelMBeanConstants.OBJECT_NAME_INJECTION_TYPE); String type = (String) value; if( type != null ) { inject(ModelMBeanConstants.OBJECT_NAME_INJECTION_TYPE, type, ObjectName.class, mbeanName); } } } finally { if (setCl) { TCLAction.UTIL.setContextClassLoader(ccl); } } return mbeanName; } /** */ public void postRegister(Boolean registrationSuccessful) { invokePostRegister(registrationSuccessful); } /** */ public void preDeregister() throws Exception { invokePreDeregister(); } /** */ public void postDeregister() { invokePostDeregister(); this.server = null; } // NotificationEmitter implementation ------------------------ public void addNotificationListener(NotificationListener listener, NotificationFilter filter, Object handback) { addNotificationListenerToResource(listener, filter, handback); } protected void addNotificationListenerToResource(NotificationListener listener, NotificationFilter filter, Object handback) { if (resource instanceof NotificationBroadcaster) { ((NotificationBroadcaster) resource).addNotificationListener(listener, filter, handback); } else { throw new RuntimeMBeanException(new IllegalArgumentException("Target XXX is not a notification broadcaster" // FIXME: add the XXX object name, store from registration )); } } public void removeNotificationListener(NotificationListener listener) throws ListenerNotFoundException { removeNotificationListenerFromResource(listener); } protected void removeNotificationListenerFromResource(NotificationListener listener) throws ListenerNotFoundException { if (resource instanceof NotificationBroadcaster) { ((NotificationBroadcaster) resource).removeNotificationListener(listener); } else { throw new RuntimeMBeanException(new IllegalArgumentException("Target XXX is not a notification broadcaster" // FIXME: add the XXX object name, store from registration )); } } public void removeNotificationListener(NotificationListener listener, NotificationFilter filter, Object handback) throws ListenerNotFoundException { removeNotificationListenerFromResource(listener, filter, handback); } protected void removeNotificationListenerFromResource(NotificationListener listener, NotificationFilter filter, Object handback) throws ListenerNotFoundException { if (resource instanceof NotificationEmitter) { ((NotificationEmitter) resource).removeNotificationListener(listener, filter, handback); } else if (resource instanceof NotificationBroadcaster) { //JGH NOTE: looks like a listener against the MBeanServer is //wrapped as a XMBean which has a broadcaster that is an NotificationEmitter //but this resource target is a NotificationBroadcaster, in which case, //w/o this .. you'll get a resource failure below removeNotificationListener(listener); } else { throw new RuntimeMBeanException(new IllegalArgumentException("Target XXX is not a notification emitter" // FIXME: add the XXX object name, store from registration )); } } public MBeanNotificationInfo[] getNotificationInfo() { return getNotificationInfoFromResource(); } protected MBeanNotificationInfo[] getNotificationInfoFromResource() { if (resource instanceof NotificationBroadcaster) { return ((NotificationBroadcaster) resource).getNotificationInfo(); } else return new MBeanNotificationInfo[]{}; } // MBeanInvoker implementation ----------------------------------- public MBeanInfo getMetaData() { return info; } public Object getResource() { return resource; } /** * Sets the XMBean resource and optionally allows the resource to interact * with the jmx microkernel via the following injection points: * #ModelMBeanConstants.MBEAN_SERVER_INJECTION_TYPE * #ModelMBeanConstants.MBEAN_INFO_INJECTION_TYPE * #ModelMBeanConstants.OBJECT_NAME_INJECTION_TYPE * @param resource - the model mbean resource */ public void setResource(Object resource) { this.resource = resource; } public ObjectName getObjectName() { if (resourceEntry == null) return null; else return resourceEntry.getObjectName(); } public void updateAttributeInfo(Descriptor attrDesc) throws MBeanException { ModelMBeanInfoSupport minfo = (ModelMBeanInfoSupport) info; minfo.setDescriptor(attrDesc, ModelMBeanConstants.ATTRIBUTE_DESCRIPTOR); } /** * Add dynamically an operation interceptor, first in the chain. */ public void addOperationInterceptor(Interceptor interceptor) { if (operationContextMap != null && interceptor != null) { // Go through all the operation InvocationContext and add the interceptor for (Iterator it = operationContextMap.entrySet().iterator(); it.hasNext();) { Map.Entry entry = (Map.Entry) it.next(); InvocationContext ctx = (InvocationContext) entry.getValue(); List list = ctx.getInterceptors(); // to make the interceptor list update atomic, make a new ArrayList, // add the new interceptor first and copy over the old ones, // then update the context List newList = new ArrayList(); newList.add(interceptor); if (list != null) { newList.addAll(list); } ctx.setInterceptors(newList); } } } /** * Remove the specified operation interceptor */ public void removeOperationInterceptor(Interceptor interceptor) { if (operationContextMap != null && interceptor != null) { // Go through all the operation InvocationContext and remove the interceptor for (Iterator it = operationContextMap.entrySet().iterator(); it.hasNext();) { Map.Entry entry = (Map.Entry) it.next(); InvocationContext ctx = (InvocationContext) entry.getValue(); List list = ctx.getInterceptors(); // to make the interceptor list update atomic, make a copy of the list // remove the interceptor (if found), then update the context if (list != null) { List newList = new ArrayList(list); // this should probably work, whether or not equals() is implemented // it'll remove the first occurence newList.remove(interceptor); ctx.setInterceptors(newList); } } } } // Other Public Methods ------------------------------------------ public void suspend() { } public void suspend(long wait) throws TimeoutException { } public void suspend(boolean force) { } public boolean isSuspended() { return false; } public void setInvocationTimeout(long time) { } public long getInvocationTimeout() { return 0l; } public void resume() { } public MBeanServer getServer() { return server; } // Protected ----------------------------------------------------- /** * Inject context from the xmbean layer to the resource * @param type - the type of injection * @param name - the setter method name of the resource * @param argType - the injection data type * @param value - the injection data value to pass to the setter */ protected void inject(String type, String name, Class argType, Object value) { try { Class resClass = resource.getClass(); Class[] sig = {argType}; Method setter = resClass.getMethod(name, sig); Object[] args = {value}; setter.invoke(resource, args); } catch(NoSuchMethodException e) { log.debug("Setter not found: "+name+"("+argType+")", e); } catch(Exception e) { log.warn("Failed to inject type: "+type+" using setter: "+name, e); } } protected ObjectName invokePreRegister(MBeanServer server, ObjectName name) throws Exception { if (resource instanceof MBeanRegistration) return ((MBeanRegistration) resource).preRegister(server, name); return name; } protected void invokePostRegister(Boolean b) { if (resource instanceof MBeanRegistration) ((MBeanRegistration) resource).postRegister(b); } protected void invokePreDeregister() throws Exception { if (resource instanceof MBeanRegistration) ((MBeanRegistration) resource).preDeregister(); } protected void invokePostDeregister() { if (resource instanceof MBeanRegistration) ((MBeanRegistration) resource).postDeregister(); } protected void initAttributeContexts(MBeanAttributeInfo[] attributes) { // create invocation contexts for attributes for (int i = 0; i < attributes.length; ++i) { InvocationContext ctx = new InvocationContext(); // fill in some default values, the attribute name ctx.setName(attributes[i].getName()); ctx.setAttributeType(attributes[i].getType()); // set myself as the invoker ctx.setInvoker(this); //ctx.add(InvocationContext.ATTRIBUTE_ACCESS, getAccessCode(attributes[i])); // store attributeContextMap.put(attributes[i].getName(), ctx); } if (log.isTraceEnabled()) log.trace(getObjectName() + " configured attribute contexts: " + operationContextMap); } protected void initOperationContexts(MBeanOperationInfo[] operations) { // create invocation contexts for operations for (int i = 0; i < operations.length; ++i) { InvocationContext ctx = new InvocationContext(); // extract operation name + signature String opName = operations[i].getName(); MBeanParameterInfo[] signature = operations[i].getSignature(); String returnType = operations[i].getReturnType(); // name is unchanged, fill in the context ctx.setName(opName); // signature doesn't change.. ctx.setSignature(signature); // return type ctx.setReturnType(returnType); // set myself as the invoker ctx.setInvoker(this); // add impact as part of ctx map (rarely accessed information) //ctx.add(InvocationContext.OPERATION_IMPACT, operations[i].getImpact()); // create an operation key consisting of the name + signature // (required for overloaded operations) OperationKey opKey = new OperationKey(opName, signature); // store operationContextMap.put(opKey, ctx); } if (log.isTraceEnabled()) log.trace(getObjectName() + " configured operation contexts: " + operationContextMap); } protected void initDispatchers() { boolean trace = log.isTraceEnabled(); // locate the resource class to receive the invocations Class clazz = null; if (resource != null) { clazz = resource.getClass(); // JBAS-1704, if the target class is *not* public, look for // an exposed MBean interface, if one exists. // This should be checking if we are dealing with a standard // mbean (but not a standard mbean deployed as a model mbean) // but it doesn't look convenient from this baseclass. if (Modifier.isPublic(clazz.getModifiers()) == false) { clazz = StandardMetaData.findStandardInterface(clazz); } } // map the Methods on the target resource for easy access MethodMapper mmap = new MethodMapper(clazz); if (trace) log.trace(getObjectName() + " " + clazz + " map=" + mmap); MBeanOperationInfo[] operations = info.getOperations(); // Set the dispatchers for the operations for (int i = 0; i < operations.length; ++i) { MBeanOperationInfo op = operations[i]; OperationKey opKey = new OperationKey(op.getName(), op.getSignature()); InvocationContext ctx = (InvocationContext) operationContextMap.get(opKey); Interceptor dispatcher = ctx.getDispatcher(); // Reconfigure if we have a Null or Reflected dispatcher if (dispatcher instanceof NullDispatcher || (dispatcher instanceof ReflectedDispatcher)) { Object target = null; dispatcher = null; Method m = mmap.lookupOperation(op); if (m == null) { // Look for an method on the model mbean m = MethodMapper.lookupOperation(op, this); if (m != null) { // operation found on the 'this' invoker target = this; dispatcher = new ReflectedDispatcher(m, dynamicResource); } else { // operation not found, use late binding // What is this late binding attempt and should there be a warning? dispatcher = new ReflectedDispatcher(dynamicResource); } } else { // operation found on the resource target = resource; dispatcher = new ReflectedDispatcher(m, dynamicResource); } if (trace) log.trace(getObjectName() + " will dispatch op=" + opKey + " to " + Strings.defaultToString(target) + " method= " + m); ctx.setTarget(target); ctx.setDispatcher(dispatcher); } } // Set the dispatchers for the attributes with getters/setters MBeanAttributeInfo[] attributes = info.getAttributes(); for (int i = 0; i < attributes.length; ++i) { MBeanAttributeInfo attribute = attributes[i]; String name = attribute.getName(); InvocationContext ctx = (InvocationContext) attributeContextMap.get(name); Method getter = mmap.lookupGetter(attribute); Method setter = mmap.lookupSetter(attribute); ctx.setDispatcher(new AttributeDispatcher(getter, setter, dynamicResource)); ctx.setTarget(resource); } } /** * Placeholder to allow subclasses to override the invocation * @param invocation the invocation * @throws MBeanException for any error */ protected void override(Invocation invocation) throws MBeanException { } protected String getSignatureString(String[] signature) { if (signature == null) return "()"; if (signature.length == 0) return "()"; StringBuffer sbuf = new StringBuffer(512); sbuf.append("("); for (int i = 0; i < signature.length - 1; ++i) { sbuf.append(signature[i]); sbuf.append(","); } sbuf.append(signature[signature.length - 1]); sbuf.append(")"); return sbuf.toString(); } // Inner classes ------------------------------------------------- protected final class OperationKey { String[] keys = null; int hash = 0; public OperationKey(final String name, final String type) { if (type != null) { keys = new String[2]; keys[0] = name; keys[1] = type; hash = name.hashCode(); } else { keys = new String[]{name}; hash = name.hashCode(); } } public OperationKey(final String name, final String[] signature) { if (signature != null) { keys = new String[signature.length + 1]; keys[0] = name; System.arraycopy(signature, 0, keys, 1, signature.length); hash = name.hashCode(); } else { keys = new String[]{name}; hash = name.hashCode(); } } public OperationKey(String name, MBeanParameterInfo[] signature) { if (signature == null) signature = new MBeanParameterInfo[0]; keys = new String[signature.length + 1]; keys[0] = name; for (int i = 0; i < signature.length; ++i) { keys[i + 1] = signature[i].getType(); } hash = name.hashCode(); } public OperationKey(MBeanOperationInfo info) { this(info.getName(), info.getSignature()); } public int hashCode() { return hash; } public boolean equals(Object o) { OperationKey target = (OperationKey) o; if (target.keys.length != keys.length) return false; for (int i = 0; i < keys.length; ++i) { if (!(keys[i].equals(target.keys[i]))) return false; } return true; } public String toString() { StringBuffer buffer = new StringBuffer(50); buffer.append(keys[0]).append("("); for (int i = 1; i < keys.length - 1; ++i) { buffer.append(keys[i]).append(','); } if (keys.length > 1) buffer.append(keys[keys.length - 1]); buffer.append(")"); return buffer.toString(); } } private void rethrowAsMBeanException(Throwable t) throws MBeanException { if (t instanceof RuntimeException) throw new RuntimeMBeanException((RuntimeException) t); else if (t instanceof Error) throw new RuntimeErrorException((Error) t); else throw new MBeanException((Exception) t); } private void rethrowAsRuntimeMBeanException(Throwable t) { if (t instanceof RuntimeException) throw new RuntimeMBeanException((RuntimeException) t); else if (t instanceof Error) throw new RuntimeErrorException((Error) t); else throw new RuntimeMBeanException(new RuntimeException("Unhandled exception", t)); } }