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 org.apache.openejb.ApplicationException;
   20   import org.apache.openejb.Injection;
   21   import org.apache.openejb.InvalidateReferenceException;
   22   import org.apache.openejb.OpenEJBException;
   23   import org.apache.openejb.SystemException;
   24   import org.apache.openejb.core.BaseContext;
   25   import org.apache.openejb.core.CoreDeploymentInfo;
   26   import org.apache.openejb.core.CoreUserTransaction;
   27   import org.apache.openejb.core.Operation;
   28   import org.apache.openejb.core.ThreadContext;
   29   import org.apache.openejb.core.interceptor.InterceptorData;
   30   import org.apache.openejb.core.interceptor.InterceptorStack;
   31   import org.apache.openejb.core.ivm.IntraVmCopyMonitor;
   32   import org.apache.openejb.core.transaction.TransactionRolledbackException;
   33   import org.apache.openejb.persistence.JtaEntityManagerRegistry;
   34   import org.apache.openejb.spi.SecurityService;
   35   import org.apache.openejb.util.Index;
   36   import org.apache.openejb.util.LogCategory;
   37   import org.apache.openejb.util.Logger;
   38   import org.apache.openejb.util.PojoSerialization;
   39   import org.apache.xbean.recipe.ObjectRecipe;
   40   import org.apache.xbean.recipe.Option;
   41   import org.apache.xbean.recipe.StaticRecipe;
   42   import org.apache.xbean.recipe.ConstructionException;
   43   
   44   import javax.ejb.EJBException;
   45   import javax.ejb.SessionContext;
   46   import javax.ejb.SessionBean;
   47   import javax.naming.Context;
   48   import javax.naming.NamingException;
   49   import javax.persistence.EntityManager;
   50   import javax.persistence.EntityManagerFactory;
   51   import javax.transaction.Transaction;
   52   import javax.transaction.TransactionManager;
   53   import java.lang.reflect.Method;
   54   import java.rmi.NoSuchObjectException;
   55   import java.rmi.RemoteException;
   56   import java.util.Hashtable;
   57   import java.util.LinkedList;
   58   import java.util.Map;
   59   import java.util.HashMap;
   60   import java.util.List;
   61   import java.io.Serializable;
   62   import java.io.ObjectStreamException;
   63   
   64   public class StatefulInstanceManager {
   65       public static final Logger logger = Logger.getInstance(LogCategory.OPENEJB, "org.apache.openejb.util.resources");
   66   
   67       protected final long timeOut;
   68   
   69       // queue of beans for LRU algorithm
   70       private final BeanEntryQueue lruQueue;
   71   
   72       private final PassivationStrategy passivator;
   73   
   74       private final int bulkPassivationSize;
   75   
   76       private final TransactionManager transactionManager;
   77       private final SecurityService securityService;
   78       private final JtaEntityManagerRegistry jtaEntityManagerRegistry;
   79   
   80       public StatefulInstanceManager(TransactionManager transactionManager, SecurityService securityService, JtaEntityManagerRegistry jtaEntityManagerRegistry, Class passivatorClass, int timeout, int poolSize, int bulkPassivate) throws OpenEJBException {
   81           this.transactionManager = transactionManager;
   82           this.securityService = securityService;
   83           this.jtaEntityManagerRegistry = jtaEntityManagerRegistry;
   84           this.lruQueue = new BeanEntryQueue(poolSize);
   85           if (poolSize == 0) {
   86               this.bulkPassivationSize = 1;
   87           } else {
   88               this.bulkPassivationSize = Math.min(bulkPassivate, poolSize);
   89           }
   90           this.timeOut = timeout * 60 * 1000;
   91   
   92           try {
   93               passivatorClass = (passivatorClass == null) ? SimplePassivater.class : passivatorClass;
   94               passivator = (PassivationStrategy) passivatorClass.newInstance();
   95           } catch (Exception e) {
   96               throw new OpenEJBException("Could not create the passivator " + passivatorClass.getName(), e);
   97           }
   98       }
   99   
  100       public void deploy(CoreDeploymentInfo deploymentInfo, Index<Method, StatefulContainer.MethodType> index) throws OpenEJBException {
  101           deploymentInfo.setContainerData(new Data(index));
  102       }
  103   
  104       public void undeploy(CoreDeploymentInfo deploymentInfo) throws OpenEJBException {
  105           Data data = (Data) deploymentInfo.getContainerData();
  106           if (data != null) {
  107               for (BeanEntry entry: data.getBeanIndex().values()) {
  108                   lruQueue.remove(entry);
  109               }
  110               deploymentInfo.setContainerData(null);
  111           }
  112       }
  113   
  114       Index<Method, StatefulContainer.MethodType> getMethodIndex(CoreDeploymentInfo deploymentInfo) {
  115           Data data = (Data) deploymentInfo.getContainerData();
  116           return data.getMethodIndex();
  117       }
  118   
  119       public Transaction getBeanTransaction(ThreadContext callContext) throws OpenEJBException {
  120           BeanEntry entry = getBeanEntry(callContext);
  121           if (entry == null) return null;
  122           return entry.beanTransaction;
  123       }
  124   
  125       public void setBeanTransaction(ThreadContext callContext, Transaction beanTransaction) throws OpenEJBException {
  126           BeanEntry entry = getBeanEntry(callContext);
  127           entry.beanTransaction = beanTransaction;
  128       }
  129   
  130       public Map<EntityManagerFactory, EntityManager> getEntityManagers(ThreadContext callContext, Index<EntityManagerFactory, Map> factories) throws OpenEJBException {
  131           BeanEntry entry = getBeanEntry(callContext);
  132           return entry.getEntityManagers(factories);
  133       }
  134   
  135       public void setEntityManagers(ThreadContext callContext, Index<EntityManagerFactory, EntityManager> entityManagers) throws OpenEJBException {
  136           BeanEntry entry = getBeanEntry(callContext);
  137           entry.setEntityManagers(entityManagers);
  138       }
  139   
  140       public Object newInstance(Object primaryKey, Class beanClass) throws OpenEJBException {
  141           Object bean = null;
  142   
  143           ThreadContext threadContext = ThreadContext.getThreadContext();
  144           Operation currentOperation = threadContext.getCurrentOperation();
  145           try {
  146               ObjectRecipe objectRecipe = new ObjectRecipe(beanClass);
  147               objectRecipe.allow(Option.FIELD_INJECTION);
  148               objectRecipe.allow(Option.PRIVATE_PROPERTIES);
  149   //            objectRecipe.allow(Option.IGNORE_MISSING_PROPERTIES);
  150   
  151               ThreadContext callContext = ThreadContext.getThreadContext();
  152               CoreDeploymentInfo deploymentInfo = callContext.getDeploymentInfo();
  153               Context ctx = deploymentInfo.getJndiEnc();
  154               SessionContext sessionContext;
  155               synchronized (this) {
  156                   try {
  157                       sessionContext = (SessionContext) ctx.lookup("java:comp/EJBContext");
  158                   } catch (NamingException e1) {
  159                       sessionContext = createSessionContext();
  160                       ctx.bind("java:comp/EJBContext", sessionContext);
  161                   }
  162               }
  163               if (javax.ejb.SessionBean.class.isAssignableFrom(beanClass) || hasSetSessionContext(beanClass)) {
  164                   callContext.setCurrentOperation(Operation.INJECTION);
  165                   objectRecipe.setProperty("sessionContext", new StaticRecipe(sessionContext));
  166               }
  167   
  168               fillInjectionProperties(objectRecipe, beanClass, deploymentInfo, ctx);
  169   
  170               bean = objectRecipe.create(beanClass.getClassLoader());
  171   
  172               Map unsetProperties = objectRecipe.getUnsetProperties();
  173               if (unsetProperties.size() > 0) {
  174                   for (Object property : unsetProperties.keySet()) {
  175                       logger.warning("Injection: No such property '" + property + "' in class " + beanClass.getName());
  176                   }
  177               }
  178               HashMap<String, Object> interceptorInstances = new HashMap<String, Object>();
  179               for (InterceptorData interceptorData : deploymentInfo.getAllInterceptors()) {
  180                   if (interceptorData.getInterceptorClass().equals(beanClass)) continue;
  181   
  182                   Class clazz = interceptorData.getInterceptorClass();
  183                   ObjectRecipe interceptorRecipe = new ObjectRecipe(clazz);
  184                   interceptorRecipe.allow(Option.FIELD_INJECTION);
  185                   interceptorRecipe.allow(Option.PRIVATE_PROPERTIES);
  186                   interceptorRecipe.allow(Option.IGNORE_MISSING_PROPERTIES);
  187   
  188                   fillInjectionProperties(interceptorRecipe, clazz, deploymentInfo, ctx);
  189   
  190                   try {
  191                       Object interceptorInstance = interceptorRecipe.create(clazz.getClassLoader());
  192                       interceptorInstances.put(clazz.getName(), interceptorInstance);
  193                   } catch (ConstructionException e) {
  194                       throw new Exception("Failed to create interceptor: " + clazz.getName(), e);
  195                   }
  196               }
  197   
  198               interceptorInstances.put(beanClass.getName(), bean);
  199   
  200               callContext.setCurrentOperation(Operation.POST_CONSTRUCT);
  201   
  202               List<InterceptorData> callbackInterceptors = deploymentInfo.getCallbackInterceptors();
  203               InterceptorStack interceptorStack = new InterceptorStack(bean, null, Operation.POST_CONSTRUCT, callbackInterceptors, interceptorInstances);
  204               interceptorStack.invoke();
  205   
  206               bean = newInstance(primaryKey, bean, interceptorInstances);
  207   
  208           } catch (Throwable callbackException) {
  209               /*
  210               In the event of an exception, OpenEJB is required to log the exception, evict the instance,
  211               and mark the transaction for rollback.  If there is a transaction to rollback, then the a
  212                 javax.transaction.TransactionRolledbackException must be throw to the client. Otherwise a
  213               java.rmi.RemoteException is thrown to the client.
  214               See EJB 1.1 specification, section 12.3.2
  215               See EJB 2.0 specification, section 18.3.3
  216               */
  217               handleCallbackException(callbackException, bean, threadContext, "setSessionContext");
  218           } finally {
  219               threadContext.setCurrentOperation(currentOperation);
  220           }
  221   
  222           // add to index
  223           BeanEntry entry = newBeanEntry(primaryKey, bean);
  224           getBeanIndex(threadContext).put(primaryKey, entry);
  225   
  226           return bean;
  227       }
  228   
  229       protected Instance newInstance(Object primaryKey, Object bean, Map<String, Object> interceptorInstances) {
  230           return new Instance(bean, interceptorInstances);
  231       }
  232   
  233       protected BeanEntry newBeanEntry(Object primaryKey, Object bean) {
  234           return new BeanEntry(bean, primaryKey, timeOut);
  235       }
  236   
  237       private static void fillInjectionProperties(ObjectRecipe objectRecipe, Class clazz, CoreDeploymentInfo deploymentInfo, Context context) {
  238           for (Injection injection : deploymentInfo.getInjections()) {
  239               if (!injection.getTarget().isAssignableFrom(clazz)) continue;
  240               try {
  241                   String jndiName = injection.getJndiName();
  242                   Object object = context.lookup("java:comp/env/" + jndiName);
  243                   if (object instanceof String) {
  244                       String string = (String) object;
  245                       // Pass it in raw so it could be potentially converted to
  246                       // another data type by an xbean-reflect property editor
  247                       objectRecipe.setProperty(injection.getTarget().getName() + "/" + injection.getName(), string);
  248                   } else {
  249                       objectRecipe.setProperty(injection.getTarget().getName() + "/" + injection.getName(), new StaticRecipe(object));
  250                   }
  251               } catch (NamingException e) {
  252                   logger.warning("Injection data not found in enc: jndiName='" + injection.getJndiName() + "', target=" + injection.getTarget() + "/" + injection.getName());
  253               }
  254           }
  255       }
  256   
  257   
  258       private boolean hasSetSessionContext(Class beanClass) {
  259           try {
  260               beanClass.getMethod("setSessionContext", SessionContext.class);
  261               return true;
  262           } catch (NoSuchMethodException e) {
  263               return false;
  264           }
  265       }
  266   
  267       private SessionContext createSessionContext() {
  268           StatefulUserTransaction userTransaction = new StatefulUserTransaction(new CoreUserTransaction(transactionManager), jtaEntityManagerRegistry);
  269           return new StatefulContext(transactionManager, securityService, userTransaction);
  270       }
  271   
  272       public Object obtainInstance(Object primaryKey, ThreadContext callContext) throws OpenEJBException {
  273           if (primaryKey == null) {
  274               throw new SystemException(new NullPointerException("Cannot obtain an instance of the stateful session bean with a null session id"));
  275           }
  276   
  277           // look for entry in index
  278           BeanEntry entry = getBeanIndex(callContext).get(primaryKey);
  279   
  280           // if we didn't find the bean in the index, try to activate it
  281           if (entry == null) {
  282               Object bean = activateInstance(primaryKey, callContext);
  283               return bean;
  284           }
  285   
  286           // if the bean is already in a transaction, just return it
  287           if (entry.beanTransaction != null) {
  288               return entry.bean;
  289           }
  290   
  291           // remove from the queue so it is not passivated while in use
  292           BeanEntry queueEntry = lruQueue.remove(entry);
  293           if (queueEntry != null) {
  294               // if bean is timed out, destroy it
  295               if (entry.isTimedOut()) {
  296                   entry = getBeanIndex(callContext).remove(entry.primaryKey);
  297                   handleTimeout(entry, callContext);
  298                   throw new InvalidateReferenceException(new NoSuchObjectException("Stateful SessionBean has timed-out"));
  299               }
  300               return entry.bean;
  301           } else {
  302               // if it is not in the queue, the bean is already being invoked
  303               // the only reentrant/concurrent operations allowed are Session synchronization callbacks
  304               Operation currentOperation = callContext.getCurrentOperation();
  305               if (currentOperation != Operation.AFTER_COMPLETION && currentOperation != Operation.BEFORE_COMPLETION) {
  306                   throw new ApplicationException(new RemoteException("Concurrent calls not allowed"));
  307               }
  308   
  309               return entry.bean;
  310           }
  311       }
  312   
  313       private Object activateInstance(Object primaryKey, ThreadContext callContext) throws SystemException, ApplicationException {
  314           // attempt to active a passivated entity
  315           BeanEntry entry = activate(primaryKey);
  316           if (entry == null) {
  317               throw new InvalidateReferenceException(new NoSuchObjectException("Not Found"));
  318           }
  319   
  320           return activateInstance(callContext, entry);
  321       }
  322   
  323       public Object activateInstance(ThreadContext callContext, BeanEntry entry)
  324           throws SystemException, ApplicationException {
  325           if (entry.isTimedOut()) {
  326               // Since the bean instance hasn't had its ejbActivate() method called yet,
  327               // it is still considered to be passivated at this point. Instances that timeout
  328               // while passivated must be evicted WITHOUT having their ejbRemove()
  329               // method invoked. Section 6.6 of EJB 1.1 specification.
  330               throw new InvalidateReferenceException(new NoSuchObjectException("Timed Out"));
  331           }
  332   
  333           // call the activate method
  334           Operation currentOperation = callContext.getCurrentOperation();
  335           callContext.setCurrentOperation(Operation.ACTIVATE);
  336           try {
  337               CoreDeploymentInfo deploymentInfo = callContext.getDeploymentInfo();
  338   
  339               StatefulInstanceManager.Instance instance = (StatefulInstanceManager.Instance) entry.bean;
  340               Method remove = instance.bean instanceof SessionBean? SessionBean.class.getMethod("ejbActivate"): null;
  341   
  342               List<InterceptorData> callbackInterceptors = deploymentInfo.getCallbackInterceptors();
  343               InterceptorStack interceptorStack = new InterceptorStack(instance.bean, remove, Operation.ACTIVATE, callbackInterceptors, instance.interceptors);
  344   
  345               interceptorStack.invoke();
  346           } catch (Throwable callbackException) {
  347               /*
  348               In the event of an exception, OpenEJB is required to log the exception, evict the instance,
  349               and mark the transaction for rollback.  If there is a transaction to rollback, then the a
  350               javax.transaction.TransactionRolledbackException must be throw to the client. Otherwise a
  351               java.rmi.RemoteException is thrown to the client.
  352               See EJB 1.1 specification, section 12.3.2
  353               */
  354               handleCallbackException(callbackException, entry.bean, callContext, "ejbActivate");
  355           } finally {
  356               callContext.setCurrentOperation(currentOperation);
  357           }
  358   
  359           // add it to the index
  360           getBeanIndex(callContext).put(entry.primaryKey, entry);
  361   
  362           return entry.bean;
  363       }
  364   
  365       protected void handleTimeout(BeanEntry entry, ThreadContext threadContext) {
  366           Operation currentOperation = threadContext.getCurrentOperation();
  367           threadContext.setCurrentOperation(Operation.PRE_DESTROY);
  368           BaseContext.State[] originalAllowedStates = threadContext.setCurrentAllowedStates(StatefulContext.getStates());
  369           CoreDeploymentInfo deploymentInfo = threadContext.getDeploymentInfo();
  370           Instance instance = (Instance) entry.bean;
  371   
  372           try {
  373               Method remove = instance.bean instanceof SessionBean? SessionBean.class.getMethod("ejbRemove"): null;
  374   
  375               List<InterceptorData> callbackInterceptors = deploymentInfo.getCallbackInterceptors();
  376               InterceptorStack interceptorStack = new InterceptorStack(instance.bean, remove, Operation.PRE_DESTROY, callbackInterceptors, instance.interceptors);
  377   
  378               interceptorStack.invoke();
  379           } catch (Throwable callbackException) {
  380               /*
  381                 Exceptions are processed "quietly"; they are not reported to the client since
  382                 the timeout that caused the ejbRemove() operation did not, "technically", take
  383                 place in the context of a client call. Logically, it may have timeout sometime
  384                 before the client call.
  385               */
  386               String logMessage = "An unexpected exception occured while invoking the ejbRemove method on the timed-out Stateful SessionBean instance; " + callbackException.getClass().getName() + " " + callbackException.getMessage();
  387   
  388               /* [1] Log the exception or error */
  389               logger.error(logMessage);
  390   
  391           } finally {
  392               logger.info("Removing the timed-out stateful session bean instance " + entry.primaryKey);
  393               threadContext.setCurrentOperation(currentOperation);
  394               threadContext.setCurrentAllowedStates(originalAllowedStates);
  395           }
  396       }
  397   
  398       public void poolInstance(ThreadContext callContext, Object bean) throws OpenEJBException {
  399           // Don't pool if the bean has been undeployed
  400           if (callContext.getDeploymentInfo().isDestroyed()) return;
  401   
  402           Object primaryKey = callContext.getPrimaryKey();
  403           if (primaryKey == null || bean == null) {
  404               throw new SystemException("Invalid arguments");
  405           }
  406   
  407           BeanEntry entry = getBeanIndex(callContext).get(primaryKey);
  408           if (entry == null) {
  409               entry = activate(primaryKey);
  410               if (entry == null) {
  411                   throw new SystemException("Invalid primaryKey:" + primaryKey);
  412               }
  413           } else if (entry.bean != bean) {
  414               throw new SystemException("Invalid ID for bean");
  415           }
  416   
  417           if (entry.beanTransaction == null) {
  418               if (callContext.getCurrentOperation() != Operation.CREATE){
  419                   try {
  420                       entry.beanTransaction = transactionManager.getTransaction();
  421                   } catch (javax.transaction.SystemException se) {
  422                       throw new SystemException("TransactionManager failure", se);
  423                   }
  424               }
  425   
  426               // only put in LRU if no current transaction
  427               if (entry.beanTransaction == null) {
  428                   // add it to end of Queue; the most reciently used bean
  429                   lruQueue.add(entry);
  430   
  431                   onPoolInstanceWithoutTransaction(callContext, entry);
  432               }
  433           }
  434       }
  435   
  436       protected void onPoolInstanceWithoutTransaction(ThreadContext callContext, BeanEntry entry) {
  437       }
  438   
  439       public Object freeInstance(ThreadContext threadContext) throws SystemException {
  440           Object primaryKey = threadContext.getPrimaryKey();
  441           BeanEntry entry = getBeanIndex(threadContext).remove(primaryKey);// remove frm index
  442           if (entry == null) {
  443               entry = activate(primaryKey);
  444           } else {
  445               lruQueue.remove(entry);
  446           }
  447   
  448           if (entry == null) {
  449               return null;
  450           }
  451   
  452           onFreeBeanEntry(threadContext, entry);
  453   
  454           return entry.bean;
  455       }
  456   
  457       protected void onFreeBeanEntry(ThreadContext threadContext, BeanEntry entry) {
  458       }
  459   
  460       protected void passivate() throws SystemException {
  461           final ThreadContext threadContext = ThreadContext.getThreadContext();
  462           Hashtable<Object, BeanEntry> stateTable = new Hashtable<Object, BeanEntry>(bulkPassivationSize);
  463   
  464           BeanEntry currentEntry;
  465           final Operation currentOperation = threadContext.getCurrentOperation();
  466           final BaseContext.State[] originalAllowedStates = threadContext.setCurrentAllowedStates(StatefulContext.getStates());
  467           CoreDeploymentInfo deploymentInfo = threadContext.getDeploymentInfo();
  468           try {
  469               for (int i = 0; i < bulkPassivationSize; ++i) {
  470                   currentEntry = lruQueue.first();
  471                   if (currentEntry == null) {
  472                       break;
  473                   }
  474                   getBeanIndex(threadContext).remove(currentEntry.primaryKey);
  475                   if (currentEntry.isTimedOut()) {
  476                       handleTimeout(currentEntry, threadContext);
  477                   } else {
  478                       passivate(threadContext, currentEntry);
  479                       stateTable.put(currentEntry.primaryKey, currentEntry);
  480                   }
  481               }
  482           } finally {
  483               threadContext.setCurrentOperation(currentOperation);
  484               threadContext.setCurrentAllowedStates(originalAllowedStates);
  485           }
  486   
  487           /*
  488              the IntraVmCopyMonitor.prePssivationOperation() demarcates 
  489              the begining of passivation; used by EjbHomeProxyHandler, 
  490              EjbObjectProxyHandler, IntraVmMetaData, and IntraVmHandle 
  491              to deterime how serialization for these artifacts.
  492           */
  493           try {
  494               IntraVmCopyMonitor.prePassivationOperation();
  495   
  496               passivator.passivate(stateTable);
  497           } finally {
  498   
  499               IntraVmCopyMonitor.postPassivationOperation();
  500           }
  501       }
  502   
  503       public void passivate(ThreadContext threadContext, BeanEntry currentEntry) {
  504           CoreDeploymentInfo deploymentInfo = threadContext.getDeploymentInfo();
  505           threadContext.setCurrentOperation(Operation.PASSIVATE);
  506           try {
  507               StatefulInstanceManager.Instance instance = (StatefulInstanceManager.Instance) currentEntry.bean;
  508   
  509               Method passivate = instance.bean instanceof SessionBean? SessionBean.class.getMethod("ejbPassivate"): null;
  510   
  511               List<InterceptorData> callbackInterceptors = deploymentInfo.getCallbackInterceptors();
  512               InterceptorStack interceptorStack = new InterceptorStack(instance.bean, passivate, Operation.PASSIVATE, callbackInterceptors, instance.interceptors);
  513   
  514               interceptorStack.invoke();
  515   
  516           } catch (Throwable e) {
  517   
  518               String logMessage = "An unexpected exception occured while invoking the ejbPassivate method on the Stateful SessionBean instance; " + e.getClass().getName() + " " + e.getMessage();
  519   
  520               /* [1] Log the exception or error */
  521               logger.error(logMessage);
  522           }
  523       }
  524   
  525       protected BeanEntry activate(Object primaryKey) throws SystemException {
  526           return (BeanEntry) passivator.activate(primaryKey);
  527       }
  528   
  529       protected InvalidateReferenceException destroy(ThreadContext callContext, BeanEntry entry, Exception t) throws SystemException {
  530   
  531           getBeanIndex(callContext).remove(entry.primaryKey);// remove frm index
  532           lruQueue.remove(entry);// remove from queue
  533           if (entry.beanTransaction != null) {
  534               try {
  535                   entry.beanTransaction.setRollbackOnly();
  536               } catch (javax.transaction.SystemException se) {
  537                   throw new SystemException(se);
  538               } catch (IllegalStateException ise) {
  539                   throw new SystemException("Attempt to rollback a non-tx context", ise);
  540               } catch (SecurityException lse) {
  541                   throw new SystemException("Container not authorized to rollback tx", lse);
  542               }
  543               return new InvalidateReferenceException(new TransactionRolledbackException(t));
  544           } else if (t instanceof RemoteException) {
  545               return new InvalidateReferenceException(t);
  546           } else {
  547               EJBException e = (EJBException) t;
  548               return new InvalidateReferenceException(new RemoteException(e.getMessage(), e.getCausedByException()));
  549           }
  550   
  551       }
  552   
  553       protected BeanEntry getBeanEntry(ThreadContext callContext) throws OpenEJBException {
  554           Object primaryKey = callContext.getPrimaryKey();
  555           if (primaryKey == null) {
  556               throw new SystemException(new NullPointerException("The primary key is null. Cannot get the bean entry"));
  557           }
  558           BeanEntry entry = getBeanIndex(callContext).get(primaryKey);
  559           if (entry == null) {
  560               Object bean = this.obtainInstance(primaryKey, ThreadContext.getThreadContext());
  561               this.poolInstance(callContext, bean);
  562               entry = getBeanIndex(callContext).get(primaryKey);
  563           }
  564           return entry;
  565       }
  566   
  567       protected Hashtable<Object, BeanEntry> getBeanIndex(ThreadContext threadContext) {
  568           CoreDeploymentInfo deployment = threadContext.getDeploymentInfo();
  569           Data data = (Data) deployment.getContainerData();
  570           return data.getBeanIndex();
  571       }
  572   
  573       private static class Data {
  574           private final Index<Method, StatefulContainer.MethodType> methodIndex;
  575           private final Hashtable<Object, BeanEntry> beanIndex = new Hashtable<Object, BeanEntry>();
  576   
  577           private Data(Index<Method, StatefulContainer.MethodType> methodIndex) {
  578               this.methodIndex = methodIndex;
  579           }
  580   
  581           public Index<Method, StatefulContainer.MethodType> getMethodIndex() {
  582               return methodIndex;
  583           }
  584   
  585           public Hashtable<Object, BeanEntry> getBeanIndex() {
  586               return beanIndex;
  587           }
  588       }
  589   
  590       class BeanEntryQueue {
  591           private final LinkedList<BeanEntry> list;
  592           private final int capacity;
  593   
  594           protected BeanEntryQueue(int preferedCapacity) {
  595               capacity = preferedCapacity;
  596               list = new LinkedList<BeanEntry>();
  597           }
  598   
  599           protected synchronized BeanEntry first() {
  600               return list.removeFirst();
  601           }
  602   
  603           protected synchronized void add(BeanEntry entry) throws SystemException {
  604               entry.resetTimeOut();
  605   
  606               list.addLast(entry);
  607               entry.inQueue = true;
  608   
  609               if (list.size() >= capacity) {// is the LRU QUE full?
  610                   passivate();
  611               }
  612           }
  613   
  614           protected synchronized BeanEntry remove(BeanEntry entry) {
  615               if (!entry.inQueue) {
  616                   return null;
  617               }
  618               if (list.remove(entry)) {
  619                   entry.inQueue = false;
  620                   return entry;
  621               } else {
  622   
  623                   return null;
  624               }
  625           }
  626       }
  627   
  628   
  629       protected void handleCallbackException(Throwable e, Object instance, ThreadContext callContext, String callBack) throws ApplicationException, SystemException {
  630   
  631           String remoteMessage = "An unexpected exception occured while invoking the " + callBack + " method on the Stateful SessionBean instance";
  632           String logMessage = remoteMessage + "; " + e.getClass().getName() + " " + e.getMessage();
  633   
  634           /* [1] Log the exception or error */
  635           logger.error(logMessage);
  636   
  637           /* [2] If the instance is in a transaction, mark the transaction for rollback. */
  638           Transaction transaction = null;
  639           try {
  640               transaction = transactionManager.getTransaction();
  641           } catch (Throwable t) {
  642               logger.error("Could not retreive the current transaction from the transaction manager while handling a callback exception from the " + callBack + " method of bean " + callContext.getPrimaryKey());
  643           }
  644           if (transaction != null) {
  645               markTxRollbackOnly(transaction);
  646           }
  647   
  648           /* [3] Discard the instance */
  649           freeInstance(callContext);
  650   
  651           /* [4] throw the java.rmi.RemoteException to the client */
  652           if (transaction == null) {
  653               throw new InvalidateReferenceException(new RemoteException(remoteMessage, e));
  654           } else {
  655               throw new InvalidateReferenceException(new TransactionRolledbackException(remoteMessage, e));
  656           }
  657   
  658       }
  659   
  660       protected void markTxRollbackOnly(Transaction tx) throws SystemException {
  661           try {
  662               if (tx != null) {
  663                   tx.setRollbackOnly();
  664               }
  665           } catch (javax.transaction.SystemException se) {
  666               throw new SystemException(se);
  667           }
  668       }
  669   
  670       public static class Instance implements Serializable {
  671           public final Object bean;
  672           public final Map<String,Object> interceptors;
  673   
  674           public Instance(Object bean, Map<String, Object> interceptors) {
  675               this.bean = bean;
  676               this.interceptors = interceptors;
  677           }
  678   
  679           protected Object writeReplace() throws ObjectStreamException {
  680               return new Serialization(this);
  681           }
  682   
  683           private static class Serialization implements Serializable {
  684               public final Object bean;
  685               public final Map<String,Object> interceptors;
  686   
  687               public Serialization(Instance i) {
  688                   if (i.bean instanceof Serializable){
  689                       bean = i.bean;
  690                   } else {
  691                       bean = new PojoSerialization(i.bean);
  692                   }
  693   
  694                   interceptors = new HashMap(i.interceptors.size());
  695                   for (Map.Entry<String, Object> e : i.interceptors.entrySet()) {
  696                       if (e.getValue() == i.bean){
  697                           // need to use the same wrapped reference or well get two copies.
  698                           interceptors.put(e.getKey(), bean);
  699                       } else if (!(e.getValue() instanceof Serializable)){
  700                           interceptors.put(e.getKey(), new PojoSerialization(e.getValue()));
  701                       }
  702                   }
  703               }
  704   
  705               protected Object readResolve() throws ObjectStreamException {
  706                   // Anything wrapped with PojoSerialization will have been automatically
  707                   // unwrapped via it's own readResolve so passing in the raw bean
  708                   // and interceptors variables is totally fine.
  709                   return new Instance(bean, interceptors);
  710               }
  711           }
  712       }
  713   }
  714   

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