Save This Page
Home » openejb-3.1.2-src » org.apache » openejb » core » stateful » [javadoc | source]
    1   /**
    2    * Licensed to the Apache Software Foundation (ASF) under one or more
    3    * contributor license agreements.  See the NOTICE file distributed with
    4    * this work for additional information regarding copyright ownership.
    5    * The ASF licenses this file to You under the Apache License, Version 2.0
    6    * (the "License"); you may not use this file except in compliance with
    7    * the License.  You may obtain a copy of the License at
    8    *
    9    *     http://www.apache.org/licenses/LICENSE-2.0
   10    *
   11    * Unless required by applicable law or agreed to in writing, software
   12    * distributed under the License is distributed on an "AS IS" BASIS,
   13    * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   14    * See the License for the specific language governing permissions and
   15    * limitations under the License.
   16    */
   17   package org.apache.openejb.core.stateful;
   18   
   19   import java.lang.reflect.Method;
   20   import java.rmi.NoSuchObjectException;
   21   import java.rmi.RemoteException;
   22   import java.rmi.dgc.VMID;
   23   import java.util.ArrayList;
   24   import java.util.HashMap;
   25   import java.util.List;
   26   import java.util.Map;
   27   import java.util.concurrent.ConcurrentHashMap;
   28   import javax.ejb.EJBAccessException;
   29   import javax.ejb.EJBException;
   30   import javax.ejb.EJBHome;
   31   import javax.ejb.EJBLocalHome;
   32   import javax.ejb.RemoveException;
   33   import javax.ejb.SessionBean;
   34   import javax.ejb.SessionContext;
   35   import javax.ejb.SessionSynchronization;
   36   import javax.ejb.NoSuchEJBException;
   37   import javax.naming.Context;
   38   import javax.naming.NamingException;
   39   import javax.persistence.EntityManager;
   40   import javax.persistence.EntityManagerFactory;
   41   import javax.transaction.Transaction;
   42   
   43   import org.apache.openejb.ApplicationException;
   44   import org.apache.openejb.ContainerType;
   45   import org.apache.openejb.DeploymentInfo;
   46   import org.apache.openejb.InjectionProcessor;
   47   import org.apache.openejb.InterfaceType;
   48   import org.apache.openejb.InvalidateReferenceException;
   49   import org.apache.openejb.OpenEJBException;
   50   import org.apache.openejb.ProxyInfo;
   51   import org.apache.openejb.RpcContainer;
   52   import org.apache.openejb.SystemException;
   53   import static org.apache.openejb.InjectionProcessor.unwrap;
   54   import org.apache.openejb.core.CoreDeploymentInfo;
   55   import org.apache.openejb.core.ExceptionType;
   56   import static org.apache.openejb.core.ExceptionType.APPLICATION_ROLLBACK;
   57   import static org.apache.openejb.core.ExceptionType.SYSTEM;
   58   import org.apache.openejb.core.Operation;
   59   import org.apache.openejb.core.ThreadContext;
   60   import org.apache.openejb.core.interceptor.InterceptorData;
   61   import org.apache.openejb.core.interceptor.InterceptorStack;
   62   import org.apache.openejb.core.stateful.Cache.CacheFilter;
   63   import org.apache.openejb.core.stateful.Cache.CacheListener;
   64   import org.apache.openejb.core.transaction.BeanTransactionPolicy;
   65   import org.apache.openejb.core.transaction.BeanTransactionPolicy.SuspendedTransaction;
   66   import org.apache.openejb.core.transaction.EjbTransactionUtil;
   67   import static org.apache.openejb.core.transaction.EjbTransactionUtil.createTransactionPolicy;
   68   import static org.apache.openejb.core.transaction.EjbTransactionUtil.handleApplicationException;
   69   import static org.apache.openejb.core.transaction.EjbTransactionUtil.handleSystemException;
   70   import org.apache.openejb.core.transaction.EjbUserTransaction;
   71   import org.apache.openejb.core.transaction.TransactionPolicy;
   72   import org.apache.openejb.core.transaction.JtaTransactionPolicy;
   73   import org.apache.openejb.core.transaction.TransactionPolicy.TransactionSynchronization;
   74   import org.apache.openejb.loader.SystemInstance;
   75   import org.apache.openejb.persistence.EntityManagerAlreadyRegisteredException;
   76   import org.apache.openejb.persistence.JtaEntityManagerRegistry;
   77   import org.apache.openejb.spi.SecurityService;
   78   import org.apache.openejb.util.Index;
   79   import org.apache.openejb.util.LogCategory;
   80   import org.apache.openejb.util.Logger;
   81   import org.apache.xbean.recipe.ConstructionException;
   82   
   83   public class StatefulContainer implements RpcContainer {
   84       private static final Logger logger = Logger.getInstance(LogCategory.OPENEJB, "org.apache.openejb.util.resources");
   85   
   86       private final Object containerID;
   87       private final SecurityService securityService;
   88   
   89       // todo this should be part of the constructor
   90       protected final JtaEntityManagerRegistry entityManagerRegistry = SystemInstance.get().getComponent(JtaEntityManagerRegistry.class);
   91   
   92       /**
   93        * Index used for getDeployments() and getDeploymentInfo(deploymentId).
   94        */
   95       protected final Map<Object, DeploymentInfo> deploymentsById = new HashMap<Object, DeploymentInfo>();
   96   
   97       protected final Cache<Object, Instance> cache;
   98       private final ConcurrentHashMap<Object, Instance> checkedOutInstances = new ConcurrentHashMap<Object, Instance>();
   99   
  100       public StatefulContainer(Object id, SecurityService securityService, Cache<Object, Instance> cache) {
  101           this.containerID = id;
  102           this.securityService = securityService;
  103           this.cache = cache;
  104           cache.setListener(new StatefulCacheListener());
  105       }
  106   
  107       private Map<Method, MethodType> getLifecycleMethodsOfInterface(CoreDeploymentInfo deploymentInfo) {
  108           Map<Method, MethodType> methods = new HashMap<Method, MethodType>();
  109   
  110           List<Method> removeMethods = deploymentInfo.getRemoveMethods();
  111           for (Method removeMethod : removeMethods) {
  112               methods.put(removeMethod, MethodType.REMOVE);
  113   
  114               for (Class businessLocal : deploymentInfo.getBusinessLocalInterfaces()) {
  115                   try {
  116                       Method method = businessLocal.getMethod(removeMethod.getName());
  117                       methods.put(method, MethodType.REMOVE);
  118                   } catch (NoSuchMethodException thatsFine) {
  119                   }
  120               }
  121   
  122               for (Class businessRemote : deploymentInfo.getBusinessRemoteInterfaces()) {
  123                   try {
  124                       Method method = businessRemote.getMethod(removeMethod.getName());
  125                       methods.put(method, MethodType.REMOVE);
  126                   } catch (NoSuchMethodException thatsFine) {
  127                   }
  128               }
  129           }
  130   
  131           Class legacyRemote = deploymentInfo.getRemoteInterface();
  132           if (legacyRemote != null) {
  133               try {
  134                   Method method = legacyRemote.getMethod("remove");
  135                   methods.put(method, MethodType.REMOVE);
  136               } catch (NoSuchMethodException thatsFine) {
  137               }
  138           }
  139   
  140           Class legacyLocal = deploymentInfo.getLocalInterface();
  141           if (legacyLocal != null) {
  142               try {
  143                   Method method = legacyLocal.getMethod("remove");
  144                   methods.put(method, MethodType.REMOVE);
  145               } catch (NoSuchMethodException thatsFine) {
  146               }
  147           }
  148   
  149           Class businessLocalHomeInterface = deploymentInfo.getBusinessLocalInterface();
  150           if (businessLocalHomeInterface != null) {
  151               for (Method method : DeploymentInfo.BusinessLocalHome.class.getMethods()) {
  152                   if (method.getName().startsWith("create")) {
  153                       methods.put(method, MethodType.CREATE);
  154                   } else if (method.getName().equals("remove")) {
  155                       methods.put(method, MethodType.REMOVE);
  156                   }
  157               }
  158           }
  159   
  160           Class businessRemoteHomeInterface = deploymentInfo.getBusinessRemoteInterface();
  161           if (businessRemoteHomeInterface != null) {
  162               for (Method method : DeploymentInfo.BusinessRemoteHome.class.getMethods()) {
  163                   if (method.getName().startsWith("create")) {
  164                       methods.put(method, MethodType.CREATE);
  165                   } else if (method.getName().equals("remove")) {
  166                       methods.put(method, MethodType.REMOVE);
  167                   }
  168               }
  169           }
  170   
  171           Class homeInterface = deploymentInfo.getHomeInterface();
  172           if (homeInterface != null) {
  173               for (Method method : homeInterface.getMethods()) {
  174                   if (method.getName().startsWith("create")) {
  175                       methods.put(method, MethodType.CREATE);
  176                   } else if (method.getName().equals("remove")) {
  177                       methods.put(method, MethodType.REMOVE);
  178                   }
  179               }
  180           }
  181   
  182           Class localHomeInterface = deploymentInfo.getLocalHomeInterface();
  183           if (localHomeInterface != null) {
  184               for (Method method : localHomeInterface.getMethods()) {
  185                   if (method.getName().startsWith("create")) {
  186                       methods.put(method, MethodType.CREATE);
  187                   } else if (method.getName().equals("remove")) {
  188                       methods.put(method, MethodType.REMOVE);
  189                   }
  190               }
  191           }
  192           return methods;
  193       }
  194   
  195       public static enum MethodType {
  196           CREATE, REMOVE, BUSINESS
  197       }
  198   
  199       public ContainerType getContainerType() {
  200           return ContainerType.STATEFUL;
  201       }
  202   
  203       public Object getContainerID() {
  204           return containerID;
  205       }
  206   
  207       public synchronized DeploymentInfo[] deployments() {
  208           return deploymentsById.values().toArray(new DeploymentInfo[deploymentsById.size()]);
  209       }
  210   
  211       public synchronized DeploymentInfo getDeploymentInfo(Object deploymentID) {
  212           return deploymentsById.get(deploymentID);
  213       }
  214   
  215       public void deploy(DeploymentInfo deploymentInfo) throws OpenEJBException {
  216           deploy((CoreDeploymentInfo) deploymentInfo);
  217       }
  218   
  219       public void undeploy(DeploymentInfo info) throws OpenEJBException {
  220           undeploy((CoreDeploymentInfo) info);
  221       }
  222   
  223       private synchronized void undeploy(final CoreDeploymentInfo deploymentInfo) throws OpenEJBException {
  224           deploymentsById.remove(deploymentInfo.getDeploymentID());
  225           deploymentInfo.setContainer(null);
  226           deploymentInfo.setContainerData(null);
  227   
  228           cache.removeAll(new CacheFilter<Instance>() {
  229               public boolean matches(Instance instance) {
  230                   return deploymentInfo == instance.deploymentInfo;
  231               }
  232           });
  233   
  234           StatefulContainerData data = (StatefulContainerData) deploymentInfo.getContainerData();
  235           if (data != null) {
  236               deploymentInfo.setContainerData(null);
  237           }
  238       }
  239   
  240       private synchronized void deploy(CoreDeploymentInfo deploymentInfo) throws OpenEJBException {
  241           Map<Method, MethodType> methods = getLifecycleMethodsOfInterface(deploymentInfo);
  242   
  243           deploymentsById.put(deploymentInfo.getDeploymentID(), deploymentInfo);
  244           deploymentInfo.setContainer(this);
  245           deploymentInfo.setContainerData(new StatefulContainerData(new Index<Method, MethodType>(methods)));
  246       }
  247   
  248       /**
  249        * @deprecated use invoke signature without 'securityIdentity' argument.
  250        */
  251       public Object invoke(Object deployID, Method callMethod, Object[] args, Object primKey, Object securityIdentity) throws OpenEJBException {
  252           return invoke(deployID, null, callMethod.getDeclaringClass(), callMethod, args, primKey);
  253       }
  254   
  255       public Object invoke(Object deployID, Class callInterface, Method callMethod, Object[] args, Object primKey) throws OpenEJBException {
  256           return invoke(deployID, null, callInterface, callMethod, args, primKey);
  257       }
  258   
  259       public Object invoke(Object deployID, InterfaceType type, Class callInterface, Method callMethod, Object[] args, Object primKey) throws OpenEJBException {
  260           CoreDeploymentInfo deployInfo = (CoreDeploymentInfo) this.getDeploymentInfo(deployID);
  261   
  262           if (deployInfo == null) throw new OpenEJBException("Deployment does not exist in this container. Deployment(id='"+deployID+"'), Container(id='"+containerID+"')");
  263   
  264           // Use the backup way to determine call type if null was supplied.
  265           if (type == null) type = deployInfo.getInterfaceType(callInterface);
  266   
  267           StatefulContainerData data = (StatefulContainerData) deployInfo.getContainerData();
  268           MethodType methodType = data.getMethodIndex().get(callMethod);
  269           methodType = (methodType != null) ? methodType : MethodType.BUSINESS;
  270   
  271           switch (methodType) {
  272               case CREATE:
  273                   return createEJBObject(deployInfo, callMethod, args, type);
  274               case REMOVE:
  275                   return removeEJBObject(deployInfo, primKey, callInterface, callMethod, args, type);
  276               default:
  277                   return businessMethod(deployInfo, primKey, callInterface, callMethod, args, type);
  278           }
  279       }
  280   
  281       protected ProxyInfo createEJBObject(CoreDeploymentInfo deploymentInfo, Method callMethod, Object[] args, InterfaceType interfaceType) throws OpenEJBException {
  282           // generate a new primary key
  283           Object primaryKey = newPrimaryKey();
  284   
  285   
  286           ThreadContext createContext = new ThreadContext(deploymentInfo, primaryKey);
  287           ThreadContext oldCallContext = ThreadContext.enter(createContext);
  288           try {
  289               // Security check
  290               checkAuthorization(callMethod, interfaceType);
  291   
  292               // Create the extended entity managers for this instance
  293               Index<EntityManagerFactory, EntityManager> entityManagers = createEntityManagers(deploymentInfo);
  294   
  295               // Register the newly created entity managers
  296               if (entityManagers != null) {
  297                   try {
  298                       entityManagerRegistry.addEntityManagers((String) deploymentInfo.getDeploymentID(), primaryKey, entityManagers);
  299                   } catch (EntityManagerAlreadyRegisteredException e) {
  300                       throw new EJBException(e);
  301                   }
  302               }
  303   
  304               createContext.setCurrentOperation(Operation.CREATE);
  305               createContext.setCurrentAllowedStates(StatefulContext.getStates());
  306   
  307               // Start transaction
  308               TransactionPolicy txPolicy = createTransactionPolicy(createContext.getDeploymentInfo().getTransactionType(callMethod), createContext);
  309   
  310               Instance instance = null;
  311               try {
  312                   // Create new instance
  313                   instance = newInstance(primaryKey, deploymentInfo.getBeanClass(), entityManagers);
  314   
  315                   // Register for synchronization callbacks
  316                   registerSessionSynchronization(instance, createContext);
  317                 
  318                   // Invoke create for legacy beans
  319                   if (!callMethod.getDeclaringClass().equals(DeploymentInfo.BusinessLocalHome.class) &&
  320                           !callMethod.getDeclaringClass().equals(DeploymentInfo.BusinessRemoteHome.class)){
  321   
  322                       // Setup for business invocation
  323                       Method createOrInit = deploymentInfo.getMatchingBeanMethod(callMethod);
  324                       createContext.set(Method.class, createOrInit);
  325   
  326                       // Initialize interceptor stack
  327                       InterceptorStack interceptorStack = new InterceptorStack(instance.bean, createOrInit, Operation.CREATE, new ArrayList<InterceptorData>(), new HashMap<String, Object>());
  328   
  329                       // Invoke
  330                       if (args == null){
  331                           interceptorStack.invoke();
  332                       } else {
  333                           interceptorStack.invoke(args);
  334                       }
  335                   }
  336               } catch (Throwable e) {
  337                   handleException(createContext, txPolicy, e);
  338               } finally {
  339                   afterInvoke(createContext, txPolicy, instance);
  340               }
  341   
  342               return new ProxyInfo(deploymentInfo, primaryKey);
  343           } finally {
  344               ThreadContext.exit(oldCallContext);
  345           }
  346       }
  347   
  348       protected Object newPrimaryKey() {
  349           return new VMID();
  350       }
  351   
  352       protected Object removeEJBObject(CoreDeploymentInfo deploymentInfo, Object primKey, Class callInterface, Method callMethod, Object[] args, InterfaceType interfaceType) throws OpenEJBException {
  353           if (primKey == null) throw new NullPointerException("primKey is null");
  354   
  355           ThreadContext callContext = new ThreadContext(deploymentInfo, primKey);
  356           ThreadContext oldCallContext = ThreadContext.enter(callContext);
  357           try {
  358               // Security check
  359               checkAuthorization(callMethod, interfaceType);
  360   
  361               // If a bean managed transaction is active, the bean can not be removed
  362               if (interfaceType.isComponent()) {
  363                   Instance instance = checkedOutInstances.get(primKey);
  364   
  365                   /**
  366                    * According to EJB 3.0 "4.4.4 Restrictions for Transactions" any remove methods
  367                    * from home or component interfaces must not be allowed if the bean instance is
  368                    * in a transaction.  Unfortunately, the Java EE 5 TCK has tests that ignore the
  369                    * restrictions in 4.4.4 and expect beans in transactions can be removed via their
  370                    * home or component interface.   The test to see if the bean instance implements
  371                    * javax.ejb.SessionBean is a workaround for passing the TCK while the tests in
  372                    * question can be challenged or the spec can be changed/updated.
  373                    */
  374                   if (instance != null && instance.bean instanceof javax.ejb.SessionBean) {
  375                       throw new ApplicationException(new RemoveException("A stateful EJB enrolled in a transaction can not be removed"));
  376                   }
  377               }
  378   
  379               // Start transaction
  380               TransactionPolicy txPolicy = createTransactionPolicy(callContext.getDeploymentInfo().getTransactionType(callMethod), callContext);
  381   
  382               Object returnValue = null;
  383               boolean retain = false;
  384               Instance instance = null;
  385               Method runMethod = null;
  386               try {
  387                   // Obtain instance
  388                   instance = obtainInstance(primKey, callContext);
  389   
  390                   // Resume previous Bean transaction if there was one
  391                   if (txPolicy instanceof BeanTransactionPolicy){
  392                       // Resume previous Bean transaction if there was one
  393                       SuspendedTransaction suspendedTransaction = instance.getBeanTransaction();
  394                       if (suspendedTransaction != null) {
  395                           instance.setBeanTransaction(null);
  396                           BeanTransactionPolicy beanTxEnv = (BeanTransactionPolicy) txPolicy;
  397                           beanTxEnv.resumeUserTransaction(suspendedTransaction);
  398                       }
  399                   }
  400   
  401                   // Register the entity managers
  402                   registerEntityManagers(instance, callContext);
  403   
  404                   // Register for synchronization callbacks
  405                   registerSessionSynchronization(instance, callContext);
  406   
  407                   // Setup for remove invocation
  408                   callContext.setCurrentOperation(Operation.REMOVE);
  409                   callContext.setCurrentAllowedStates(StatefulContext.getStates());
  410                   callContext.setInvokedInterface(callInterface);
  411                   runMethod = deploymentInfo.getMatchingBeanMethod(callMethod);
  412                   callContext.set(Method.class, runMethod);
  413   
  414                   // Do not pass arguments on home.remove(remote) calls
  415                   Class<?> declaringClass = callMethod.getDeclaringClass();
  416                   if (declaringClass.equals(EJBHome.class) || declaringClass.equals(EJBLocalHome.class)){
  417                       args = new Object[]{};
  418                   }
  419                   
  420                   // Initialize interceptor stack
  421                   List<InterceptorData> interceptors = deploymentInfo.getMethodInterceptors(runMethod);
  422                   InterceptorStack interceptorStack = new InterceptorStack(instance.bean, runMethod, Operation.REMOVE, interceptors, instance.interceptors);
  423   
  424                   // Invoke
  425                   if (args == null){
  426                       returnValue = interceptorStack.invoke();
  427                   } else {
  428                       returnValue = interceptorStack.invoke(args);
  429                   }
  430               } catch (InvalidateReferenceException e) {
  431                   throw e;
  432               } catch (Throwable e) {
  433                   if (interfaceType.isBusiness()) {
  434                       retain = deploymentInfo.retainIfExeption(runMethod);
  435                       handleException(callContext, txPolicy, e);
  436                   } else {
  437                       try {
  438                           handleException(callContext, txPolicy, e);
  439                       } catch (ApplicationException ae){
  440                           // Don't throw application exceptions for non-business interface removes
  441                       }
  442                   }
  443               } finally {
  444                   if (!retain) {
  445                       try {
  446                           callContext.setCurrentOperation(Operation.PRE_DESTROY);
  447                           List<InterceptorData> callbackInterceptors = deploymentInfo.getCallbackInterceptors();
  448                           InterceptorStack interceptorStack = new InterceptorStack(instance.bean, null, Operation.PRE_DESTROY, callbackInterceptors, instance.interceptors);
  449                           interceptorStack.invoke();
  450                       } catch (Throwable callbackException) {
  451                           String logMessage = "An unexpected exception occured while invoking the preDestroy method on the removed Stateful SessionBean instance; " + callbackException.getClass().getName() + " " + callbackException.getMessage();
  452   
  453                           /* [1] Log the exception or error */
  454                           logger.error(logMessage);
  455   
  456                       } finally {
  457                           callContext.setCurrentOperation(Operation.REMOVE);
  458                       }
  459   
  460                       // todo destroy extended persistence contexts
  461                       discardInstance(callContext);
  462                   }
  463   
  464                   // Commit transaction
  465                   afterInvoke(callContext, txPolicy, instance);
  466               }
  467   
  468               return returnValue;
  469           } finally {
  470               ThreadContext.exit(oldCallContext);
  471           }
  472       }
  473   
  474       protected Object businessMethod(CoreDeploymentInfo deploymentInfo, Object primKey, Class callInterface, Method callMethod, Object[] args, InterfaceType interfaceType) throws OpenEJBException {
  475           ThreadContext callContext = new ThreadContext(deploymentInfo, primKey);
  476           ThreadContext oldCallContext = ThreadContext.enter(callContext);
  477           try {
  478               // Security check
  479               checkAuthorization(callMethod, interfaceType);
  480   
  481               // Start transaction
  482               TransactionPolicy txPolicy = createTransactionPolicy(callContext.getDeploymentInfo().getTransactionType(callMethod), callContext);
  483   
  484               Object returnValue = null;
  485               Instance instance = null;
  486               try {
  487                   // Obtain instance
  488                   instance = obtainInstance(primKey, callContext);
  489   
  490                   // Resume previous Bean transaction if there was one
  491                   if (txPolicy instanceof BeanTransactionPolicy){
  492                       SuspendedTransaction suspendedTransaction = instance.getBeanTransaction();
  493                       if (suspendedTransaction != null) {
  494                           instance.setBeanTransaction(null);
  495                           BeanTransactionPolicy beanTxEnv = (BeanTransactionPolicy) txPolicy;
  496                           beanTxEnv.resumeUserTransaction(suspendedTransaction);
  497                       }
  498                   }
  499   
  500                   // Register the entity managers
  501                   registerEntityManagers(instance, callContext);
  502                   // Register for synchronization callbacks
  503                   registerSessionSynchronization(instance, callContext);
  504   
  505                   // Setup for business invocation
  506                   callContext.setCurrentOperation(Operation.BUSINESS);
  507                   callContext.setCurrentAllowedStates(StatefulContext.getStates());
  508                   callContext.setInvokedInterface(callInterface);
  509                   Method runMethod = deploymentInfo.getMatchingBeanMethod(callMethod);
  510                   callContext.set(Method.class, runMethod);
  511   
  512                   // Initialize interceptor stack
  513                   List<InterceptorData> interceptors = deploymentInfo.getMethodInterceptors(runMethod);
  514                   InterceptorStack interceptorStack = new InterceptorStack(instance.bean, runMethod, Operation.BUSINESS, interceptors, instance.interceptors);
  515   
  516                   // Invoke
  517                   returnValue = interceptorStack.invoke(args);
  518               } catch (Throwable e) {
  519                   handleException(callContext, txPolicy, e);
  520               } finally {
  521                   // Commit transaction
  522                   afterInvoke(callContext, txPolicy, instance);
  523               }
  524               return returnValue;
  525           } finally {
  526               ThreadContext.exit(oldCallContext);
  527           }
  528       }
  529   
  530       private Instance newInstance(Object primaryKey, Class beanClass, Map<EntityManagerFactory, EntityManager> entityManagers) throws OpenEJBException {
  531           Instance instance = null;
  532   
  533           ThreadContext threadContext = ThreadContext.getThreadContext();
  534           Operation currentOperation = threadContext.getCurrentOperation();
  535           try {
  536               ThreadContext callContext = ThreadContext.getThreadContext();
  537               CoreDeploymentInfo deploymentInfo = callContext.getDeploymentInfo();
  538               Context ctx = deploymentInfo.getJndiEnc();
  539   
  540               // Get or create the session context
  541               SessionContext sessionContext;
  542               synchronized (this) {
  543                   try {
  544                       sessionContext = (SessionContext) ctx.lookup("java:comp/EJBContext");
  545                   } catch (NamingException e1) {
  546                       StatefulUserTransaction userTransaction = new StatefulUserTransaction(new EjbUserTransaction(), entityManagerRegistry);
  547                       sessionContext = new StatefulContext(securityService, userTransaction);
  548                       ctx.bind("java:comp/EJBContext", sessionContext);
  549                   }
  550               }
  551   
  552               // Create bean instance
  553               InjectionProcessor injectionProcessor = new InjectionProcessor(beanClass, deploymentInfo.getInjections(), null, null, unwrap(ctx));
  554               try {
  555                   if (SessionBean.class.isAssignableFrom(beanClass) || beanClass.getMethod("setSessionContext", SessionContext.class) != null) {
  556                       callContext.setCurrentOperation(Operation.INJECTION);
  557                       injectionProcessor.setProperty("sessionContext", sessionContext);
  558                   }
  559               } catch (NoSuchMethodException ignored) {
  560                   // bean doesn't have a setSessionContext method, so we don't need to inject one
  561               }
  562               Object bean = injectionProcessor.createInstance();
  563   
  564               // Create interceptors
  565               HashMap<String, Object> interceptorInstances = new HashMap<String, Object>();
  566               for (InterceptorData interceptorData : deploymentInfo.getAllInterceptors()) {
  567                   if (interceptorData.getInterceptorClass().equals(beanClass)) {
  568                       continue;
  569                   }
  570   
  571                   Class clazz = interceptorData.getInterceptorClass();
  572                   InjectionProcessor interceptorInjector = new InjectionProcessor(clazz, deploymentInfo.getInjections(), unwrap(ctx));
  573                   try {
  574                       Object interceptorInstance = interceptorInjector.createInstance();
  575                       interceptorInstances.put(clazz.getName(), interceptorInstance);
  576                   } catch (ConstructionException e) {
  577                       throw new Exception("Failed to create interceptor: " + clazz.getName(), e);
  578                   }
  579               }
  580               interceptorInstances.put(beanClass.getName(), bean);
  581   
  582               // Invoke post construct method
  583               callContext.setCurrentOperation(Operation.POST_CONSTRUCT);
  584               List<InterceptorData> callbackInterceptors = deploymentInfo.getCallbackInterceptors();
  585               InterceptorStack interceptorStack = new InterceptorStack(bean, null, Operation.POST_CONSTRUCT, callbackInterceptors, interceptorInstances);
  586               interceptorStack.invoke();
  587   
  588               // Wrap-up everthing into a object
  589               instance = new Instance(deploymentInfo, primaryKey, bean, interceptorInstances, entityManagers);
  590   
  591           } catch (Throwable callbackException) {
  592               discardInstance(threadContext);
  593               handleSystemException(threadContext.getTransactionPolicy(), callbackException, threadContext);
  594           } finally {
  595               threadContext.setCurrentOperation(currentOperation);
  596           }
  597   
  598           // add to cache
  599           cache.add(primaryKey, instance);
  600   
  601           // instance starts checked-out
  602           checkedOutInstances.put(primaryKey, instance);
  603   
  604           return instance;
  605       }
  606   
  607       private Instance obtainInstance(Object primaryKey, ThreadContext callContext) throws OpenEJBException {
  608           if (primaryKey == null) {
  609               throw new SystemException(new NullPointerException("Cannot obtain an instance of the stateful session bean with a null session id"));
  610           }
  611   
  612           Transaction currentTransaction = getTransaction(callContext);
  613   
  614           // Find the instance
  615           Instance instance = checkedOutInstances.get(primaryKey);
  616           if (instance == null) {
  617               try {
  618                   instance = cache.checkOut(primaryKey);
  619               } catch (OpenEJBException e) {
  620                   throw e;
  621               } catch (Exception e) {
  622                   throw new SystemException("Unexpected load exception", e);
  623               }
  624   
  625               // Did we find the instance?
  626               if (instance == null) {
  627                   throw new InvalidateReferenceException(new NoSuchObjectException("Not Found"));
  628               }
  629   
  630               // remember instance until it is returned to the cache
  631               checkedOutInstances.put(primaryKey, instance);
  632           }
  633   
  634   
  635           synchronized (instance) {
  636               // Is the instance alreayd in use?
  637               if (instance.isInUse()) {
  638                   // the bean is already being invoked; the only reentrant/concurrent operations allowed are Session synchronization callbacks
  639                   Operation currentOperation = callContext.getCurrentOperation();
  640                   if (currentOperation != Operation.AFTER_COMPLETION && currentOperation != Operation.BEFORE_COMPLETION) {
  641                       throw new ApplicationException(new RemoteException("Concurrent calls not allowed."));
  642                   }
  643               }
  644   
  645               if (instance.getTransaction() != null){
  646                   if (!instance.getTransaction().equals(currentTransaction) && !instance.getLock().tryLock()) {
  647                       throw new ApplicationException(new RemoteException("Instance is in a transaction and cannot be invoked outside that transaction.  See EJB 3.0 Section 4.4.4"));
  648                   }
  649               } else {
  650                   instance.setTransaction(currentTransaction);
  651               }
  652   
  653               // Mark the instance in use so we can detect reentrant calls
  654               instance.setInUse(true);
  655   
  656               return instance;
  657           }
  658       }
  659   
  660       private Transaction getTransaction(ThreadContext callContext) {
  661           TransactionPolicy policy = callContext.getTransactionPolicy();
  662   
  663           Transaction currentTransaction = null;
  664           if (policy instanceof JtaTransactionPolicy) {
  665               JtaTransactionPolicy jtaPolicy = (JtaTransactionPolicy) policy;
  666   
  667               currentTransaction = jtaPolicy.getCurrentTransaction();
  668           }
  669           return currentTransaction;
  670       }
  671   
  672       private void releaseInstance(Instance instance) {
  673           // Don't pool if the bean has been undeployed
  674           if (instance.deploymentInfo.isDestroyed()) return;
  675   
  676           // verify the instance is not associated with a bean-managed transaction
  677           if (instance.getBeanTransaction() != null) {
  678               new IllegalStateException("Instance has an active bean-managed transaction");
  679           }
  680   
  681           // no longer in use
  682           instance.setInUse(false);
  683   
  684           if (instance.getTransaction() == null) {
  685               // return to cache
  686               cache.checkIn(instance.primaryKey);
  687   
  688               // no longer checked out
  689               checkedOutInstances.remove(instance.primaryKey);
  690           }
  691       }
  692   
  693       private void discardInstance(ThreadContext threadContext) {
  694           Object primaryKey = threadContext.getPrimaryKey();
  695           if (primaryKey == null) {
  696               return;
  697           }
  698   
  699           checkedOutInstances.remove(primaryKey);
  700           cache.remove(primaryKey);
  701       }
  702   
  703       private void checkAuthorization(Method callMethod, InterfaceType interfaceType) throws ApplicationException {
  704           boolean authorized = securityService.isCallerAuthorized(callMethod, interfaceType);
  705           if (!authorized) {
  706               throw new ApplicationException(new EJBAccessException("Unauthorized Access by Principal Denied"));
  707           }
  708       }
  709   
  710       private void handleException(ThreadContext callContext, TransactionPolicy txPolicy, Throwable e) throws ApplicationException {
  711           if (e instanceof ApplicationException) {
  712               throw (ApplicationException) e;
  713           }
  714   
  715           ExceptionType type = callContext.getDeploymentInfo().getExceptionType(e);
  716           if (type == SYSTEM) {
  717               discardInstance(callContext);
  718               handleSystemException(txPolicy, e, callContext);
  719           } else {
  720               handleApplicationException(txPolicy, e, type == APPLICATION_ROLLBACK);
  721           }
  722       }
  723   
  724       private void afterInvoke(ThreadContext callContext, TransactionPolicy txPolicy, Instance instance) throws OpenEJBException {
  725           try {
  726               unregisterEntityManagers(instance, callContext);
  727               if (instance != null && txPolicy instanceof BeanTransactionPolicy) {
  728                   // suspend the currently running transaction if any
  729                   SuspendedTransaction suspendedTransaction = null;
  730                   try {
  731                       BeanTransactionPolicy beanTxEnv = (BeanTransactionPolicy) txPolicy;
  732                       suspendedTransaction = beanTxEnv.suspendUserTransaction();
  733                   } catch (SystemException e) {
  734                       handleSystemException(txPolicy, e, callContext);
  735                   } finally {
  736                       instance.setBeanTransaction(suspendedTransaction);
  737                   }
  738               }
  739           } finally {
  740               if (instance != null) {
  741                   instance.setInUse(false);
  742               }
  743               EjbTransactionUtil.afterInvoke(txPolicy, callContext);
  744           }
  745       }
  746   
  747       private Index<EntityManagerFactory, EntityManager> createEntityManagers(CoreDeploymentInfo deploymentInfo) {
  748           // create the extended entity managers
  749           Index<EntityManagerFactory, Map> factories = deploymentInfo.getExtendedEntityManagerFactories();
  750           Index<EntityManagerFactory, EntityManager> entityManagers = null;
  751           if (factories != null && factories.size() > 0) {
  752               entityManagers = new Index<EntityManagerFactory, EntityManager>(new ArrayList<EntityManagerFactory>(factories.keySet()));
  753               for (Map.Entry<EntityManagerFactory, Map> entry : factories.entrySet()) {
  754                   EntityManagerFactory entityManagerFactory = entry.getKey();
  755                   Map properties = entry.getValue();
  756   
  757   
  758                   EntityManager entityManager = entityManagerRegistry.getInheritedEntityManager(entityManagerFactory);
  759                   if (entityManager == null) {
  760                       if (properties != null) {
  761                           entityManager = entityManagerFactory.createEntityManager(properties);
  762                       } else {
  763                           entityManager = entityManagerFactory.createEntityManager();
  764                       }
  765                   }
  766                   entityManagers.put(entityManagerFactory, entityManager);
  767               }
  768           }
  769           return entityManagers;
  770       }
  771   
  772       private void registerEntityManagers(Instance instance, ThreadContext callContext) throws OpenEJBException {
  773           if (entityManagerRegistry == null) return;
  774   
  775           CoreDeploymentInfo deploymentInfo = callContext.getDeploymentInfo();
  776   
  777           // get the factories
  778           Index<EntityManagerFactory, Map> factories = deploymentInfo.getExtendedEntityManagerFactories();
  779           if (factories == null) return;
  780   
  781           // get the managers for the factories
  782           Map<EntityManagerFactory, EntityManager> entityManagers = instance.getEntityManagers(factories);
  783           if (entityManagers == null) return;
  784   
  785           // register them
  786           try {
  787               entityManagerRegistry.addEntityManagers((String) deploymentInfo.getDeploymentID(), instance.primaryKey, entityManagers);
  788           } catch (EntityManagerAlreadyRegisteredException e) {
  789               throw new EJBException(e);
  790           }
  791       }
  792   
  793       private void unregisterEntityManagers(Instance instance, ThreadContext callContext) {
  794           if (entityManagerRegistry == null) return;
  795           if (instance == null) return;
  796   
  797           CoreDeploymentInfo deploymentInfo = callContext.getDeploymentInfo();
  798   
  799           // register them
  800           entityManagerRegistry.removeEntityManagers((String) deploymentInfo.getDeploymentID(), instance.primaryKey);
  801       }
  802   
  803   
  804       private void registerSessionSynchronization(Instance instance, ThreadContext callContext)  {
  805           TransactionPolicy txPolicy = callContext.getTransactionPolicy();
  806           if (txPolicy == null) {
  807               throw new IllegalStateException("ThreadContext does not contain a TransactionEnvironment");
  808           }
  809   
  810           SessionSynchronizationCoordinator coordinator = (SessionSynchronizationCoordinator) txPolicy.getResource(SessionSynchronizationCoordinator.class);
  811           if (coordinator == null) {
  812               coordinator = new SessionSynchronizationCoordinator(txPolicy);
  813               txPolicy.registerSynchronization(coordinator);
  814               txPolicy.putResource(SessionSynchronizationCoordinator.class, coordinator);
  815           }
  816   
  817           // SessionSynchronization are only enabled for beans after CREATE that are not bean-managed and implement the SessionSynchronization interface
  818           boolean synchronize = callContext.getCurrentOperation() != Operation.CREATE &&
  819                   callContext.getDeploymentInfo().isSessionSynchronized() &&
  820                   txPolicy.isTransactionActive();
  821   
  822           coordinator.registerSessionSynchronization(instance, callContext.getDeploymentInfo(), callContext.getPrimaryKey(), synchronize);
  823       }
  824   
  825       /**
  826        * SessionSynchronizationCoordinator handles afterBegin, beforeCompletion and afterCompletion callbacks.
  827        *
  828        * This class also is responsible for calling releaseInstance after the transaction completes. 
  829        */
  830       private class SessionSynchronizationCoordinator implements TransactionSynchronization {
  831           private final Map<Object, Synchronization> registry = new HashMap<Object, Synchronization>();
  832           private final TransactionPolicy txPolicy;
  833   
  834           private SessionSynchronizationCoordinator(TransactionPolicy txPolicy) {
  835               this.txPolicy = txPolicy;
  836           }
  837   
  838           public class Synchronization {
  839               private final Instance instance;
  840   
  841               private boolean callSessionSynchronization;
  842   
  843               public Synchronization(Instance instance) {
  844                   this.instance = instance;
  845               }
  846   
  847               public synchronized boolean isCallSessionSynchronization() {
  848                   return callSessionSynchronization;
  849               }
  850   
  851               public synchronized boolean setCallSessionSynchronization(boolean synchronize) {
  852                   boolean oldValue = this.callSessionSynchronization;
  853                   this.callSessionSynchronization = synchronize;
  854                   return oldValue;
  855               }
  856   
  857           }
  858   
  859           private void registerSessionSynchronization(Instance instance, CoreDeploymentInfo deploymentInfo, Object primaryKey, boolean synchronize) {
  860   
  861               Synchronization synchronization = registry.get(primaryKey);
  862   
  863               if (synchronization == null){
  864                   synchronization = new Synchronization(instance);
  865                   registry.put(primaryKey, synchronization);
  866               }
  867   
  868               boolean wasSynchronized = synchronization.setCallSessionSynchronization(synchronize);
  869   
  870               // check if afterBegin has already been invoked or if this is not a session synchronization bean
  871               if (wasSynchronized || !synchronize) {
  872                   return;
  873               }
  874   
  875               // Invoke afterBegin
  876               ThreadContext callContext = new ThreadContext(instance.deploymentInfo, instance.primaryKey, Operation.AFTER_BEGIN);
  877               callContext.setCurrentAllowedStates(StatefulContext.getStates());
  878               ThreadContext oldCallContext = ThreadContext.enter(callContext);
  879               try {
  880   
  881                   Method afterBegin = SessionSynchronization.class.getMethod("afterBegin");
  882   
  883                   List<InterceptorData> interceptors = deploymentInfo.getMethodInterceptors(afterBegin);
  884                   InterceptorStack interceptorStack = new InterceptorStack(instance.bean, afterBegin, Operation.AFTER_BEGIN, interceptors, instance.interceptors);
  885                   interceptorStack.invoke();
  886   
  887               } catch (Exception e) {
  888                   String message = "An unexpected system exception occured while invoking the afterBegin method on the SessionSynchronization object";
  889   
  890                   // [1] Log the exception or error
  891                   logger.error(message, e);
  892   
  893                   // Caller handles transaction rollback and discardInstance
  894   
  895                   // [4] throw the java.rmi.RemoteException to the client
  896                   throw new RuntimeException(message, e);
  897               } finally {
  898                   ThreadContext.exit(oldCallContext);
  899               }
  900           }
  901   
  902           public void beforeCompletion() {
  903               for (Synchronization synchronization : registry.values()) {
  904   
  905                   Instance instance = synchronization.instance;
  906   
  907                   // don't call beforeCompletion when transaction is marked rollback only
  908                   if (txPolicy.isRollbackOnly()) return;
  909   
  910                   // only call beforeCompletion on beans with session synchronization
  911                   if (!synchronization.isCallSessionSynchronization()) continue;
  912   
  913                   // Invoke beforeCompletion
  914                   ThreadContext callContext = new ThreadContext(instance.deploymentInfo, instance.primaryKey, Operation.BEFORE_COMPLETION);
  915                   callContext.setCurrentAllowedStates(StatefulContext.getStates());
  916                   ThreadContext oldCallContext = ThreadContext.enter(callContext);
  917                   try {
  918                       instance.setInUse(true);
  919   
  920                       Method beforeCompletion = SessionSynchronization.class.getMethod("beforeCompletion");
  921   
  922                       List<InterceptorData> interceptors = instance.deploymentInfo.getMethodInterceptors(beforeCompletion);
  923                       InterceptorStack interceptorStack = new InterceptorStack(instance.bean, beforeCompletion, Operation.BEFORE_COMPLETION, interceptors, instance.interceptors);
  924                       interceptorStack.invoke();
  925   
  926                       instance.setInUse(false);
  927                   } catch (InvalidateReferenceException e) {
  928                       // exception has alredy been handled
  929                   } catch (Exception e) {
  930                       String message = "An unexpected system exception occured while invoking the beforeCompletion method on the SessionSynchronization object";
  931   
  932                       // [1] Log the exception or error
  933                       logger.error(message, e);
  934   
  935                       // [2] Mark the transaction for rollback.
  936                       txPolicy.setRollbackOnly();
  937   
  938                       // [3] Discard the instance
  939                       discardInstance(callContext);
  940   
  941                       // [4] throw the java.rmi.RemoteException to the client
  942                       throw new RuntimeException(message, e);
  943                   } finally {
  944                       ThreadContext.exit(oldCallContext);
  945                   }
  946               }
  947           }
  948   
  949           public void afterCompletion(Status status) {
  950               Throwable firstException = null;
  951               for (Synchronization synchronization : registry.values()) {
  952   
  953                   Instance instance = synchronization.instance;
  954   
  955                   ThreadContext callContext = new ThreadContext(instance.deploymentInfo, instance.primaryKey, Operation.AFTER_COMPLETION);
  956                   callContext.setCurrentAllowedStates(StatefulContext.getStates());
  957                   ThreadContext oldCallContext = ThreadContext.enter(callContext);
  958                   try {
  959                       instance.setInUse(true);
  960                       if (synchronization.isCallSessionSynchronization()) {
  961                           Method afterCompletion = SessionSynchronization.class.getMethod("afterCompletion", boolean.class);
  962   
  963                           List<InterceptorData> interceptors = instance.deploymentInfo.getMethodInterceptors(afterCompletion);
  964                           InterceptorStack interceptorStack = new InterceptorStack(instance.bean, afterCompletion, Operation.AFTER_COMPLETION, interceptors, instance.interceptors);
  965                           interceptorStack.invoke(status == Status.COMMITTED);
  966                       }
  967                       instance.setTransaction(null);
  968                       releaseInstance(instance);
  969                   } catch (InvalidateReferenceException inv) {
  970                       // exception has alredy been handled
  971                   } catch (Throwable e) {
  972                       String message = "An unexpected system exception occured while invoking the afterCompletion method on the SessionSynchronization object";
  973   
  974                       // [1] Log the exception or error
  975                       logger.error(message, e);
  976   
  977                       // Transaction is complete so can not be rolled back
  978   
  979                       // [3] Discard the instance
  980                       discardInstance(callContext);
  981   
  982                       // [4] throw throw first exception to the client
  983                       if (firstException == null) firstException = e;
  984                   } finally {
  985                       ThreadContext.exit(oldCallContext);
  986                   }
  987               }
  988   
  989               if (firstException != null) {
  990                   throw new RuntimeException("An unexpected system exception occured while invoking the afterCompletion method on the SessionSynchronization object", firstException);
  991               }
  992           }
  993       }
  994   
  995       public class StatefulCacheListener implements CacheListener<Instance> {
  996           public void afterLoad(Instance instance) throws SystemException, ApplicationException {
  997               CoreDeploymentInfo deploymentInfo = instance.deploymentInfo;
  998   
  999               ThreadContext threadContext = new ThreadContext(instance.deploymentInfo, instance.primaryKey, Operation.ACTIVATE);
 1000               ThreadContext oldContext = ThreadContext.enter(threadContext);
 1001               try {
 1002                   Method remove = instance.bean instanceof SessionBean ? SessionBean.class.getMethod("ejbActivate") : null;
 1003   
 1004                   List<InterceptorData> callbackInterceptors = deploymentInfo.getCallbackInterceptors();
 1005                   InterceptorStack interceptorStack = new InterceptorStack(instance.bean, remove, Operation.ACTIVATE, callbackInterceptors, instance.interceptors);
 1006   
 1007                   interceptorStack.invoke();
 1008               } catch (Throwable callbackException) {
 1009                   discardInstance(threadContext);
 1010                   handleSystemException(threadContext.getTransactionPolicy(), callbackException, threadContext);
 1011               } finally {
 1012                   ThreadContext.exit(oldContext);
 1013               }
 1014           }
 1015   
 1016           public void beforeStore(Instance instance) {
 1017               CoreDeploymentInfo deploymentInfo = instance.deploymentInfo;
 1018   
 1019               ThreadContext threadContext = new ThreadContext(deploymentInfo, instance.primaryKey, Operation.PASSIVATE);
 1020               ThreadContext oldContext = ThreadContext.enter(threadContext);
 1021               try {
 1022                   Method passivate = instance.bean instanceof SessionBean ? SessionBean.class.getMethod("ejbPassivate") : null;
 1023   
 1024                   List<InterceptorData> callbackInterceptors = deploymentInfo.getCallbackInterceptors();
 1025                   InterceptorStack interceptorStack = new InterceptorStack(instance.bean, passivate, Operation.PASSIVATE, callbackInterceptors, instance.interceptors);
 1026   
 1027                   interceptorStack.invoke();
 1028   
 1029               } catch (Throwable e) {
 1030                   logger.error("An unexpected exception occured while invoking the ejbPassivate method on the Stateful SessionBean instance", e);
 1031               } finally {
 1032                   ThreadContext.exit(oldContext);
 1033               }
 1034           }
 1035   
 1036           public void timedOut(Instance instance) {
 1037               CoreDeploymentInfo deploymentInfo = instance.deploymentInfo;
 1038   
 1039               ThreadContext threadContext = new ThreadContext(deploymentInfo, instance.primaryKey, Operation.PRE_DESTROY);
 1040               threadContext.setCurrentAllowedStates(StatefulContext.getStates());
 1041               ThreadContext oldContext = ThreadContext.enter(threadContext);
 1042               try {
 1043                   Method remove = instance.bean instanceof SessionBean ? SessionBean.class.getMethod("ejbRemove") : null;
 1044   
 1045                   List<InterceptorData> callbackInterceptors = deploymentInfo.getCallbackInterceptors();
 1046                   InterceptorStack interceptorStack = new InterceptorStack(instance.bean, remove, Operation.PRE_DESTROY, callbackInterceptors, instance.interceptors);
 1047   
 1048                   interceptorStack.invoke();
 1049               } catch (Throwable e) {
 1050                   logger.error("An unexpected exception occured while invoking the ejbRemove method on the timed-out Stateful SessionBean instance", e);
 1051               } finally {
 1052                   logger.info("Removing the timed-out stateful session bean instance " + instance.primaryKey);
 1053                   ThreadContext.exit(oldContext);
 1054               }
 1055           }
 1056       }    
 1057   
 1058       private static class StatefulContainerData {
 1059           private final Index<Method, MethodType> methodIndex;
 1060   
 1061           private StatefulContainerData(Index<Method, MethodType> methodIndex) {
 1062               this.methodIndex = methodIndex;
 1063           }
 1064   
 1065           public Index<Method, MethodType> getMethodIndex() {
 1066               return methodIndex;
 1067           }
 1068   
 1069       }
 1070   }

Save This Page
Home » openejb-3.1.2-src » org.apache » openejb » core » stateful » [javadoc | source]