Package org.apache.openejb.core.stateful

Source Code of org.apache.openejb.core.stateful.StatefulContainer

/**
* 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.stateful;

import java.lang.reflect.Method;
import java.rmi.RemoteException;
import java.rmi.dgc.VMID;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.ejb.EJBAccessException;
import javax.ejb.EJBException;
import javax.ejb.EJBHome;
import javax.ejb.EJBLocalHome;
import javax.ejb.RemoveException;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.transaction.TransactionManager;
import javax.transaction.TransactionRequiredException;

import org.apache.openejb.ApplicationException;
import org.apache.openejb.ContainerType;
import org.apache.openejb.DeploymentInfo;
import org.apache.openejb.OpenEJBException;
import org.apache.openejb.ProxyInfo;
import org.apache.openejb.RpcContainer;
import org.apache.openejb.InvalidateReferenceException;
import org.apache.openejb.InterfaceType;
import org.apache.openejb.core.CoreDeploymentInfo;
import org.apache.openejb.core.Operation;
import org.apache.openejb.core.ThreadContext;
import org.apache.openejb.core.ExceptionType;
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.loader.SystemInstance;
import org.apache.openejb.persistence.EntityManagerAlreadyRegisteredException;
import org.apache.openejb.persistence.JtaEntityManagerRegistry;
import org.apache.openejb.spi.SecurityService;
import org.apache.openejb.util.Index;
import org.apache.openejb.util.LogCategory;
import org.apache.openejb.util.Logger;

/**
* @org.apache.xbean.XBean element="statefulContainer"
*/
public class StatefulContainer implements RpcContainer, TransactionContainer {
    private static final Logger logger = Logger.getInstance(LogCategory.OPENEJB, "org.apache.openejb.util.resources");

    private final Object containerID;
    private final TransactionManager transactionManager;
    private final SecurityService securityService;
    protected final StatefulInstanceManager instanceManager;
    // todo this should be part of the constructor
    protected final JtaEntityManagerRegistry entityManagerRegistry = SystemInstance.get().getComponent(JtaEntityManagerRegistry.class);

    /**
     * Index used for getDeployments() and getDeploymentInfo(deploymentId).
     */
    protected final Map<Object, DeploymentInfo> deploymentsById = new HashMap<Object, DeploymentInfo>();


    public StatefulContainer(Object id,
        TransactionManager transactionManager,
        SecurityService securityService,
        Class passivator,
        int timeOut,
        int poolSize,
        int bulkPassivate) throws OpenEJBException {
        this.containerID = id;
        this.transactionManager = transactionManager;
        this.securityService = securityService;

        instanceManager = newStatefulInstanceManager(transactionManager,
            securityService,
            passivator,
            timeOut,
            poolSize,
            bulkPassivate);
    }

    protected StatefulInstanceManager newStatefulInstanceManager(TransactionManager transactionManager,
        SecurityService securityService,
        Class passivator,
        int timeOut,
        int poolSize,
        int bulkPassivate) throws OpenEJBException {
        return new StatefulInstanceManager(transactionManager,
            securityService,
            entityManagerRegistry,
            passivator,
            timeOut,
            poolSize,
            bulkPassivate);
    }

    private Map<Method, MethodType> getLifecycleMethodsOfInterface(CoreDeploymentInfo deploymentInfo) {
        Map<Method, MethodType> methods = new HashMap<Method, MethodType>();

        List<Method> removeMethods = deploymentInfo.getRemoveMethods();
        for (Method removeMethod : removeMethods) {
            methods.put(removeMethod, MethodType.REMOVE);

            for (Class businessLocal : deploymentInfo.getBusinessLocalInterfaces()) {
                try {
                    Method method = businessLocal.getMethod(removeMethod.getName());
                    methods.put(method, MethodType.REMOVE);
                } catch (NoSuchMethodException thatsFine) {
                }
            }

            for (Class businessRemote : deploymentInfo.getBusinessRemoteInterfaces()) {
                try {
                    Method method = businessRemote.getMethod(removeMethod.getName());
                    methods.put(method, MethodType.REMOVE);
                } catch (NoSuchMethodException thatsFine) {
                }
            }
        }

        Class legacyRemote = deploymentInfo.getRemoteInterface();
        if (legacyRemote != null) {
            try {
                Method method = legacyRemote.getMethod("remove");
                methods.put(method, MethodType.REMOVE);
            } catch (NoSuchMethodException thatsFine) {
            }
        }

        Class legacyLocal = deploymentInfo.getLocalInterface();
        if (legacyLocal != null) {
            try {
                Method method = legacyLocal.getMethod("remove");
                methods.put(method, MethodType.REMOVE);
            } catch (NoSuchMethodException thatsFine) {
            }
        }

        Class businessLocalHomeInterface = deploymentInfo.getBusinessLocalInterface();
        if (businessLocalHomeInterface != null) {
            for (Method method : DeploymentInfo.BusinessLocalHome.class.getMethods()) {
                if (method.getName().startsWith("create")) {
                    methods.put(method, MethodType.CREATE);
                } else if (method.getName().equals("remove")) {
                    methods.put(method, MethodType.REMOVE);
                }
            }
        }

        Class businessRemoteHomeInterface = deploymentInfo.getBusinessRemoteInterface();
        if (businessRemoteHomeInterface != null) {
            for (Method method : DeploymentInfo.BusinessRemoteHome.class.getMethods()) {
                if (method.getName().startsWith("create")) {
                    methods.put(method, MethodType.CREATE);
                } else if (method.getName().equals("remove")) {
                    methods.put(method, MethodType.REMOVE);
                }
            }
        }

        Class homeInterface = deploymentInfo.getHomeInterface();
        if (homeInterface != null) {
            for (Method method : homeInterface.getMethods()) {
                if (method.getName().startsWith("create")) {
                    methods.put(method, MethodType.CREATE);
                } else if (method.getName().equals("remove")) {
                    methods.put(method, MethodType.REMOVE);
                }
            }
        }

        Class localHomeInterface = deploymentInfo.getLocalHomeInterface();
        if (localHomeInterface != null) {
            for (Method method : localHomeInterface.getMethods()) {
                if (method.getName().startsWith("create")) {
                    methods.put(method, MethodType.CREATE);
                } else if (method.getName().equals("remove")) {
                    methods.put(method, MethodType.REMOVE);
                }
            }
        }
        return methods;
    }

    public static enum MethodType {
        CREATE, REMOVE, BUSINESS
    }

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

    public Object getContainerID() {
        return containerID;
    }

    public StatefulInstanceManager getInstanceManager() {
        return instanceManager;
    }

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

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

    public void deploy(DeploymentInfo deploymentInfo) throws OpenEJBException {
        deploy((CoreDeploymentInfo) deploymentInfo);
    }

    public void undeploy(DeploymentInfo info) throws OpenEJBException {
        undeploy((CoreDeploymentInfo) info);
    }

    private synchronized void undeploy(CoreDeploymentInfo deploymentInfo) throws OpenEJBException {
        deploymentsById.remove(deploymentInfo.getDeploymentID());
        deploymentInfo.setContainer(null);
        deploymentInfo.setContainerData(null);
        instanceManager.undeploy(deploymentInfo);
    }

    private synchronized void deploy(CoreDeploymentInfo deploymentInfo) throws OpenEJBException {
        Map<Method, MethodType> methods = getLifecycleMethodsOfInterface(deploymentInfo);

        deploymentsById.put(deploymentInfo.getDeploymentID(), deploymentInfo);
        deploymentInfo.setContainer(this);
        instanceManager.deploy(deploymentInfo, new Index<Method, MethodType>(methods));
    }

    /**
     * @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 deployID, Class callInterface, Method callMethod, Object [] args, Object primKey) throws OpenEJBException {
        CoreDeploymentInfo deployInfo = (CoreDeploymentInfo) this.getDeploymentInfo(deployID);
        if (deployInfo == null)
            throw new OpenEJBException("Deployment does not exist in this container. Deployment(id='" + deployID + "'), Container(id='" + containerID + "')");

        MethodType methodType = instanceManager.getMethodIndex(deployInfo).get(callMethod);
        methodType = (methodType != null) ? methodType : MethodType.BUSINESS;

        switch (methodType) {
            case CREATE:
                return createEJBObject(deployInfo, callInterface, callMethod, args);
            case REMOVE:
                return removeEJBObject(deployInfo, primKey, callInterface, callMethod, args);
            default:
                return businessMethod(deployInfo, primKey, callInterface, callMethod, args);
        }
    }

    protected ProxyInfo createEJBObject(CoreDeploymentInfo deploymentInfo, Class callInterface, Method callMethod, Object [] args) throws OpenEJBException {
        // generate a new primary key
        Object primaryKey = newPrimaryKey();


        ThreadContext createContext = new ThreadContext(deploymentInfo, primaryKey);
        createContext.setCurrentOperation(Operation.CREATE);
        createContext.setCurrentAllowedStates(StatefulContext.getStates());
        ThreadContext oldCallContext = ThreadContext.enter(createContext);

        try {
            checkAuthorization(deploymentInfo, callMethod, callInterface);

            // create the extended entity managers
            Index<EntityManagerFactory, EntityManager> entityManagers = createEntityManagers(deploymentInfo);
            // register them
            if (entityManagers != null) {
                try {
                    entityManagerRegistry.addEntityManagers((String) deploymentInfo.getDeploymentID(), primaryKey, entityManagers);
                } catch (EntityManagerAlreadyRegisteredException e) {
                    throw new EJBException(e);
                }
            }

            // allocate a new instance
            Object o = instanceManager.newInstance(primaryKey, deploymentInfo.getBeanClass());
            StatefulInstanceManager.Instance instance = (StatefulInstanceManager.Instance) o;

            instanceManager.setEntityManagers(createContext, entityManagers);

            if (!callMethod.getDeclaringClass().equals(DeploymentInfo.BusinessLocalHome.class) && !callMethod.getDeclaringClass().equals(DeploymentInfo.BusinessRemoteHome.class)){

                Method createOrInit = deploymentInfo.getMatchingBeanMethod(callMethod);

                InterceptorStack interceptorStack = new InterceptorStack(instance.bean, createOrInit, Operation.CREATE, new ArrayList<InterceptorData>(), new HashMap<String,Object>());

                _invoke(callMethod, interceptorStack, args, instance, createContext);
            }

            instanceManager.poolInstance(createContext, instance);

            return new ProxyInfo(deploymentInfo, primaryKey);
        } finally {
            ThreadContext.exit(oldCallContext);
        }
    }

    protected Object newPrimaryKey() {
        return new VMID();
    }

    protected Object removeEJBObject(CoreDeploymentInfo deploymentInfo, Object primKey, Class callInterface, Method callMethod, Object[] args) throws OpenEJBException {
        ThreadContext callContext = new ThreadContext(deploymentInfo, primKey);
        ThreadContext oldCallContext = ThreadContext.enter(callContext);
        try {
            checkAuthorization(deploymentInfo, callMethod, callInterface);

            InterfaceType type = deploymentInfo.getInterfaceType(callInterface);
            if (type.isComponent() && instanceManager.getBeanTransaction(callContext) != null) {
                throw new ApplicationException(new RemoveException("A stateful EJB enrolled in a transaction can not be removed"));
            }

            Method runMethod = deploymentInfo.getMatchingBeanMethod(callMethod);
            StatefulInstanceManager.Instance instance = (StatefulInstanceManager.Instance) instanceManager.obtainInstance(primKey, callContext);

            if (instance == null) throw new ApplicationException(new javax.ejb.NoSuchEJBException());

            boolean retain = false;
            try {
                callContext.setCurrentAllowedStates(StatefulContext.getStates());
                callContext.setCurrentOperation(Operation.REMOVE);
                callContext.setInvokedInterface(callInterface);

                Class<?> declaringClass = callMethod.getDeclaringClass();
                if (declaringClass.equals(EJBHome.class) || declaringClass.equals(EJBLocalHome.class)){
                    args = new Object[]{}; // no args to pass on home.remove(remote) calls
                }
               
                List<InterceptorData> interceptors = deploymentInfo.getMethodInterceptors(runMethod);
                InterceptorStack interceptorStack = new InterceptorStack(instance.bean, runMethod, Operation.REMOVE, interceptors, instance.interceptors);
                return _invoke(callMethod, interceptorStack, args, instance, callContext);

            } catch(InvalidateReferenceException e){
                throw e;
            } catch(ApplicationException e){
                if (type.isBusiness()){
                    retain = deploymentInfo.retainIfExeption(runMethod);
                    throw e;
                } else {
                    return null;
                }
            } finally {
                if (retain){
                    instanceManager.poolInstance(callContext, instance);
                } else {
                    callContext.setCurrentOperation(Operation.PRE_DESTROY);

                    try {
                        List<InterceptorData> callbackInterceptors = deploymentInfo.getCallbackInterceptors();
                        InterceptorStack interceptorStack = new InterceptorStack(instance.bean, null, Operation.PRE_DESTROY, callbackInterceptors, instance.interceptors);
                        interceptorStack.invoke();
                    } catch (Throwable callbackException) {
                        String logMessage = "An unexpected exception occured while invoking the preDestroy method on the removed Stateful SessionBean instance; " + callbackException.getClass().getName() + " " + callbackException.getMessage();

                        /* [1] Log the exception or error */
                        logger.error(logMessage);

                    } finally {
                        callContext.setCurrentOperation(Operation.REMOVE);
                    }

                    // todo destroy extended persistence contexts
                    instanceManager.freeInstance(callContext);
                }
            }
        } finally {
            ThreadContext.exit(oldCallContext);
        }
    }

    protected Object businessMethod(CoreDeploymentInfo deploymentInfo, Object primKey, Class callInterface, Method callMethod, Object[] args) throws OpenEJBException {
        ThreadContext callContext = new ThreadContext(deploymentInfo, primKey);
        ThreadContext oldCallContext = ThreadContext.enter(callContext);
        try {
            checkAuthorization(deploymentInfo, callMethod, callInterface);

            Object bean = instanceManager.obtainInstance(primKey, callContext);
            callContext.setCurrentOperation(Operation.BUSINESS);
            callContext.setCurrentAllowedStates(StatefulContext.getStates());
            callContext.setInvokedInterface(callInterface);
            Method runMethod = deploymentInfo.getMatchingBeanMethod(callMethod);

            callContext.set(Method.class, runMethod);

            StatefulInstanceManager.Instance instance = (StatefulInstanceManager.Instance) bean;

            List<InterceptorData> interceptors = deploymentInfo.getMethodInterceptors(runMethod);
            InterceptorStack interceptorStack = new InterceptorStack(instance.bean, runMethod, Operation.BUSINESS, interceptors, instance.interceptors);
            Object returnValue = _invoke(callMethod, interceptorStack, args, bean, callContext);

            instanceManager.poolInstance(callContext, bean);

            return returnValue;
        } finally {
            ThreadContext.exit(oldCallContext);
        }
    }

    private void checkAuthorization(CoreDeploymentInfo deployInfo, Method callMethod, Class callInterface) throws ApplicationException {
        boolean authorized = securityService.isCallerAuthorized(callMethod, deployInfo.getInterfaceType(callInterface));
        if (!authorized) {
            throw new ApplicationException(new EJBAccessException("Unauthorized Access by Principal Denied"));
        }
    }

    protected Object _invoke(Method callMethod, InterceptorStack interceptorStack, Object [] args, Object bean, ThreadContext callContext) throws OpenEJBException {

        TransactionPolicy txPolicy = callContext.getDeploymentInfo().getTransactionPolicy(callMethod);
        TransactionContext txContext = new TransactionContext(callContext, transactionManager);
        try {
            txPolicy.beforeInvoke(bean, txContext);
        } catch (ApplicationException e) {
            if (e.getRootCause() instanceof TransactionRequiredException ||
                    e.getRootCause() instanceof RemoteException) {

                instanceManager.poolInstance(callContext, bean);
            }
            throw e;
        }

        Object returnValue = null;
        try {
            registerEntityManagers(callContext);
            if (args == null){
                returnValue = interceptorStack.invoke();
            } else {
                returnValue = interceptorStack.invoke(args);
            }
        } catch (Throwable re) {// handle reflection exception
            ExceptionType type = callContext.getDeploymentInfo().getExceptionType(re);
            if (type == ExceptionType.SYSTEM) {
                /* System Exception ****************************/

                txPolicy.handleSystemException(re, bean, txContext);
            } else {
                /* Application Exception ***********************/
                instanceManager.poolInstance(callContext, bean);

                txPolicy.handleApplicationException(re, type == ExceptionType.APPLICATION_ROLLBACK, txContext);
            }
        } finally {
            unregisterEntityManagers(callContext);
            txPolicy.afterInvoke(bean, txContext);
        }

        return returnValue;
    }

    private Index<EntityManagerFactory, EntityManager> createEntityManagers(CoreDeploymentInfo deploymentInfo) {
        // create the extended entity managers
        Index<EntityManagerFactory, Map> factories = deploymentInfo.getExtendedEntityManagerFactories();
        Index<EntityManagerFactory, EntityManager> entityManagers = null;
        if (factories != null && factories.size() > 0) {
            entityManagers = new Index<EntityManagerFactory, EntityManager>(new ArrayList<EntityManagerFactory>(factories.keySet()));
            for (Map.Entry<EntityManagerFactory, Map> entry : factories.entrySet()) {
                EntityManagerFactory entityManagerFactory = entry.getKey();
                Map properties = entry.getValue();


                EntityManager entityManager = entityManagerRegistry.getInheritedEntityManager(entityManagerFactory);
                if (entityManager == null) {
                    if (properties != null) {
                        entityManager = entityManagerFactory.createEntityManager(properties);
                    } else {
                        entityManager = entityManagerFactory.createEntityManager();
                    }
                }
                entityManagers.put(entityManagerFactory, entityManager);
            }
        }
        return entityManagers;
    }

    private void registerEntityManagers(ThreadContext callContext) throws OpenEJBException {
        if (entityManagerRegistry == null) return;

        CoreDeploymentInfo deploymentInfo = callContext.getDeploymentInfo();

        // get the factories
        Index<EntityManagerFactory, Map> factories = deploymentInfo.getExtendedEntityManagerFactories();
        if (factories == null) return;

        // get the managers for the factories
        Object primaryKey = callContext.getPrimaryKey();
        Map<EntityManagerFactory, EntityManager> entityManagers = instanceManager.getEntityManagers(callContext, factories);
        if (entityManagers == null) return;

        // register them
        try {
            entityManagerRegistry.addEntityManagers((String) deploymentInfo.getDeploymentID(), primaryKey, entityManagers);
        } catch (EntityManagerAlreadyRegisteredException e) {
            throw new EJBException(e);
        }
    }

    private void unregisterEntityManagers(ThreadContext callContext) {
        if (entityManagerRegistry == null) return;

        CoreDeploymentInfo deploymentInfo = callContext.getDeploymentInfo();

        // get the managers for the factories
        Object primaryKey = callContext.getPrimaryKey();

        // register them
        entityManagerRegistry.removeEntityManagers((String) deploymentInfo.getDeploymentID(), primaryKey);
    }

    public void discardInstance(Object bean, ThreadContext threadContext) {
        try {
            instanceManager.freeInstance(threadContext);
        } catch (Throwable t) {
            logger.error("", t);
        }
    }
}
TOP

Related Classes of org.apache.openejb.core.stateful.StatefulContainer

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.