You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@tomee.apache.org by da...@apache.org on 2006/11/30 18:37:38 UTC

svn commit: r481006 - in /incubator/openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb: ./ core/ core/mdb/

Author: dain
Date: Thu Nov 30 09:37:34 2006
New Revision: 481006

URL: http://svn.apache.org/viewvc?view=rev&rev=481006
Log:
Coppied and updated MDB container from openejb2

Added:
    incubator/openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/core/mdb/
    incubator/openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/core/mdb/EndpointFactory.java
    incubator/openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/core/mdb/EndpointHandler.java
    incubator/openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/core/mdb/MdbContainer.java
Modified:
    incubator/openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/DeploymentInfo.java
    incubator/openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/core/CoreDeploymentInfo.java

Modified: incubator/openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/DeploymentInfo.java
URL: http://svn.apache.org/viewvc/incubator/openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/DeploymentInfo.java?view=diff&rev=481006&r1=481005&r2=481006
==============================================================================
--- incubator/openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/DeploymentInfo.java (original)
+++ incubator/openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/DeploymentInfo.java Thu Nov 30 09:37:34 2006
@@ -71,6 +71,10 @@
 
     Class getInterface(InterfaceType interfaceType);
 
+    Class getMdbInterface();
+
+    ClassLoader getClassLoader();
+
     public interface BusinessLocalHome extends javax.ejb.EJBLocalHome {
         Object create();
     }

Modified: incubator/openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/core/CoreDeploymentInfo.java
URL: http://svn.apache.org/viewvc/incubator/openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/core/CoreDeploymentInfo.java?view=diff&rev=481006&r1=481005&r2=481006
==============================================================================
--- incubator/openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/core/CoreDeploymentInfo.java (original)
+++ incubator/openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/core/CoreDeploymentInfo.java Thu Nov 30 09:37:34 2006
@@ -31,6 +31,7 @@
 import javax.ejb.SessionSynchronization;
 import javax.ejb.EnterpriseBean;
 import javax.ejb.SessionBean;
+import javax.ejb.MessageDrivenBean;
 
 import org.apache.openejb.Container;
 import org.apache.openejb.RpcContainer;
@@ -74,6 +75,7 @@
     private Class pkClass;
     private Class businessLocal;
     private Class businessRemote;
+    private Class mdbInterface;
 
     private Method postConstruct;
     private Method preDestroy;
@@ -171,6 +173,22 @@
         }
     }
 
+    public CoreDeploymentInfo(DeploymentContext context, Class beanClass, Class mdbInterface) throws SystemException {
+        this.context = context;
+        this.beanClass = beanClass;
+        this.mdbInterface = mdbInterface;
+        this.componentType = BeanType.MESSAGE_DRIVEN;
+
+        if (MessageDrivenBean.class.isAssignableFrom(beanClass)){
+            try {
+                this.preDestroy = MessageDrivenBean.class.getMethod("ejbRemove");
+            } catch (NoSuchMethodException e) {
+                throw new IllegalStateException(e);
+            }
+        }
+        createMethodMap();
+    }
+
     public Object getContainerData() {
         return containerData;
     }
@@ -285,6 +303,10 @@
         return businessRemote;
     }
 
+    public Class getMdbInterface() {
+        return mdbInterface;
+    }
+
     public Class getPrimaryKeyClass() {
         return pkClass;
     }
@@ -337,6 +359,10 @@
         return context.getJndiContext();
     }
 
+    public ClassLoader getClassLoader() {
+        return context.getClassLoader();
+    }
+
     public boolean isReentrant() {
         return isReentrant;
     }
@@ -593,6 +619,9 @@
             throw new org.apache.openejb.SystemException(nsme);
         }
 
+        if (mdbInterface != null) {
+            mapObjectInterface(mdbInterface);
+        }
     }
 
     private void mapHomeInterface(Class intrface) {

Added: incubator/openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/core/mdb/EndpointFactory.java
URL: http://svn.apache.org/viewvc/incubator/openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/core/mdb/EndpointFactory.java?view=auto&rev=481006
==============================================================================
--- incubator/openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/core/mdb/EndpointFactory.java (added)
+++ incubator/openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/core/mdb/EndpointFactory.java Thu Nov 30 09:37:34 2006
@@ -0,0 +1,52 @@
+/**
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package org.apache.openejb.core.mdb;
+
+import org.apache.openejb.DeploymentInfo;
+
+import javax.resource.spi.UnavailableException;
+import javax.resource.spi.endpoint.MessageEndpoint;
+import javax.resource.spi.endpoint.MessageEndpointFactory;
+import javax.transaction.xa.XAResource;
+import java.lang.reflect.Method;
+import java.lang.reflect.Proxy;
+
+public class EndpointFactory implements MessageEndpointFactory {
+    private final MdbContainer mdbContainer;
+    private final DeploymentInfo deploymentInfo;
+    private final ClassLoader classLoader;
+    private final Class[] interfaces;
+
+    public EndpointFactory(MdbContainer mdbContainer, DeploymentInfo mdbDeploymentInfo) {
+        this.mdbContainer = mdbContainer;
+        this.deploymentInfo = mdbDeploymentInfo;
+        this.classLoader = mdbDeploymentInfo.getClassLoader();
+        interfaces = new Class[]{mdbDeploymentInfo.getMdbInterface(), MessageEndpoint.class};
+    }
+
+    public MessageEndpoint createEndpoint(XAResource xaResource) throws UnavailableException {
+        EndpointHandler endpointHandler = new EndpointHandler(mdbContainer, deploymentInfo, null, xaResource);
+        MessageEndpoint messageEndpoint = (MessageEndpoint) Proxy.newProxyInstance(classLoader, interfaces, endpointHandler);
+        return messageEndpoint;
+    }
+
+    public boolean isDeliveryTransacted(Method method) throws NoSuchMethodException {
+        byte transactionAttribute = deploymentInfo.getTransactionAttribute(method);
+        return DeploymentInfo.TX_REQUIRED == transactionAttribute;
+    }
+}

Added: incubator/openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/core/mdb/EndpointHandler.java
URL: http://svn.apache.org/viewvc/incubator/openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/core/mdb/EndpointHandler.java?view=auto&rev=481006
==============================================================================
--- incubator/openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/core/mdb/EndpointHandler.java (added)
+++ incubator/openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/core/mdb/EndpointHandler.java Thu Nov 30 09:37:34 2006
@@ -0,0 +1,248 @@
+/**
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package org.apache.openejb.core.mdb;
+
+import org.apache.openejb.DeploymentInfo;
+
+import javax.ejb.EJBException;
+import javax.resource.ResourceException;
+import javax.transaction.xa.XAResource;
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.Method;
+import java.util.Arrays;
+
+/**
+ * Container for the local interface of a Message Driven Bean.
+ * This container owns implementations of EJBLocalHome and EJBLocalObject
+ * that can be used by a client in the same classloader as the server.
+ * <p/>
+ * The implementation of the interfaces is generated using cglib FastClass
+ * proxies to avoid the overhead of native Java reflection.
+ * <p/>
+ * <p/>
+ * <p/>
+ * <p/>
+ * The J2EE connector and EJB specifications are not clear on what happens when beforeDelivery or
+ * afterDelivery throw an exception, so here is what we have decided:
+ * <p/>
+ * Exception from beforeDelivery:
+ * if container started TX, roll it back
+ * reset class loader to adapter classloader
+ * reset state to STATE_NONE
+ * <p/>
+ * Exception from delivery method:
+ * if container started TX, roll it back
+ * reset class loader to adapter classloader
+ * if state was STATE_BEFORE_CALLED, set state to STATE_ERROR so after can still be called
+ * <p/>
+ * Exception from afterDelivery:
+ * if container started TX, roll it back
+ * reset class loader to adapter classloader
+ * reset state to STATE_NONE
+ * <p/>
+ * One subtle side effect of this is if the adapter ignores an exception from beforeDelivery and
+ * continues with delivery and afterDelivery, the delivery will be treated as a single standalone
+ * delivery and the afterDelivery will throw an IllegalStateException.
+ *
+ * @version $Revision: 451417 $ $Date: 2006-09-29 13:13:22 -0700 (Fri, 29 Sep 2006) $
+ */
+public class EndpointHandler implements InvocationHandler {
+    private static enum State {
+        NONE, BEFORE_CALLED, METHOD_CALLED
+    }
+
+    private final MdbContainer container;
+    private final DeploymentInfo deployment;
+    private final Object instance;
+
+    private boolean released = false;
+    private State state = State.NONE;
+    private ClassLoader adapterClassLoader;
+
+    public EndpointHandler(MdbContainer container, DeploymentInfo deployment, Object instance, XAResource xaResource) {
+        this.container = container;
+        this.deployment = deployment;
+        this.instance = instance;
+    }
+
+    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
+        String methodName = method.getName();
+        Class<?>[] parameterTypes = method.getParameterTypes();
+
+        if (method.getDeclaringClass() == Object.class) {
+            if ("toString".equals(methodName) && parameterTypes.length == 0) {
+                return toString();
+            } else if ("equals".equals(methodName) && parameterTypes.length == 1) {
+                return equals(args[0]);
+            } else if ("hashCode".equals(methodName) && parameterTypes.length == 0) {
+                return hashCode();
+            } else {
+                throw new UnsupportedOperationException("Unkown method: " + method);
+            }
+        }
+
+        if ("beforeDelivery".equals(methodName) && Arrays.deepEquals(new Class[] {Method.class}, parameterTypes)) {
+            beforeDelivery((Method) args[0]);
+            return null;
+        } else if ("afterDelivery".equals(methodName) && parameterTypes.length == 0) {
+            afterDelivery();
+            return null;
+        } else if ("release".equals(methodName) && parameterTypes.length == 0) {
+            release();
+            return null;
+        } else {
+            Object value = deliverMessage(method, args);
+            return value;
+        }
+
+    }
+
+    public void beforeDelivery(Method method) throws NoSuchMethodException, ResourceException {
+        // verify current state
+        if (released) throw new IllegalStateException("Proxy has been released");
+        switch (state) {
+            case BEFORE_CALLED:
+                throw new IllegalStateException("beforeDelivery can not be called again until message is delivered and afterDelivery is called");
+            case METHOD_CALLED:
+                throw new IllegalStateException("The last message delivery must be completed with an afterDeliver before beforeDeliver can be called again");
+        }
+
+        // call afterDelivery on the container
+        installAppClassLoader();
+        try {
+            container.beforeDelivery(deployment.getDeploymentID(), instance, method);
+        } catch (NoSuchMethodException e) {
+            restoreAdapterClassLoader();
+            throw e;
+        } catch (ResourceException e) {
+            restoreAdapterClassLoader();
+            throw e;
+        } catch (Throwable throwable) {
+            restoreAdapterClassLoader();
+            throw new ResourceException(throwable);
+        }
+
+        // before completed successfully we are now ready to invoke bean
+        state = State.BEFORE_CALLED;
+    }
+
+    public Object deliverMessage(Method method, Object[] args) throws Throwable {
+        // verify current state
+        if (released) throw new IllegalStateException("Proxy has been released");
+        switch (state) {
+            case BEFORE_CALLED:
+                state = State.METHOD_CALLED;
+            case METHOD_CALLED:
+                throw new IllegalStateException("The last message delivery must be completed with an afterDeliver before another message can be delivered");
+        }
+
+
+        // if beforeDelivery was not called, call it now
+        if (state == State.NONE) {
+            try {
+                container.beforeDelivery(deployment.getDeploymentID(), instance, method);
+            } catch (Throwable throwable) {
+                if (throwable instanceof EJBException) {
+                    throw (EJBException) throwable;
+                }
+                throw (EJBException) new EJBException().initCause(throwable);
+            }
+        }
+
+        boolean exceptionThrown = false;
+        try {
+            Object value = container.invoke(instance, method, args);
+            return value;
+        } catch (Throwable throwable) {
+            exceptionThrown = true;
+            throw throwable;
+        } finally {
+            // if the adapter is not using before/after, we must call afterDelivery to clean up
+            if (state == State.NONE) {
+                try {
+                    container.afterDelivery(instance);
+                } catch (Throwable throwable) {
+                    // if bean threw an exception, do not override that exception
+                    if (!exceptionThrown) {
+                        EJBException ejbException;
+                        if (throwable instanceof EJBException) {
+                            ejbException = (EJBException) throwable;
+                        } else {
+                            ejbException = new EJBException();
+                            ejbException.initCause(throwable);
+                        }
+                        throw ejbException;
+                    }
+                }
+            }
+        }
+    }
+
+    public void afterDelivery() throws ResourceException {
+        // verify current state
+        if (released) throw new IllegalStateException("Proxy has been released");
+        switch (state) {
+            case BEFORE_CALLED:
+                throw new IllegalStateException("Exactally one message must be delivered between beforeDelivery and afterDelivery");
+            case NONE:
+                throw new IllegalStateException("afterDelivery may only be called if message delivery began with a beforeDelivery call");
+        }
+
+
+        // call afterDelivery on the container
+        try {
+            container.afterDelivery(instance);
+        } catch (ResourceException e) {
+            throw e;
+        } catch (Throwable throwable) {
+            throw new ResourceException(throwable);
+        } finally {
+            // we are now in the default NONE state
+            state = State.NONE;
+            restoreAdapterClassLoader();
+        }
+    }
+
+    public void release() {
+        if (released) return;
+        released = true;
+
+        // notify the container
+        try {
+            container.release(instance);
+        } finally {
+            restoreAdapterClassLoader();
+        }
+    }
+
+    private void installAppClassLoader() {
+        Thread currentThread = Thread.currentThread();
+
+        adapterClassLoader = currentThread.getContextClassLoader();
+        if (adapterClassLoader != deployment.getClassLoader()) {
+            currentThread.setContextClassLoader(deployment.getClassLoader());
+        }
+    }
+
+    private void restoreAdapterClassLoader() {
+        if (adapterClassLoader != deployment.getClassLoader()) {
+            Thread.currentThread().setContextClassLoader(adapterClassLoader);
+        }
+        adapterClassLoader = null;
+    }
+}

Added: incubator/openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/core/mdb/MdbContainer.java
URL: http://svn.apache.org/viewvc/incubator/openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/core/mdb/MdbContainer.java?view=auto&rev=481006
==============================================================================
--- incubator/openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/core/mdb/MdbContainer.java (added)
+++ incubator/openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/core/mdb/MdbContainer.java Thu Nov 30 09:37:34 2006
@@ -0,0 +1,209 @@
+/**
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package org.apache.openejb.core.mdb;
+
+import org.apache.openejb.OpenEJBException;
+import org.apache.openejb.DeploymentInfo;
+import org.apache.openejb.Container;
+import org.apache.openejb.core.ThreadContext;
+import org.apache.openejb.core.CoreDeploymentInfo;
+import org.apache.openejb.core.transaction.TransactionContext;
+import org.apache.openejb.core.transaction.TransactionPolicy;
+import org.apache.log4j.Logger;
+
+import javax.transaction.TransactionManager;
+import java.lang.reflect.Method;
+import java.rmi.RemoteException;
+import java.util.HashMap;
+import java.util.Map;
+
+public class MdbContainer implements Container {
+    private static final Logger logger = Logger.getLogger("OpenEJB");
+    private static final Object[] NO_ARGS = new Object[0];
+
+    private final Object containerID;
+    private final TransactionManager transactionManager;
+
+    private final Map<Object, DeploymentInfo> deploymentRegistry = new HashMap<Object, DeploymentInfo>();
+
+    public MdbContainer(Object containerID, TransactionManager transactionManager) {
+        this.containerID = containerID;
+        this.transactionManager = transactionManager;
+    }
+
+    public synchronized DeploymentInfo [] deployments() {
+        return deploymentRegistry.values().toArray(new DeploymentInfo[deploymentRegistry.size()]);
+    }
+
+    public synchronized DeploymentInfo getDeploymentInfo(Object deploymentID) {
+        return deploymentRegistry.get(deploymentID);
+    }
+
+    public int getContainerType() {
+        return Container.MESSAGE_DRIVEN;
+    }
+
+    public Object getContainerID() {
+        return containerID;
+    }
+
+    public synchronized void deploy(Object deploymentID, DeploymentInfo info) throws OpenEJBException {
+        deploymentRegistry.put(deploymentID, info);
+        CoreDeploymentInfo di = (CoreDeploymentInfo) info;
+        di.setContainer(this);
+    }
+
+    public synchronized void undeploy(Object deploymentID) throws OpenEJBException {
+        CoreDeploymentInfo di = (CoreDeploymentInfo) deploymentRegistry.remove(deploymentID);
+        di.setContainer(null);
+    }
+
+    public void beforeDelivery(Object deployId, Object instance, Method method) throws Throwable {
+        // get the target deployment (MDB)
+        CoreDeploymentInfo deployInfo = (CoreDeploymentInfo) getDeploymentInfo(deployId);
+
+        // obtain the context objects
+        ThreadContext callContext = ThreadContext.getThreadContext();
+        MdbCallContext mdbCallContext = new MdbCallContext();
+
+        // create the tx data
+        mdbCallContext.txPolicy = deployInfo.getTransactionPolicy(method);
+        mdbCallContext.txContext = new TransactionContext(callContext, transactionManager);
+
+        // call the tx before method
+        mdbCallContext.txPolicy.beforeInvoke(instance, mdbCallContext.txContext);
+
+        // save the tx data into the thread context
+        callContext.setDeploymentInfo(deployInfo);
+        callContext.setUnspecified(mdbCallContext);
+    }
+
+    public Object invoke(Object instance, Method method, Object... args) throws Throwable {
+        if (args == null) {
+            args = NO_ARGS;
+        }
+
+        Object returnValue = null;
+        Throwable exception = null;
+        try {
+            // get the context data
+            ThreadContext callContext = ThreadContext.getThreadContext();
+            CoreDeploymentInfo deployInfo = callContext.getDeploymentInfo();
+
+            if (logger.isInfoEnabled()) {
+                logger.info("invoking method " + method.getName() + " on " + deployInfo.getDeploymentID());
+            }
+
+            // determine the target method on the bean instance class
+            Method targetMethod = deployInfo.getMatchingBeanMethod(method);
+
+            // ivoke the target method
+            returnValue = _invoke(instance, targetMethod, args, (MdbCallContext) callContext.getUnspecified());
+            return returnValue;
+        } catch (org.apache.openejb.ApplicationException ae) {
+            // Application exceptions must be reported dirctly to the client. They
+            // do not impact the viability of the proxy.
+            exception = (ae.getRootCause() != null) ? ae.getRootCause() : ae;
+            throw exception;
+        } catch (org.apache.openejb.SystemException se) {
+            // A system exception would be highly unusual and would indicate a sever
+            // problem with the container system.
+            exception = (se.getRootCause() != null) ? se.getRootCause() : se;
+            logger.error("The container received an unexpected exception: ", exception);
+            throw new RemoteException("Container has suffered a SystemException", exception);
+        } catch (org.apache.openejb.OpenEJBException oe) {
+            // This is a normal container exception thrown while processing the request
+            exception = (oe.getRootCause() != null) ? oe.getRootCause() : oe;
+            logger.warn("The container received an unexpected exception: ", exception);
+            throw new RemoteException("Unknown Container Exception", oe.getRootCause());
+        } finally {
+            // Log the invocation results
+            if (logger.isDebugEnabled()) {
+                if (exception == null) {
+                    logger.debug("finished invoking method " + method.getName() + ". Return value:" + returnValue);
+                } else {
+                    logger.debug("finished invoking method " + method.getName() + " with exception " + exception);
+                }
+            } else if (logger.isInfoEnabled()) {
+                if (exception == null) {
+                    logger.debug("finished invoking method " + method.getName());
+                } else {
+                    logger.debug("finished invoking method " + method.getName() + " with exception " + exception);
+                }
+            }
+        }
+    }
+
+    private Object _invoke(Object instance, Method runMethod, Object [] args, MdbCallContext mdbCallContext) throws OpenEJBException {
+        try {
+            Object returnValue = runMethod.invoke(instance, args);
+            return returnValue;
+        } catch (java.lang.reflect.InvocationTargetException ite) {// handle exceptions thrown by enterprise bean
+            if (ite.getTargetException() instanceof RuntimeException) {
+                //
+                /// System Exception ****************************
+                mdbCallContext.txPolicy.handleSystemException(ite.getTargetException(), instance, mdbCallContext.txContext);
+            } else {
+                //
+                // Application Exception ***********************
+                mdbCallContext.txPolicy.handleApplicationException(ite.getTargetException(), mdbCallContext.txContext);
+            }
+        } catch (Throwable re) {// handle reflection exception
+            //  Any exception thrown by reflection; not by the enterprise bean. Possible
+            //  Exceptions are:
+            //    IllegalAccessException - if the underlying method is inaccessible.
+            //    IllegalArgumentException - if the number of actual and formal parameters differ, or if an unwrapping conversion fails.
+            //    NullPointerException - if the specified object is null and the method is an instance method.
+            //    ExceptionInInitializerError - if the initialization provoked by this method fails.
+            mdbCallContext.txPolicy.handleSystemException(re, instance, mdbCallContext.txContext);
+        }
+        throw new AssertionError("Should not get here");
+    }
+
+
+    public void afterDelivery(Object instance) throws Throwable {
+        // get the mdb call context
+        ThreadContext callContext = ThreadContext.getThreadContext();
+        MdbCallContext mdbCallContext = (MdbCallContext) callContext.getUnspecified();
+        ThreadContext.setThreadContext(null);
+
+        // invoke the tx after method
+        mdbCallContext.txPolicy.afterInvoke(instance, mdbCallContext.txContext);
+    }
+
+    public void release(Object instance) {
+        // get the mdb call context
+        ThreadContext callContext = ThreadContext.getThreadContext();
+        MdbCallContext mdbCallContext = (MdbCallContext) callContext.getUnspecified();
+        ThreadContext.setThreadContext(null);
+
+        // if we have an mdb call context we need to invoke the after invoke method
+        if (mdbCallContext != null) {
+            try {
+                mdbCallContext.txPolicy.afterInvoke(instance, mdbCallContext.txContext);
+            } catch (Exception e) {
+                logger.error("error while releasing message endpoint", e);
+            }
+        }
+    }
+
+    private static class MdbCallContext {
+        private TransactionPolicy txPolicy;
+        private TransactionContext txContext;
+    }
+}