Package org.apache.openejb.core.mdb

Source Code of org.apache.openejb.core.mdb.MdbContainer$MdbCallContext

/**
*
* 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.SystemException;
import org.apache.openejb.ApplicationException;
import org.apache.openejb.ContainerType;
import org.apache.openejb.RpcContainer;
import org.apache.openejb.core.BaseContext;
import org.apache.openejb.core.CoreDeploymentInfo;
import org.apache.openejb.core.Operation;
import org.apache.openejb.core.ThreadContext;
import org.apache.openejb.core.timer.EjbTimerService;
import org.apache.openejb.core.interceptor.InterceptorData;
import org.apache.openejb.core.interceptor.InterceptorStack;
import org.apache.openejb.core.transaction.TransactionContainer;
import org.apache.openejb.core.transaction.TransactionContext;
import org.apache.openejb.core.transaction.TransactionPolicy;
import org.apache.openejb.spi.SecurityService;
import org.apache.openejb.util.LogCategory;
import org.apache.openejb.util.Logger;

import org.apache.xbean.recipe.ObjectRecipe;
import org.apache.xbean.recipe.Option;

import javax.transaction.TransactionManager;
import javax.transaction.Transaction;
import javax.transaction.xa.XAResource;
import javax.resource.spi.ResourceAdapter;
import javax.resource.spi.ActivationSpec;
import javax.resource.spi.UnavailableException;
import javax.resource.ResourceException;
import java.lang.reflect.Method;
import java.util.List;
import java.util.Map;
import java.util.Arrays;
import java.util.TreeSet;
import java.util.Set;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.ConcurrentHashMap;

public class MdbContainer implements RpcContainer, TransactionContainer {
    private static final Logger logger = Logger.getInstance(LogCategory.OPENEJB, "org.apache.openejb.util.resources");
    private static final Object[] NO_ARGS = new Object[0];

    private final Object containerID;
    private final TransactionManager transactionManager;
    private final SecurityService securityService;
    private final ResourceAdapter resourceAdapter;
    private final Class messageListenerInterface;
    private final Class activationSpecClass;
    private final int instanceLimit;

    private final ConcurrentMap<Object, CoreDeploymentInfo> deployments = new ConcurrentHashMap<Object, CoreDeploymentInfo>();

    public MdbContainer(Object containerID, TransactionManager transactionManager, SecurityService securityService, ResourceAdapter resourceAdapter, Class messageListenerInterface, Class activationSpecClass, int instanceLimit) {
        this.containerID = containerID;
        this.transactionManager = transactionManager;
        this.securityService = securityService;
        this.resourceAdapter = resourceAdapter;
        this.messageListenerInterface = messageListenerInterface;
        this.activationSpecClass = activationSpecClass;
        this.instanceLimit = instanceLimit;
    }

    public DeploymentInfo [] deployments() {
        return deployments.values().toArray(new DeploymentInfo[deployments.size()]);
    }

    public DeploymentInfo getDeploymentInfo(Object deploymentID) {
        return deployments.get(deploymentID);
    }

    public ContainerType getContainerType() {
        return ContainerType.MESSAGE_DRIVEN;
    }

    public Object getContainerID() {
        return containerID;
    }

    public ResourceAdapter getResourceAdapter() {
        return resourceAdapter;
    }

    public Class getMessageListenerInterface() {
        return messageListenerInterface;
    }

    public Class getActivationSpecClass() {
        return activationSpecClass;
    }

    public void deploy(DeploymentInfo info) throws OpenEJBException {
        CoreDeploymentInfo deploymentInfo = (CoreDeploymentInfo) info;
        Object deploymentId = deploymentInfo.getDeploymentID();
        if (!deploymentInfo.getMdbInterface().equals(messageListenerInterface)) {
            throw new OpenEJBException("Deployment '" + deploymentId + "' has message listener interface " +
                    deploymentInfo.getMdbInterface().getName() + " but this MDB container only supports " +
                    messageListenerInterface);
        }

        // create the activation spec
        ActivationSpec activationSpec = createActivationSpec(deploymentInfo);

        // create the message endpoint
        MdbInstanceFactory instanceFactory = new MdbInstanceFactory(deploymentInfo, transactionManager, securityService, instanceLimit);
        EndpointFactory endpointFactory = new EndpointFactory(activationSpec, this, deploymentInfo, instanceFactory);

        // update the data structures
        // this must be done before activating the endpoint since the ra may immedately begin delivering messages
        deploymentInfo.setContainer(this);
        deploymentInfo.setContainerData(endpointFactory);
        deployments.put(deploymentId, deploymentInfo);

        // activate the endpoint
        try {
            resourceAdapter.endpointActivation(endpointFactory, activationSpec);
        } catch (ResourceException e) {
            // activation failed... clean up
            deploymentInfo.setContainer(null);
            deploymentInfo.setContainerData(null);
            deployments.remove(deploymentId);

            throw new OpenEJBException(e);
        }

        // start the timer service
        EjbTimerService timerService = deploymentInfo.getEjbTimerService();
        if (timerService != null) {
            timerService.start();
        }
    }

    private ActivationSpec createActivationSpec(DeploymentInfo deploymentInfo)throws OpenEJBException {
        try {
            // initialize the object recipe
            ObjectRecipe objectRecipe = new ObjectRecipe(activationSpecClass);
            objectRecipe.allow(Option.IGNORE_MISSING_PROPERTIES);
            objectRecipe.disallow(Option.FIELD_INJECTION);

            Map<String, String> activationProperties = deploymentInfo.getActivationProperties();
            for (Map.Entry<String, String> entry : activationProperties.entrySet()) {
                objectRecipe.setMethodProperty(entry.getKey(), entry.getValue());
            }

            // create the activationSpec
            ActivationSpec activationSpec = (ActivationSpec) objectRecipe.create(activationSpecClass.getClassLoader());

            // verify all properties except "destination" and "destinationType" were consumed
            Set<String> unusedProperties = new TreeSet<String>(objectRecipe.getUnsetProperties().keySet());
            unusedProperties.remove("destination");
            unusedProperties.remove("destinationType");
            if (!unusedProperties.isEmpty()) {
                throw new IllegalArgumentException("No setter found for the activation spec properties: " + unusedProperties);
            }


            // validate the activation spec
            try {
                activationSpec.validate();
            } catch (UnsupportedOperationException uoe) {
                logger.info("ActivationSpec does not support validate. Implementation of validate is optional");
            }
           

            // set the resource adapter into the activation spec
            activationSpec.setResourceAdapter(resourceAdapter);

            return activationSpec;
        } catch (Exception e) {
            throw new OpenEJBException("Unable to create activation spec", e);
        }
    }

    public void undeploy(DeploymentInfo info) throws OpenEJBException {
        if (!(info instanceof CoreDeploymentInfo)) {
            return;
        }

        CoreDeploymentInfo deploymentInfo = (CoreDeploymentInfo) info;
        try {
            EndpointFactory endpointFactory = (EndpointFactory) deploymentInfo.getContainerData();
            if (endpointFactory != null) {
                resourceAdapter.endpointDeactivation(endpointFactory, endpointFactory.getActivationSpec());
            }
        } finally {
            deploymentInfo.setContainer(null);
            deploymentInfo.setContainerData(null);
            deployments.remove(deploymentInfo.getDeploymentID());
        }
    }

    /**
     * @deprecated use invoke signature without 'securityIdentity' argument.
     */
    public Object invoke(Object deployID, Method callMethod, Object[] args, Object primKey, Object securityIdentity) throws OpenEJBException {
        return invoke(deployID, callMethod.getDeclaringClass(), callMethod, args, primKey);
    }

    public Object invoke(Object deploymentId, Class callInterface, Method method, Object[] args, Object primKey) throws OpenEJBException {
        CoreDeploymentInfo deploymentInfo = (CoreDeploymentInfo) getDeploymentInfo(deploymentId);

        EndpointFactory endpointFactory = (EndpointFactory) deploymentInfo.getContainerData();
        MdbInstanceFactory instanceFactory = endpointFactory.getInstanceFactory();
        Instance instance = null;
        try {
            instance = (Instance) instanceFactory.createInstance(true);
        } catch (UnavailableException e) {
            throw new SystemException("Unable to create instance for invocation", e);
        }

        try {
            beforeDelivery(deploymentInfo, instance, method, null);
            Object value = invoke(instance, method, args);
            afterDelivery(instance);
            return value;
        } finally {
            instanceFactory.freeInstance(instance, true);
        }
    }

    public void beforeDelivery(CoreDeploymentInfo deployInfo, Object instance, Method method, XAResource xaResource) throws SystemException {
        // intialize call context
        ThreadContext callContext = new ThreadContext(deployInfo, null);
        ThreadContext oldContext = ThreadContext.enter(callContext);

        // create mdb context
        MdbCallContext mdbCallContext = new MdbCallContext();
        callContext.set(MdbCallContext.class, mdbCallContext);
        mdbCallContext.deliveryMethod = method;
        mdbCallContext.oldCallContext = oldContext;

        // add tx data
        mdbCallContext.txPolicy = deployInfo.getTransactionPolicy(method);
        mdbCallContext.txContext = new TransactionContext(callContext, transactionManager);

        // call the tx before method
        try {
            boolean adapterTransaction = transactionManager.getTransaction() != null;
            mdbCallContext.txPolicy.beforeInvoke(instance, mdbCallContext.txContext);

            // if we have an xaResource and a transaction was not imported from the adapter, enlist the xaResource
            if (xaResource != null && !adapterTransaction) {
                Transaction transaction = transactionManager.getTransaction();
                if (transaction != null) {
                    transaction.enlistResource(xaResource);
                }
            }
        } catch (ApplicationException e) {
            ThreadContext.exit(oldContext);
            throw new SystemException("Should never get an Application exception", e);
        } catch (SystemException e) {
            ThreadContext.exit(oldContext);
            throw e;
        } catch (Exception e) {
            ThreadContext.exit(oldContext);
            throw new SystemException("Unable to enlist xa resource in the transaction", e);
        }
    }

    public Object invoke(Object instance, Method method, Object... args) throws SystemException, ApplicationException {
        if (args == null) {
            args = NO_ARGS;
        }

        // get the context data
        ThreadContext callContext = ThreadContext.getThreadContext();
        CoreDeploymentInfo deployInfo = callContext.getDeploymentInfo();
        MdbCallContext mdbCallContext = callContext.get(MdbCallContext.class);

        if (mdbCallContext == null) {
            throw new IllegalStateException("beforeDelivery was not called");
        }

        // verify the delivery method passed to beforeDeliver is the same method that was invoked
        if (!mdbCallContext.deliveryMethod.getName().equals(method.getName()) ||
                !Arrays.deepEquals(mdbCallContext.deliveryMethod.getParameterTypes(), method.getParameterTypes())) {
            throw new IllegalStateException("Delivery method specified in beforeDelivery is not the delivery method called");
        }

        // remember the return value or exception so it can be logged
        Object returnValue = null;
        OpenEJBException openEjbException = null;
        Operation oldOperation = callContext.getCurrentOperation();
        callContext.setCurrentOperation(Operation.BUSINESS);
        BaseContext.State[] originalStates = callContext.setCurrentAllowedStates(MdbContext.getStates());
        try {
            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);


            callContext.set(Method.class, targetMethod);

            // invoke the target method
            returnValue = _invoke(instance, targetMethod, args, deployInfo, mdbCallContext);
            return returnValue;
        } catch (ApplicationException e) {
            openEjbException = e;
            throw e;
        } catch (SystemException e) {
            openEjbException = e;
            throw e;
        } finally {
            callContext.setCurrentOperation(oldOperation);
            callContext.setCurrentAllowedStates(originalStates);
            // Log the invocation results
            if (logger.isDebugEnabled()) {
                if (openEjbException == null) {
                    logger.debug("finished invoking method " + method.getName() + ". Return value:" + returnValue);
                } else {
                    Throwable exception = (openEjbException.getRootCause() != null) ? openEjbException.getRootCause() : openEjbException;
                    logger.debug("finished invoking method " + method.getName() + " with exception " + exception);
                }
            }
        }
    }

    private Object _invoke(Object instance, Method runMethod, Object [] args, DeploymentInfo deploymentInfo, MdbCallContext mdbCallContext) throws SystemException, ApplicationException {
        Object returnValue = null;
        try {
            List<InterceptorData> interceptors = deploymentInfo.getMethodInterceptors(runMethod);
            InterceptorStack interceptorStack = new InterceptorStack(((Instance)instance).bean, runMethod, Operation.BUSINESS, interceptors, ((Instance)instance).interceptors);
            returnValue = interceptorStack.invoke(args);           
            return returnValue;
        } catch (java.lang.reflect.InvocationTargetException ite) {// handle exceptions thrown by enterprise bean
            if (!isApplicationException(deploymentInfo, ite.getTargetException())) {
                //
                /// System Exception ****************************
                mdbCallContext.txPolicy.handleSystemException(ite.getTargetException(), instance, mdbCallContext.txContext);
            } else {
                //
                // Application Exception ***********************
                mdbCallContext.txPolicy.handleApplicationException(ite.getTargetException(), false, 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.
            if (!isApplicationException(deploymentInfo, re)) {
                //
                /// System Exception ****************************
                mdbCallContext.txPolicy.handleSystemException(re, instance, mdbCallContext.txContext);
            } else {
                //
                // Application Exception ***********************
                mdbCallContext.txPolicy.handleApplicationException(re, false, mdbCallContext.txContext);
            }
        }
        throw new AssertionError("Should not get here");
    }

    private boolean isApplicationException(DeploymentInfo deploymentInfo, Throwable e) {
        return e instanceof Exception && !(e instanceof RuntimeException);
    }

    public void afterDelivery(Object instance) throws SystemException {
        // get the mdb call context
        ThreadContext callContext = ThreadContext.getThreadContext();
        MdbCallContext mdbCallContext = callContext.get(MdbCallContext.class);

        // invoke the tx after method
        try {
            mdbCallContext.txPolicy.afterInvoke(instance, mdbCallContext.txContext);
        } catch (ApplicationException e) {
            throw new SystemException("Should never get an Application exception", e);
        } finally {
            ThreadContext.exit(mdbCallContext.oldCallContext);
        }
    }

    public void release(CoreDeploymentInfo deployInfo, Object instance) {
        // get the mdb call context
        ThreadContext callContext = ThreadContext.getThreadContext();
        if (callContext == null) {
            callContext = new ThreadContext(deployInfo, null);
            ThreadContext.enter(callContext);

        }

        // if we have an mdb call context we need to invoke the after invoke method
        MdbCallContext mdbCallContext = callContext.get(MdbCallContext.class);
        if (mdbCallContext != null) {
            try {
                mdbCallContext.txPolicy.afterInvoke(instance, mdbCallContext.txContext);
            } catch (Exception e) {
                logger.error("error while releasing message endpoint", e);
            } finally {
                EndpointFactory endpointFactory = (EndpointFactory) deployInfo.getContainerData();
                endpointFactory.getInstanceFactory().freeInstance((Instance)instance, false);
                ThreadContext.exit(mdbCallContext.oldCallContext);
            }
        }

    }

    private static class MdbCallContext {
        private Method deliveryMethod;
        private TransactionPolicy txPolicy;
        private TransactionContext txContext;
        private ThreadContext oldCallContext;
    }

    public void discardInstance(Object instance, ThreadContext context) {
    }
}
TOP

Related Classes of org.apache.openejb.core.mdb.MdbContainer$MdbCallContext

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.