0001: /*
0002: * JBoss, Home of Professional Open Source.
0003: * Copyright 2006, Red Hat Middleware LLC, and individual contributors
0004: * as indicated by the @author tags. See the copyright.txt file in the
0005: * distribution for a full listing of individual contributors.
0006: *
0007: * This is free software; you can redistribute it and/or modify it
0008: * under the terms of the GNU Lesser General Public License as
0009: * published by the Free Software Foundation; either version 2.1 of
0010: * the License, or (at your option) any later version.
0011: *
0012: * This software is distributed in the hope that it will be useful,
0013: * but WITHOUT ANY WARRANTY; without even the implied warranty of
0014: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
0015: * Lesser General Public License for more details.
0016: *
0017: * You should have received a copy of the GNU Lesser General Public
0018: * License along with this software; if not, write to the Free
0019: * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
0020: * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
0021: */
0022: package org.jboss.mx.server;
0023:
0024: import java.lang.reflect.Method;
0025: import java.lang.reflect.Modifier;
0026: import java.util.ArrayList;
0027: import java.util.HashMap;
0028: import java.util.Iterator;
0029: import java.util.List;
0030: import java.util.Map;
0031:
0032: import javax.management.Attribute;
0033: import javax.management.AttributeList;
0034: import javax.management.AttributeNotFoundException;
0035: import javax.management.Descriptor;
0036: import javax.management.InvalidAttributeValueException;
0037: import javax.management.JMRuntimeException;
0038: import javax.management.ListenerNotFoundException;
0039: import javax.management.MBeanAttributeInfo;
0040: import javax.management.MBeanException;
0041: import javax.management.MBeanInfo;
0042: import javax.management.MBeanNotificationInfo;
0043: import javax.management.MBeanOperationInfo;
0044: import javax.management.MBeanParameterInfo;
0045: import javax.management.MBeanRegistration;
0046: import javax.management.MBeanServer;
0047: import javax.management.NotificationBroadcaster;
0048: import javax.management.NotificationEmitter;
0049: import javax.management.NotificationFilter;
0050: import javax.management.NotificationListener;
0051: import javax.management.ObjectName;
0052: import javax.management.ReflectionException;
0053: import javax.management.RuntimeErrorException;
0054: import javax.management.RuntimeMBeanException;
0055: import javax.management.RuntimeOperationsException;
0056: import javax.management.modelmbean.ModelMBeanInfo;
0057: import javax.management.modelmbean.ModelMBeanInfoSupport;
0058:
0059: import org.jboss.logging.Logger;
0060: import org.jboss.mx.interceptor.AttributeDispatcher;
0061: import org.jboss.mx.interceptor.Interceptor;
0062: import org.jboss.mx.interceptor.ReflectedDispatcher;
0063: import org.jboss.mx.metadata.StandardMetaData;
0064: import org.jboss.mx.modelmbean.ModelMBeanConstants;
0065: import org.jboss.mx.server.InvocationContext.NullDispatcher;
0066: import org.jboss.mx.server.registry.MBeanEntry;
0067: import org.jboss.util.Strings;
0068:
0069: /**
0070: * A base MBeanInvoker class that provides common state
0071: *
0072: * @author <a href="mailto:juha@jboss.org">Juha Lindfors</a>.
0073: * @author <a href="mailto:scott.stark@jboss.org">Scott Stark</a>.
0074: * @author <a href="mailto:dimitris@jboss.org">Dimitris Andreadis</a>.
0075: * @version $Revision: 57200 $
0076: */
0077: public abstract class AbstractMBeanInvoker implements MBeanInvoker {
0078: /**
0079: * Used to propagate the MBeanEntry during the preRegister callback
0080: */
0081: static ThreadLocal preRegisterInfo = new ThreadLocal();
0082:
0083: // Attributes ----------------------------------------------------
0084:
0085: /**
0086: * The target object for this invoker.
0087: */
0088: private Object resource = null;
0089: /**
0090: * The mbean server register entry used for the TCL
0091: */
0092: protected MBeanEntry resourceEntry = null;
0093:
0094: /**
0095: * Whether this is a dynamic resource
0096: */
0097: protected boolean dynamicResource = true;
0098:
0099: /**
0100: * The metadata describing this MBean.
0101: */
0102: protected MBeanInfo info = null;
0103:
0104: protected Map attributeContextMap = new HashMap();
0105: protected Map operationContextMap = new HashMap();
0106: protected Map constructorContextMap = new HashMap();
0107:
0108: protected InvocationContext getMBeanInfoCtx = null;
0109: protected InvocationContext preRegisterCtx = null;
0110: protected InvocationContext postRegisterCtx = null;
0111: protected InvocationContext preDeregisterCtx = null;
0112: protected InvocationContext postDeregisterCtx = null;
0113:
0114: // TODO: allow to config invoker specific logs
0115: // : multitarget mbean for invoker + log?
0116:
0117: protected Logger log = Logger.getLogger(AbstractMBeanInvoker.class);
0118:
0119: /**
0120: * The MBeanServer passed in to preRegister
0121: */
0122:
0123: private MBeanServer server;
0124:
0125: /**
0126: * Set the MBeanEntry thread local value.
0127: * @param entry - the entry that will be used on successful registration
0128: */
0129: public static void setMBeanEntry(MBeanEntry entry) {
0130: preRegisterInfo.set(entry);
0131: }
0132:
0133: /**
0134: * An accessor for the MBeanEntry thread local
0135: * @return
0136: */
0137: public static MBeanEntry getMBeanEntry() {
0138: return (MBeanEntry) preRegisterInfo.get();
0139: }
0140:
0141: // Constructors --------------------------------------------------
0142:
0143: /**
0144: * Constructs a new invoker.
0145: */
0146: public AbstractMBeanInvoker() {
0147: }
0148:
0149: /**
0150: * Constructs a new invoker with a given target resource.
0151: */
0152: public AbstractMBeanInvoker(Object resource) {
0153: this .resource = resource;
0154: }
0155:
0156: /**
0157: * Constructs an invoker with the target resource entry.
0158: * @param resourceEntry
0159: */
0160: public AbstractMBeanInvoker(MBeanEntry resourceEntry) {
0161: this .resourceEntry = resourceEntry;
0162: this .resource = resourceEntry.getResourceInstance();
0163: }
0164:
0165: // DynamicMBean implementation -----------------------------------
0166:
0167: /**
0168: * Invokes the target resource. The default invocation used by this invoker
0169: * implement sends the invocation through a stack of interceptors before
0170: * reaching the target method.
0171: * @param operationName name of the target method
0172: * @param args argumetns for the target method
0173: * @param signature signature of the target method
0174: * @throws MBeanException if the target method raised a hecked exception
0175: * @throws ReflectionException if there was an error trying to resolve or
0176: * invoke the target method
0177: * @throws RuntimeMBeanException if the target method raised an unchecked
0178: * exception
0179: */
0180: public Object invoke(String operationName, Object[] args,
0181: String[] signature) throws MBeanException,
0182: ReflectionException {
0183:
0184: // TODO: __JBOSSMX_INVOCATION
0185:
0186: if (operationName == null)
0187: throw new ReflectionException(new IllegalArgumentException(
0188: "Null operation name"));
0189:
0190: // If we have dynamic capability, check for a dynamic invocation
0191: String opName = operationName;
0192: if (dynamicResource) {
0193: int dot = operationName.lastIndexOf('.');
0194: if (dot != -1) {
0195: if (dot < operationName.length() - 1)
0196: opName = operationName.substring(dot + 1);
0197: }
0198: }
0199:
0200: // get the server side invocation context
0201: OperationKey key = new OperationKey(opName, signature);
0202: InvocationContext ctx = (InvocationContext) operationContextMap
0203: .get(key);
0204:
0205: // if the server does not contain this context, we do not have the operation
0206: if (ctx == null) {
0207: // This is just stupid - the RI is fundamentally broken and hence the spec
0208: boolean operationExists = false;
0209: if (dynamicResource) {
0210: for (Iterator i = operationContextMap.keySet()
0211: .iterator(); i.hasNext();) {
0212: OperationKey this Key = (OperationKey) i.next();
0213: if (opName.equals(this Key.keys[0])) {
0214: operationExists = true;
0215: break;
0216: }
0217: }
0218: if (operationExists)
0219: throw new ReflectionException(
0220: new NoSuchMethodException(
0221: "Unable to find operation "
0222: + operationName
0223: + getSignatureString(signature)));
0224: }
0225: throw new ReflectionException(new IllegalArgumentException(
0226: "Unable to find operation " + operationName
0227: + getSignatureString(signature)));
0228: }
0229:
0230: // create the invocation object
0231: Invocation invocation = new Invocation();
0232:
0233: // copy the server's invocation context to the invocation
0234: invocation.addContext(ctx);
0235:
0236: // set the invocation's entry point
0237: invocation.setType(InvocationContext.OP_INVOKE);
0238:
0239: // Use the passed operation
0240: invocation.setName(operationName);
0241:
0242: // set the args
0243: invocation.setArgs(args);
0244:
0245: override(invocation);
0246:
0247: ClassLoader mbeanTCL = resourceEntry.getClassLoader();
0248: final ClassLoader ccl = TCLAction.UTIL.getContextClassLoader();
0249: boolean setCl = ccl != mbeanTCL && mbeanTCL != null;
0250: if (setCl) {
0251: TCLAction.UTIL.setContextClassLoader(mbeanTCL);
0252: }
0253:
0254: try {
0255: // the default invocation implementation will invoke each interceptor
0256: // declared in the invocation context before invoking the target method
0257: return invocation.invoke();
0258: } catch (MBeanException e) {
0259: throw e;
0260: } catch (ReflectionException e) {
0261: throw e;
0262: } catch (JMRuntimeException e) {
0263: throw e;
0264: } catch (Throwable t) {
0265: rethrowAsMBeanException(t);
0266: return null;
0267: }
0268:
0269: // TODO: should be fixed by adding invocation return value object
0270: finally {
0271: Descriptor descriptor = invocation.getDescriptor();
0272: if (descriptor != null) {
0273: ctx.setDescriptor(descriptor);
0274: if (dynamicResource
0275: && ModelMBeanConstants.OPERATION_DESCRIPTOR
0276: .equals(descriptor
0277: .getFieldValue(ModelMBeanConstants.DESCRIPTOR_TYPE))) {
0278: ModelMBeanInfoSupport minfo = (ModelMBeanInfoSupport) info;
0279: minfo.setDescriptor(descriptor,
0280: ModelMBeanConstants.OPERATION_DESCRIPTOR);
0281: }
0282: }
0283: invocation.setArgs(null);
0284: invocation.setDescriptor(null);
0285: invocation.setDispatcher(null);
0286:
0287: if (setCl) {
0288: TCLAction.UTIL.setContextClassLoader(ccl);
0289: }
0290: }
0291:
0292: }
0293:
0294: /**
0295: * Returns an attribte value. The request for the value is forced through a
0296: * set of interceptors before the value is returned.
0297: * @param attribute attribute name
0298: * @return attribute value
0299: * @throws AttributeNotFoundException if the requested attribute is not part
0300: * of the MBean's management interface
0301: * @throws MBeanException if retrieving the attribute value causes an
0302: * application exception
0303: * @throws ReflectionException if there was an error trying to retrieve the
0304: * attribute value
0305: */
0306: public Object getAttribute(String attribute)
0307: throws AttributeNotFoundException, MBeanException,
0308: ReflectionException {
0309: // TODO: __JBOSSMX_INVOCATION
0310:
0311: if (attribute == null)
0312: throw new RuntimeOperationsException(
0313: new IllegalArgumentException(
0314: "Cannot get null attribute"));
0315:
0316: // lookup the server side invocation context
0317: InvocationContext ctx = (InvocationContext) attributeContextMap
0318: .get(attribute);
0319:
0320: // if we don't have a server side invocation context for the attribute,
0321: // it does not exist as far as we are concerned
0322: if (ctx == null)
0323: throw new AttributeNotFoundException("not found: "
0324: + attribute);
0325:
0326: if (ctx.isReadable() == false)
0327: throw new AttributeNotFoundException("Attribute '"
0328: + attribute + "' found, but it is not readable");
0329:
0330: // create the invocation object
0331: Invocation invocation = new Invocation();
0332:
0333: // copy the server's invocation context to the invocation
0334: invocation.addContext(ctx);
0335:
0336: // indicate the invocation access point was getAttribute() method
0337: invocation.setType(InvocationContext.OP_GETATTRIBUTE);
0338: invocation.setArgs(null);
0339:
0340: override(invocation);
0341:
0342: ClassLoader mbeanTCL = resourceEntry.getClassLoader();
0343: final ClassLoader ccl = TCLAction.UTIL.getContextClassLoader();
0344: boolean setCl = ccl != mbeanTCL && mbeanTCL != null;
0345: if (setCl) {
0346: TCLAction.UTIL.setContextClassLoader(mbeanTCL);
0347: }
0348:
0349: try {
0350: return invocation.invoke();
0351: } catch (AttributeNotFoundException e) {
0352: throw e;
0353: } catch (MBeanException e) {
0354: throw e;
0355: } catch (ReflectionException e) {
0356: throw e;
0357: } catch (JMRuntimeException e) {
0358: throw e;
0359: } catch (Throwable t) {
0360: rethrowAsMBeanException(t);
0361: return null;
0362: }
0363:
0364: // TODO: should be fixed by adding invocation return value object
0365: finally {
0366: Descriptor attrDesc = invocation.getDescriptor();
0367: ctx.setDescriptor(attrDesc);
0368: updateAttributeInfo(attrDesc);
0369:
0370: if (setCl) {
0371: TCLAction.UTIL.setContextClassLoader(ccl);
0372: }
0373: }
0374: }
0375:
0376: /**
0377: * Sets an attribute value. The operation is forced through a set of
0378: * interceptors before the new value for the attribute is set.
0379: * @param attribute new attribute value
0380: * @throws AttributeNotFoundException if the requested attribute is not part
0381: * of the MBean's management interface
0382: * @throws InvalidAttributeValueException if the attribute contains a value
0383: * not suitable for the attribute
0384: * @throws MBeanException if setting the attribute value causes an
0385: * application exception
0386: * @throws ReflectionException if there was an error trying to set the
0387: * attribute value.
0388: */
0389: public void setAttribute(Attribute attribute)
0390: throws AttributeNotFoundException,
0391: InvalidAttributeValueException, MBeanException,
0392: ReflectionException {
0393: // TODO: __JBOSSMX_INVOCATION
0394:
0395: if (attribute == null)
0396: throw new InvalidAttributeValueException("null attribute");
0397:
0398: // lookup the server side invocation context
0399: String name = attribute.getName();
0400: InvocationContext ctx = (InvocationContext) attributeContextMap
0401: .get(name);
0402:
0403: // if we don't have a server side invocation context for the attribute,
0404: // it does not exist as far as we are concerned
0405: if (ctx == null)
0406: throw new AttributeNotFoundException("not found: " + name);
0407: else if (ctx.isWritable() == false) {
0408: throw new AttributeNotFoundException("Attribute '" + name
0409: + "' is not writable");
0410: }
0411:
0412: // create the invocation object
0413: Invocation invocation = new Invocation();
0414:
0415: // copy the server context to the invocation
0416: invocation.addContext(ctx);
0417:
0418: // indicate the access point as setAttribute()
0419: invocation.setType(InvocationContext.OP_SETATTRIBUTE);
0420:
0421: // set the attribute value as the argument
0422: invocation.setArgs(new Object[] { attribute.getValue() });
0423:
0424: override(invocation);
0425:
0426: ClassLoader mbeanTCL = resourceEntry.getClassLoader();
0427: final ClassLoader ccl = TCLAction.UTIL.getContextClassLoader();
0428: boolean setCl = ccl != mbeanTCL && mbeanTCL != null;
0429: if (setCl) {
0430: TCLAction.UTIL.setContextClassLoader(mbeanTCL);
0431: }
0432:
0433: try {
0434: // the default invocation implementation will invoke each interceptor
0435: // declared in the invocation context before invoking the target method
0436: invocation.invoke();
0437: } catch (AttributeNotFoundException e) {
0438: throw e;
0439: } catch (InvalidAttributeValueException e) {
0440: throw e;
0441: } catch (MBeanException e) {
0442: throw e;
0443: } catch (ReflectionException e) {
0444: throw e;
0445: } catch (JMRuntimeException e) {
0446: throw e;
0447: } catch (Throwable t) {
0448: rethrowAsMBeanException(t);
0449: }
0450:
0451: // TODO: should be fixed by adding invocation return value object
0452: finally {
0453: /* Obtain the updated attribute descriptor and propagate to the
0454: invocation context and ModelMBeanInfo. The latter is required in
0455: order for getMBeanInfo() to show an updated view.
0456: */
0457: Descriptor attrDesc = invocation.getDescriptor();
0458: ctx.setDescriptor(attrDesc);
0459: updateAttributeInfo(attrDesc);
0460:
0461: if (setCl) {
0462: TCLAction.UTIL.setContextClassLoader(ccl);
0463: }
0464: }
0465: }
0466:
0467: public MBeanInfo getMBeanInfo() {
0468: // create the invocation object
0469: Invocation invocation = new Invocation(getMBeanInfoCtx);
0470:
0471: // set the invocation's access point as getMBeanInfo()
0472: invocation.setType(InvocationContext.OP_GETMBEANINFO);
0473:
0474: if (resourceEntry == null)
0475: resourceEntry = getMBeanEntry();
0476: ClassLoader mbeanTCL = resourceEntry.getClassLoader();
0477: final ClassLoader ccl = TCLAction.UTIL.getContextClassLoader();
0478: boolean setCl = ccl != mbeanTCL && mbeanTCL != null;
0479: if (setCl) {
0480: TCLAction.UTIL.setContextClassLoader(mbeanTCL);
0481: }
0482:
0483: try {
0484: MBeanInfo info = (MBeanInfo) invocation.invoke();
0485: return info;
0486: } catch (JMRuntimeException e) {
0487: throw e;
0488: } catch (Throwable t) {
0489: rethrowAsRuntimeMBeanException(t);
0490: return null;
0491: } finally {
0492: if (setCl) {
0493: TCLAction.UTIL.setContextClassLoader(ccl);
0494: }
0495: }
0496: }
0497:
0498: public AttributeList getAttributes(java.lang.String[] attributes) {
0499: if (attributes == null)
0500: throw new IllegalArgumentException("null array");
0501:
0502: AttributeList list = new AttributeList();
0503:
0504: for (int i = 0; i < attributes.length; ++i) {
0505: try {
0506: list.add(new Attribute(attributes[i],
0507: getAttribute(attributes[i])));
0508: } catch (Throwable ignored) {
0509: // if the attribute could not be retrieved, skip it
0510: }
0511: }
0512:
0513: return list;
0514: }
0515:
0516: public AttributeList setAttributes(AttributeList attributes) {
0517: if (attributes == null)
0518: throw new IllegalArgumentException("null list");
0519:
0520: AttributeList results = new AttributeList();
0521: Iterator it = attributes.iterator();
0522:
0523: while (it.hasNext()) {
0524: Attribute attr = (Attribute) it.next();
0525: try {
0526: setAttribute(attr);
0527: results.add(attr);
0528: } catch (Throwable ignored) {
0529: // if unable to set the attribute, skip it
0530: if (log.isTraceEnabled())
0531: log.trace(
0532: "Unhandled setAttribute() for attribute: "
0533: + attr.getName(), ignored);
0534: }
0535: }
0536:
0537: return results;
0538: }
0539:
0540: // MBeanRegistration implementation ------------------------------
0541:
0542: /**
0543: * Initializes this invoker. At the registration time we can be sure that all
0544: * of the metadata is available and initialize the invoker and cache the data
0545: * accordingly. <p>
0546: *
0547: * Subclasses that override the <tt>preRegister</tt> method must make sure
0548: * they call <tt>super.preRegister()</tt> in their implementation to ensure
0549: * proper initialization of the invoker.
0550: */
0551: public ObjectName preRegister(MBeanServer server, ObjectName name)
0552: throws Exception {
0553: this .resourceEntry = (MBeanEntry) preRegisterInfo.get();
0554: this .server = server;
0555:
0556: ObjectName mbeanName = null;
0557: Descriptor mbeanDescriptor = null;
0558: if (info instanceof ModelMBeanInfo) {
0559: ModelMBeanInfo minfo = (ModelMBeanInfo) info;
0560: try {
0561: mbeanDescriptor = minfo.getDescriptor("",
0562: ModelMBeanConstants.MBEAN_DESCRIPTOR);
0563: String type = (String) mbeanDescriptor
0564: .getFieldValue(ModelMBeanConstants.MBEAN_SERVER_INJECTION_TYPE);
0565: if (type != null) {
0566: inject(
0567: ModelMBeanConstants.MBEAN_SERVER_INJECTION_TYPE,
0568: type, MBeanServer.class, getServer());
0569: }
0570: } catch (MBeanException e) {
0571: log.warn("Failed to obtain descriptor: "
0572: + ModelMBeanConstants.MBEAN_DESCRIPTOR, e);
0573: }
0574:
0575: }
0576:
0577: ClassLoader mbeanTCL = resourceEntry.getClassLoader();
0578: final ClassLoader ccl = TCLAction.UTIL.getContextClassLoader();
0579: boolean setCl = ccl != mbeanTCL && mbeanTCL != null;
0580: if (setCl) {
0581: TCLAction.UTIL.setContextClassLoader(mbeanTCL);
0582: }
0583:
0584: try {
0585: initAttributeContexts(info.getAttributes());
0586:
0587: initOperationContexts(info.getOperations());
0588:
0589: if (resource != null)
0590: initDispatchers();
0591:
0592: mbeanName = invokePreRegister(server, name);
0593: if (mbeanDescriptor != null) {
0594: Object value = mbeanDescriptor
0595: .getFieldValue(ModelMBeanConstants.OBJECT_NAME_INJECTION_TYPE);
0596: String type = (String) value;
0597: if (type != null) {
0598: inject(
0599: ModelMBeanConstants.OBJECT_NAME_INJECTION_TYPE,
0600: type, ObjectName.class, mbeanName);
0601: }
0602: }
0603: } finally {
0604: if (setCl) {
0605: TCLAction.UTIL.setContextClassLoader(ccl);
0606: }
0607: }
0608: return mbeanName;
0609: }
0610:
0611: /**
0612: */
0613: public void postRegister(Boolean registrationSuccessful) {
0614: invokePostRegister(registrationSuccessful);
0615: }
0616:
0617: /**
0618: */
0619: public void preDeregister() throws Exception {
0620: invokePreDeregister();
0621: }
0622:
0623: /**
0624: */
0625: public void postDeregister() {
0626: invokePostDeregister();
0627: this .server = null;
0628: }
0629:
0630: // NotificationEmitter implementation ------------------------
0631:
0632: public void addNotificationListener(NotificationListener listener,
0633: NotificationFilter filter, Object handback) {
0634: addNotificationListenerToResource(listener, filter, handback);
0635: }
0636:
0637: protected void addNotificationListenerToResource(
0638: NotificationListener listener, NotificationFilter filter,
0639: Object handback) {
0640: if (resource instanceof NotificationBroadcaster) {
0641: ((NotificationBroadcaster) resource)
0642: .addNotificationListener(listener, filter, handback);
0643: } else {
0644: throw new RuntimeMBeanException(
0645: new IllegalArgumentException(
0646: "Target XXX is not a notification broadcaster"
0647:
0648: // FIXME: add the XXX object name, store from registration
0649: ));
0650: }
0651: }
0652:
0653: public void removeNotificationListener(NotificationListener listener)
0654: throws ListenerNotFoundException {
0655: removeNotificationListenerFromResource(listener);
0656: }
0657:
0658: protected void removeNotificationListenerFromResource(
0659: NotificationListener listener)
0660: throws ListenerNotFoundException {
0661: if (resource instanceof NotificationBroadcaster) {
0662: ((NotificationBroadcaster) resource)
0663: .removeNotificationListener(listener);
0664: } else {
0665: throw new RuntimeMBeanException(
0666: new IllegalArgumentException(
0667: "Target XXX is not a notification broadcaster"
0668:
0669: // FIXME: add the XXX object name, store from registration
0670: ));
0671: }
0672: }
0673:
0674: public void removeNotificationListener(
0675: NotificationListener listener, NotificationFilter filter,
0676: Object handback) throws ListenerNotFoundException {
0677: removeNotificationListenerFromResource(listener, filter,
0678: handback);
0679: }
0680:
0681: protected void removeNotificationListenerFromResource(
0682: NotificationListener listener, NotificationFilter filter,
0683: Object handback) throws ListenerNotFoundException {
0684: if (resource instanceof NotificationEmitter) {
0685: ((NotificationEmitter) resource)
0686: .removeNotificationListener(listener, filter,
0687: handback);
0688: } else if (resource instanceof NotificationBroadcaster) {
0689: //JGH NOTE: looks like a listener against the MBeanServer is
0690: //wrapped as a XMBean which has a broadcaster that is an NotificationEmitter
0691: //but this resource target is a NotificationBroadcaster, in which case,
0692: //w/o this .. you'll get a resource failure below
0693: removeNotificationListener(listener);
0694: } else {
0695: throw new RuntimeMBeanException(
0696: new IllegalArgumentException(
0697: "Target XXX is not a notification emitter"
0698:
0699: // FIXME: add the XXX object name, store from registration
0700: ));
0701: }
0702: }
0703:
0704: public MBeanNotificationInfo[] getNotificationInfo() {
0705: return getNotificationInfoFromResource();
0706: }
0707:
0708: protected MBeanNotificationInfo[] getNotificationInfoFromResource() {
0709: if (resource instanceof NotificationBroadcaster) {
0710: return ((NotificationBroadcaster) resource)
0711: .getNotificationInfo();
0712: } else
0713: return new MBeanNotificationInfo[] {};
0714: }
0715:
0716: // MBeanInvoker implementation -----------------------------------
0717:
0718: public MBeanInfo getMetaData() {
0719: return info;
0720: }
0721:
0722: public Object getResource() {
0723: return resource;
0724: }
0725:
0726: /**
0727: * Sets the XMBean resource and optionally allows the resource to interact
0728: * with the jmx microkernel via the following injection points:
0729: * #ModelMBeanConstants.MBEAN_SERVER_INJECTION_TYPE
0730: * #ModelMBeanConstants.MBEAN_INFO_INJECTION_TYPE
0731: * #ModelMBeanConstants.OBJECT_NAME_INJECTION_TYPE
0732: * @param resource - the model mbean resource
0733: */
0734: public void setResource(Object resource) {
0735: this .resource = resource;
0736: }
0737:
0738: public ObjectName getObjectName() {
0739: if (resourceEntry == null)
0740: return null;
0741: else
0742: return resourceEntry.getObjectName();
0743: }
0744:
0745: public void updateAttributeInfo(Descriptor attrDesc)
0746: throws MBeanException {
0747: ModelMBeanInfoSupport minfo = (ModelMBeanInfoSupport) info;
0748: minfo.setDescriptor(attrDesc,
0749: ModelMBeanConstants.ATTRIBUTE_DESCRIPTOR);
0750: }
0751:
0752: /**
0753: * Add dynamically an operation interceptor, first in the chain.
0754: */
0755: public void addOperationInterceptor(Interceptor interceptor) {
0756: if (operationContextMap != null && interceptor != null) {
0757: // Go through all the operation InvocationContext and add the interceptor
0758: for (Iterator it = operationContextMap.entrySet()
0759: .iterator(); it.hasNext();) {
0760: Map.Entry entry = (Map.Entry) it.next();
0761:
0762: InvocationContext ctx = (InvocationContext) entry
0763: .getValue();
0764: List list = ctx.getInterceptors();
0765:
0766: // to make the interceptor list update atomic, make a new ArrayList,
0767: // add the new interceptor first and copy over the old ones,
0768: // then update the context
0769: List newList = new ArrayList();
0770: newList.add(interceptor);
0771:
0772: if (list != null) {
0773: newList.addAll(list);
0774: }
0775:
0776: ctx.setInterceptors(newList);
0777: }
0778: }
0779: }
0780:
0781: /**
0782: * Remove the specified operation interceptor
0783: */
0784: public void removeOperationInterceptor(Interceptor interceptor) {
0785: if (operationContextMap != null && interceptor != null) {
0786: // Go through all the operation InvocationContext and remove the interceptor
0787: for (Iterator it = operationContextMap.entrySet()
0788: .iterator(); it.hasNext();) {
0789: Map.Entry entry = (Map.Entry) it.next();
0790:
0791: InvocationContext ctx = (InvocationContext) entry
0792: .getValue();
0793: List list = ctx.getInterceptors();
0794:
0795: // to make the interceptor list update atomic, make a copy of the list
0796: // remove the interceptor (if found), then update the context
0797: if (list != null) {
0798: List newList = new ArrayList(list);
0799:
0800: // this should probably work, whether or not equals() is implemented
0801: // it'll remove the first occurence
0802: newList.remove(interceptor);
0803:
0804: ctx.setInterceptors(newList);
0805: }
0806: }
0807: }
0808: }
0809:
0810: // Other Public Methods ------------------------------------------
0811:
0812: public void suspend() {
0813: }
0814:
0815: public void suspend(long wait) throws TimeoutException {
0816: }
0817:
0818: public void suspend(boolean force) {
0819: }
0820:
0821: public boolean isSuspended() {
0822: return false;
0823: }
0824:
0825: public void setInvocationTimeout(long time) {
0826: }
0827:
0828: public long getInvocationTimeout() {
0829: return 0l;
0830: }
0831:
0832: public void resume() {
0833: }
0834:
0835: public MBeanServer getServer() {
0836: return server;
0837: }
0838:
0839: // Protected -----------------------------------------------------
0840:
0841: /**
0842: * Inject context from the xmbean layer to the resource
0843: * @param type - the type of injection
0844: * @param name - the setter method name of the resource
0845: * @param argType - the injection data type
0846: * @param value - the injection data value to pass to the setter
0847: */
0848: protected void inject(String type, String name, Class argType,
0849: Object value) {
0850: try {
0851: Class resClass = resource.getClass();
0852: Class[] sig = { argType };
0853: Method setter = resClass.getMethod(name, sig);
0854: Object[] args = { value };
0855: setter.invoke(resource, args);
0856: } catch (NoSuchMethodException e) {
0857: log.debug(
0858: "Setter not found: " + name + "(" + argType + ")",
0859: e);
0860: } catch (Exception e) {
0861: log.warn("Failed to inject type: " + type
0862: + " using setter: " + name, e);
0863: }
0864: }
0865:
0866: protected ObjectName invokePreRegister(MBeanServer server,
0867: ObjectName name) throws Exception {
0868: if (resource instanceof MBeanRegistration)
0869: return ((MBeanRegistration) resource).preRegister(server,
0870: name);
0871:
0872: return name;
0873: }
0874:
0875: protected void invokePostRegister(Boolean b) {
0876: if (resource instanceof MBeanRegistration)
0877: ((MBeanRegistration) resource).postRegister(b);
0878: }
0879:
0880: protected void invokePreDeregister() throws Exception {
0881: if (resource instanceof MBeanRegistration)
0882: ((MBeanRegistration) resource).preDeregister();
0883: }
0884:
0885: protected void invokePostDeregister() {
0886: if (resource instanceof MBeanRegistration)
0887: ((MBeanRegistration) resource).postDeregister();
0888: }
0889:
0890: protected void initAttributeContexts(MBeanAttributeInfo[] attributes) {
0891: // create invocation contexts for attributes
0892: for (int i = 0; i < attributes.length; ++i) {
0893: InvocationContext ctx = new InvocationContext();
0894:
0895: // fill in some default values, the attribute name
0896: ctx.setName(attributes[i].getName());
0897:
0898: ctx.setAttributeType(attributes[i].getType());
0899:
0900: // set myself as the invoker
0901: ctx.setInvoker(this );
0902:
0903: //ctx.add(InvocationContext.ATTRIBUTE_ACCESS, getAccessCode(attributes[i]));
0904:
0905: // store
0906: attributeContextMap.put(attributes[i].getName(), ctx);
0907: }
0908: if (log.isTraceEnabled())
0909: log.trace(getObjectName()
0910: + " configured attribute contexts: "
0911: + operationContextMap);
0912: }
0913:
0914: protected void initOperationContexts(MBeanOperationInfo[] operations) {
0915: // create invocation contexts for operations
0916: for (int i = 0; i < operations.length; ++i) {
0917: InvocationContext ctx = new InvocationContext();
0918:
0919: // extract operation name + signature
0920: String opName = operations[i].getName();
0921: MBeanParameterInfo[] signature = operations[i]
0922: .getSignature();
0923: String returnType = operations[i].getReturnType();
0924:
0925: // name is unchanged, fill in the context
0926: ctx.setName(opName);
0927:
0928: // signature doesn't change..
0929: ctx.setSignature(signature);
0930:
0931: // return type
0932: ctx.setReturnType(returnType);
0933:
0934: // set myself as the invoker
0935: ctx.setInvoker(this );
0936:
0937: // add impact as part of ctx map (rarely accessed information)
0938: //ctx.add(InvocationContext.OPERATION_IMPACT, operations[i].getImpact());
0939:
0940: // create an operation key consisting of the name + signature
0941: // (required for overloaded operations)
0942: OperationKey opKey = new OperationKey(opName, signature);
0943:
0944: // store
0945: operationContextMap.put(opKey, ctx);
0946: }
0947:
0948: if (log.isTraceEnabled())
0949: log.trace(getObjectName()
0950: + " configured operation contexts: "
0951: + operationContextMap);
0952: }
0953:
0954: protected void initDispatchers() {
0955: boolean trace = log.isTraceEnabled();
0956:
0957: // locate the resource class to receive the invocations
0958: Class clazz = null;
0959: if (resource != null) {
0960: clazz = resource.getClass();
0961:
0962: // JBAS-1704, if the target class is *not* public, look for
0963: // an exposed MBean interface, if one exists.
0964: // This should be checking if we are dealing with a standard
0965: // mbean (but not a standard mbean deployed as a model mbean)
0966: // but it doesn't look convenient from this baseclass.
0967: if (Modifier.isPublic(clazz.getModifiers()) == false) {
0968: clazz = StandardMetaData.findStandardInterface(clazz);
0969: }
0970: }
0971:
0972: // map the Methods on the target resource for easy access
0973: MethodMapper mmap = new MethodMapper(clazz);
0974: if (trace)
0975: log.trace(getObjectName() + " " + clazz + " map=" + mmap);
0976:
0977: MBeanOperationInfo[] operations = info.getOperations();
0978:
0979: // Set the dispatchers for the operations
0980: for (int i = 0; i < operations.length; ++i) {
0981: MBeanOperationInfo op = operations[i];
0982: OperationKey opKey = new OperationKey(op.getName(), op
0983: .getSignature());
0984: InvocationContext ctx = (InvocationContext) operationContextMap
0985: .get(opKey);
0986:
0987: Interceptor dispatcher = ctx.getDispatcher();
0988:
0989: // Reconfigure if we have a Null or Reflected dispatcher
0990: if (dispatcher instanceof NullDispatcher
0991: || (dispatcher instanceof ReflectedDispatcher)) {
0992: Object target = null;
0993: dispatcher = null;
0994: Method m = mmap.lookupOperation(op);
0995: if (m == null) {
0996: // Look for an method on the model mbean
0997: m = MethodMapper.lookupOperation(op, this );
0998: if (m != null) {
0999: // operation found on the 'this' invoker
1000: target = this ;
1001: dispatcher = new ReflectedDispatcher(m,
1002: dynamicResource);
1003: } else {
1004: // operation not found, use late binding
1005: // What is this late binding attempt and should there be a warning?
1006: dispatcher = new ReflectedDispatcher(
1007: dynamicResource);
1008: }
1009: } else {
1010: // operation found on the resource
1011: target = resource;
1012: dispatcher = new ReflectedDispatcher(m,
1013: dynamicResource);
1014: }
1015: if (trace)
1016: log.trace(getObjectName() + " will dispatch op="
1017: + opKey + " to "
1018: + Strings.defaultToString(target)
1019: + " method= " + m);
1020: ctx.setTarget(target);
1021: ctx.setDispatcher(dispatcher);
1022: }
1023: }
1024:
1025: // Set the dispatchers for the attributes with getters/setters
1026: MBeanAttributeInfo[] attributes = info.getAttributes();
1027: for (int i = 0; i < attributes.length; ++i) {
1028: MBeanAttributeInfo attribute = attributes[i];
1029: String name = attribute.getName();
1030: InvocationContext ctx = (InvocationContext) attributeContextMap
1031: .get(name);
1032:
1033: Method getter = mmap.lookupGetter(attribute);
1034: Method setter = mmap.lookupSetter(attribute);
1035: ctx.setDispatcher(new AttributeDispatcher(getter, setter,
1036: dynamicResource));
1037: ctx.setTarget(resource);
1038: }
1039: }
1040:
1041: /**
1042: * Placeholder to allow subclasses to override the invocation
1043: * @param invocation the invocation
1044: * @throws MBeanException for any error
1045: */
1046: protected void override(Invocation invocation)
1047: throws MBeanException {
1048: }
1049:
1050: protected String getSignatureString(String[] signature) {
1051: if (signature == null)
1052: return "()";
1053: if (signature.length == 0)
1054: return "()";
1055:
1056: StringBuffer sbuf = new StringBuffer(512);
1057:
1058: sbuf.append("(");
1059: for (int i = 0; i < signature.length - 1; ++i) {
1060: sbuf.append(signature[i]);
1061: sbuf.append(",");
1062: }
1063: sbuf.append(signature[signature.length - 1]);
1064: sbuf.append(")");
1065:
1066: return sbuf.toString();
1067: }
1068:
1069: // Inner classes -------------------------------------------------
1070: protected final class OperationKey {
1071: String[] keys = null;
1072: int hash = 0;
1073:
1074: public OperationKey(final String name, final String type) {
1075: if (type != null) {
1076: keys = new String[2];
1077:
1078: keys[0] = name;
1079: keys[1] = type;
1080:
1081: hash = name.hashCode();
1082: }
1083:
1084: else {
1085: keys = new String[] { name };
1086: hash = name.hashCode();
1087: }
1088: }
1089:
1090: public OperationKey(final String name, final String[] signature) {
1091: if (signature != null) {
1092: keys = new String[signature.length + 1];
1093:
1094: keys[0] = name;
1095:
1096: System.arraycopy(signature, 0, keys, 1,
1097: signature.length);
1098:
1099: hash = name.hashCode();
1100: }
1101:
1102: else {
1103: keys = new String[] { name };
1104: hash = name.hashCode();
1105: }
1106: }
1107:
1108: public OperationKey(String name, MBeanParameterInfo[] signature) {
1109: if (signature == null)
1110: signature = new MBeanParameterInfo[0];
1111:
1112: keys = new String[signature.length + 1];
1113:
1114: keys[0] = name;
1115:
1116: for (int i = 0; i < signature.length; ++i) {
1117: keys[i + 1] = signature[i].getType();
1118: }
1119:
1120: hash = name.hashCode();
1121: }
1122:
1123: public OperationKey(MBeanOperationInfo info) {
1124: this (info.getName(), info.getSignature());
1125: }
1126:
1127: public int hashCode() {
1128: return hash;
1129: }
1130:
1131: public boolean equals(Object o) {
1132: OperationKey target = (OperationKey) o;
1133:
1134: if (target.keys.length != keys.length)
1135: return false;
1136:
1137: for (int i = 0; i < keys.length; ++i) {
1138: if (!(keys[i].equals(target.keys[i])))
1139: return false;
1140: }
1141:
1142: return true;
1143: }
1144:
1145: public String toString() {
1146: StringBuffer buffer = new StringBuffer(50);
1147: buffer.append(keys[0]).append("(");
1148:
1149: for (int i = 1; i < keys.length - 1; ++i) {
1150: buffer.append(keys[i]).append(',');
1151: }
1152:
1153: if (keys.length > 1)
1154: buffer.append(keys[keys.length - 1]);
1155: buffer.append(")");
1156: return buffer.toString();
1157: }
1158: }
1159:
1160: private void rethrowAsMBeanException(Throwable t)
1161: throws MBeanException {
1162: if (t instanceof RuntimeException)
1163: throw new RuntimeMBeanException((RuntimeException) t);
1164: else if (t instanceof Error)
1165: throw new RuntimeErrorException((Error) t);
1166: else
1167: throw new MBeanException((Exception) t);
1168: }
1169:
1170: private void rethrowAsRuntimeMBeanException(Throwable t) {
1171: if (t instanceof RuntimeException)
1172: throw new RuntimeMBeanException((RuntimeException) t);
1173: else if (t instanceof Error)
1174: throw new RuntimeErrorException((Error) t);
1175: else
1176: throw new RuntimeMBeanException(new RuntimeException(
1177: "Unhandled exception", t));
1178: }
1179: }
|