You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@tomee.apache.org by da...@apache.org on 2007/04/10 04:01:56 UTC

svn commit: r526994 - in /incubator/openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/core: cmp/CmpContainer.java entity/EntityContainer.java entity/EntityInstanceManager.java entity/EntrancyTracker.java

Author: dain
Date: Mon Apr  9 19:01:55 2007
New Revision: 526994

URL: http://svn.apache.org/viewvc?view=rev&rev=526994
Log:
Implement reentrancy checks

Added:
    incubator/openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/core/entity/EntrancyTracker.java
Modified:
    incubator/openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/core/cmp/CmpContainer.java
    incubator/openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/core/entity/EntityContainer.java
    incubator/openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/core/entity/EntityInstanceManager.java

Modified: incubator/openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/core/cmp/CmpContainer.java
URL: http://svn.apache.org/viewvc/incubator/openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/core/cmp/CmpContainer.java?view=diff&rev=526994&r1=526993&r2=526994
==============================================================================
--- incubator/openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/core/cmp/CmpContainer.java (original)
+++ incubator/openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/core/cmp/CmpContainer.java Mon Apr  9 19:01:55 2007
@@ -42,6 +42,7 @@
 import javax.ejb.FinderException;
 import javax.ejb.EJBAccessException;
 import javax.transaction.TransactionManager;
+import javax.transaction.TransactionSynchronizationRegistry;
 
 import org.apache.openejb.ApplicationException;
 import org.apache.openejb.DeploymentInfo;
@@ -49,12 +50,14 @@
 import org.apache.openejb.ProxyInfo;
 import org.apache.openejb.RpcContainer;
 import org.apache.openejb.ContainerType;
+import org.apache.openejb.loader.SystemInstance;
 import org.apache.openejb.core.CoreDeploymentInfo;
 import org.apache.openejb.core.Operation;
 import org.apache.openejb.core.ThreadContext;
 import org.apache.openejb.core.timer.EjbTimerService;
 import org.apache.openejb.core.timer.EjbTimerServiceImpl;
 import org.apache.openejb.core.entity.EntityContext;
+import org.apache.openejb.core.entity.EntrancyTracker;
 import org.apache.openejb.core.transaction.TransactionContainer;
 import org.apache.openejb.core.transaction.TransactionContext;
 import org.apache.openejb.core.transaction.TransactionPolicy;
@@ -93,6 +96,10 @@
      */
     protected final Map<Object, CmpEngine> cmpEngines = new HashMap<Object, CmpEngine>();
 
+    /**
+     * Tracks entity instances that have been "entered" so we can throw reentrancy exceptions.
+     */
+    protected EntrancyTracker entrancyTracker;
 
     public CmpContainer(Object id, TransactionManager transactionManager, SecurityService securityService, String cmpEngineFactory, String engine, String connectorName) {
         this.transactionManager = transactionManager;
@@ -101,6 +108,7 @@
         this.cmpEngineFactory = cmpEngineFactory;
         this.connectorName = connectorName;
         this.engine = engine;
+        entrancyTracker = new EntrancyTracker(SystemInstance.get().getComponent(TransactionSynchronizationRegistry.class));
 
         cmpCallback = new ContainerCmpCallback();
     }
@@ -469,6 +477,8 @@
 
         EntityBean bean = null;
         Object returnValue = null;
+
+        entrancyTracker.enter(deploymentInfo, callContext.getPrimaryKey());
         try {
             CmpEngine cmpEngine = getCmpEngine(deploymentInfo);
             bean = (EntityBean) cmpEngine.loadBean(callContext, callContext.getPrimaryKey());
@@ -494,6 +504,7 @@
             /* System Exception ****************************/
             txPolicy.handleSystemException(e, bean, txContext);
         } finally {
+            entrancyTracker.exit(deploymentInfo, callContext.getPrimaryKey());
             txPolicy.afterInvoke(bean, txContext);
         }
 

Modified: incubator/openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/core/entity/EntityContainer.java
URL: http://svn.apache.org/viewvc/incubator/openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/core/entity/EntityContainer.java?view=diff&rev=526994&r1=526993&r2=526994
==============================================================================
--- incubator/openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/core/entity/EntityContainer.java (original)
+++ incubator/openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/core/entity/EntityContainer.java Mon Apr  9 19:01:55 2007
@@ -23,6 +23,7 @@
 import java.util.HashMap;
 import java.util.Iterator;
 import java.util.Map;
+import java.util.Vector;
 
 import javax.ejb.EJBAccessException;
 import javax.ejb.EJBHome;
@@ -34,6 +35,7 @@
 import javax.ejb.Timer;
 import javax.transaction.Transaction;
 import javax.transaction.TransactionManager;
+import javax.transaction.TransactionSynchronizationRegistry;
 
 import org.apache.openejb.ApplicationException;
 import org.apache.openejb.ContainerType;
@@ -41,6 +43,7 @@
 import org.apache.openejb.OpenEJBException;
 import org.apache.openejb.ProxyInfo;
 import org.apache.openejb.SystemException;
+import org.apache.openejb.loader.SystemInstance;
 import org.apache.openejb.core.BaseContext;
 import org.apache.openejb.core.CoreDeploymentInfo;
 import org.apache.openejb.core.Operation;
@@ -68,10 +71,16 @@
     private TransactionManager transactionManager;
     private SecurityService securityService;
 
+    /**
+     * Tracks entity instances that have been "entered" so we can throw reentrancy exceptions.
+     */
+    protected EntrancyTracker entrancyTracker;
+
     public EntityContainer(Object id, TransactionManager transactionManager, SecurityService securityService, int poolSize) throws OpenEJBException {
         this.containerID = id;
         this.transactionManager = transactionManager;
         this.securityService = securityService;
+        entrancyTracker = new EntrancyTracker(SystemInstance.get().getComponent(TransactionSynchronizationRegistry.class));
 
         instanceManager = new EntityInstanceManager(this, transactionManager, securityService, poolSize);
     }
@@ -199,9 +208,8 @@
         txPolicy.beforeInvoke(bean, txContext);
 
         Object returnValue = null;
-
+        entrancyTracker.enter(callContext.getDeploymentInfo(), callContext.getPrimaryKey());
         try {
-
             bean = instanceManager.obtainInstance(callContext);
 
             ejbLoad_If_No_Transaction(callContext, bean);
@@ -233,6 +241,7 @@
             */
             txPolicy.handleSystemException(iae, bean, txContext);
         } finally {
+            entrancyTracker.exit(callContext.getDeploymentInfo(), callContext.getPrimaryKey());
             txPolicy.afterInvoke(bean, txContext);
         }
 
@@ -426,7 +435,7 @@
         */
         if (returnValue instanceof java.util.Collection) {
             Iterator keys = ((Collection) returnValue).iterator();
-            java.util.Vector proxies = new java.util.Vector();
+            Vector<ProxyInfo> proxies = new Vector<ProxyInfo>();
             while (keys.hasNext()) {
                 Object primaryKey = keys.next();
                 proxies.addElement(new ProxyInfo(deploymentInfo, primaryKey, objectInterface, this));
@@ -434,7 +443,7 @@
             returnValue = proxies;
         } else if (returnValue instanceof java.util.Enumeration) {
             Enumeration keys = (Enumeration) returnValue;
-            java.util.Vector proxies = new java.util.Vector();
+            Vector<ProxyInfo> proxies = new Vector<ProxyInfo>();
             while (keys.hasMoreElements()) {
                 Object primaryKey = keys.nextElement();
                 proxies.addElement(new ProxyInfo(deploymentInfo, primaryKey, objectInterface, this));
@@ -520,5 +529,4 @@
             }
         }
     }
-
 }

Modified: incubator/openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/core/entity/EntityInstanceManager.java
URL: http://svn.apache.org/viewvc/incubator/openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/core/entity/EntityInstanceManager.java?view=diff&rev=526994&r1=526993&r2=526994
==============================================================================
--- incubator/openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/core/entity/EntityInstanceManager.java (original)
+++ incubator/openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/core/entity/EntityInstanceManager.java Mon Apr  9 19:01:55 2007
@@ -18,6 +18,9 @@
 
 import org.apache.openejb.ApplicationException;
 import org.apache.openejb.OpenEJBException;
+import org.apache.openejb.SystemException;
+import org.apache.openejb.InvalidateReferenceException;
+import org.apache.openejb.DeploymentInfo;
 import org.apache.openejb.spi.SecurityService;
 import org.apache.openejb.core.BaseContext;
 import org.apache.openejb.core.CoreDeploymentInfo;
@@ -35,9 +38,11 @@
 import javax.transaction.Transaction;
 import javax.transaction.TransactionManager;
 import javax.transaction.Synchronization;
-import javax.transaction.SystemException;
+import javax.transaction.RollbackException;
 import java.util.HashMap;
 import java.util.Hashtable;
+import java.util.Map;
+import java.rmi.RemoteException;
 
 public class EntityInstanceManager {
 
@@ -53,12 +58,12 @@
     * by using an instance of the inner Key class. The Key class is a compound key composed
     * of the tx, deployment, and primary key identifiers.
     */
-    protected Hashtable txReadyPool = new Hashtable();
+    protected Hashtable<Object,SyncronizationWrapper> txReadyPool = new Hashtable<Object,SyncronizationWrapper>();
     /*
     * contains a collection of LinkListStacks indexed by deployment id. Each indexed stack
     * represents the method ready pool of for that class.
     */
-    protected HashMap poolMap = null;
+    protected Map<Object,LinkedListStack> poolMap = null;
 
     public Logger logger = Logger.getInstance("OpenEJB", "org.apache.openejb.util.resources");
 
@@ -71,37 +76,37 @@
         this.securityService = securityService;
         this.poolsize = poolSize;
         this.container = container;
-        poolMap = new HashMap();// put size in later
+        poolMap = new HashMap<Object,LinkedListStack>();// put size in later
 
-        org.apache.openejb.DeploymentInfo[] deploymentInfos = this.container.deployments();
+        DeploymentInfo[] deploymentInfos = this.container.deployments();
         for (int i = 0; i < deploymentInfos.length; i++) {
-            org.apache.openejb.DeploymentInfo deploymentInfo = deploymentInfos[i];
+            DeploymentInfo deploymentInfo = deploymentInfos[i];
             deploy(deploymentInfo);
         }
     }
 
-    public void deploy(org.apache.openejb.DeploymentInfo deploymentInfo) {
+    public void deploy(DeploymentInfo deploymentInfo) {
         poolMap.put(deploymentInfo.getDeploymentID(), new LinkedListStack(poolsize / 2));
     }
 
-    public void undeploy(org.apache.openejb.DeploymentInfo deploymentInfo) {
+    public void undeploy(DeploymentInfo deploymentInfo) {
         poolMap.remove(deploymentInfo.getDeploymentID());
     }
 
-    public EntityBean obtainInstance(ThreadContext callContext)
-            throws OpenEJBException {
-        Transaction currentTx = null;
+    public EntityBean obtainInstance(ThreadContext callContext) throws OpenEJBException {
+        Transaction currentTx;
         try {
             currentTx = getTransactionManager().getTransaction();
         } catch (javax.transaction.SystemException se) {
             logger.error("Transaction Manager getTransaction() failed.", se);
-            throw new org.apache.openejb.SystemException("TransactionManager failure");
+            throw new SystemException("TransactionManager failure");
         }
 
         Object primaryKey = callContext.getPrimaryKey();// null if its a servicing a home methods (create, find, ejbHome)
         if (currentTx != null && primaryKey != null) {// primkey is null if create operation is called
-            Key key = new Key(currentTx, callContext.getDeploymentInfo().getDeploymentID(), primaryKey);
-            SyncronizationWrapper wrapper = (SyncronizationWrapper) txReadyPool.get(key);
+            CoreDeploymentInfo deploymentInfo = callContext.getDeploymentInfo();
+            Key key = new Key(currentTx, deploymentInfo.getDeploymentID(), primaryKey);
+            SyncronizationWrapper wrapper = txReadyPool.get(key);
 
             if (wrapper != null) {// if true, the requested bean instance is already enrolled in a transaction
 
@@ -115,7 +120,7 @@
                     * its likely that the application server would have already made the reference invalid, but this bit of
                     * code is an extra precaution.
                     */
-                    throw new org.apache.openejb.InvalidateReferenceException(new NoSuchObjectException("Entity not found: " + primaryKey));
+                    throw new InvalidateReferenceException(new NoSuchObjectException("Entity not found: " + primaryKey));
                 } else if (callContext.getCurrentOperation() == Operation.REMOVE) {
                     /*
                     *  To avoid calling ejbStore( ) on a bean that after its removed, we can not delegate
@@ -129,22 +134,16 @@
                     return wrapper.getEntityBean();
                 } else {
 
-                    org.apache.openejb.core.CoreDeploymentInfo depInfo = (org.apache.openejb.core.CoreDeploymentInfo) callContext.getDeploymentInfo();
-                    if (depInfo.isReentrant()) {
-                        /*
-                         * If the bean is declared as reentrant then the instance may be accessed
-                         * by more then one thread at a time.  This is one of the reasons that reentrancy
-                         * is bad. In this case beans must be programmed to be multi threaded. The other reason
-                         * reentrancy is bad has to do with transaction isolation. Multiple instances writing to
-                         * the same database records will inevitably cancel out previous writes within the same tx.
-                         *
-                         * In the future we may change this to return a new instance of the bean and to
-                         * link it and its wrapper to the original wrapper, but for now we choose this strategy because
-                         * its simpler to implement.
-                        */
-                        return wrapper.getEntityBean();
-                    } else
-                        throw new org.apache.openejb.ApplicationException(new java.rmi.RemoteException("Attempted reentrant access. Bean is not reentrant"));
+                    // If the bean is declared as reentrant then the instance may be accessed
+                    // by more then one thread at a time.  This is one of the reasons that reentrancy
+                    // is bad. In this case beans must be programmed to be multi threaded. The other reason
+                    // reentrancy is bad has to do with transaction isolation. Multiple instances writing to
+                    // the same database records will inevitably cancel out previous writes within the same tx.
+                    //
+                    // In the future we may change this to return a new instance of the bean and to
+                    // link it and its wrapper to the original wrapper, but for now we choose this strategy because
+                    // its simpler to implement.
+                    return wrapper.getEntityBean();
                 }
             } else {
                 /*
@@ -169,24 +168,23 @@
 
                 try {
                     currentTx.registerSynchronization(wrapper);
-                } catch (javax.transaction.SystemException se) {
-                    logger.error("Transaction Manager registerSynchronization() failed.", se);
-                    throw new org.apache.openejb.SystemException(se);
-                } catch (javax.transaction.RollbackException re) {
-                    throw new org.apache.openejb.ApplicationException(new TransactionRolledbackException(re));
+                } catch (javax.transaction.SystemException e) {
+                    logger.error("Transaction Manager registerSynchronization() failed.", e);
+                    throw new SystemException(e);
+                } catch (RollbackException e) {
+                    throw new ApplicationException(new TransactionRolledbackException(e));
                 }
                 loadingBean(bean, callContext);
                 Operation orginalOperation = callContext.getCurrentOperation();
-                callContext.setCurrentOperation(org.apache.openejb.core.Operation.LOAD);
-                BaseContext.State[] originalStates = callContext.setCurrentAllowedStates(EntityContext.getStates());
+                callContext.setCurrentOperation(Operation.LOAD);
                 try {
                     bean.ejbLoad();
                 } catch (NoSuchEntityException e) {
-                    throw new org.apache.openejb.InvalidateReferenceException(new NoSuchObjectException("Entity not found: " + primaryKey).initCause(e));
+                    throw new InvalidateReferenceException(new NoSuchObjectException("Entity not found: " + primaryKey).initCause(e));
                 } catch (Exception e) {
                     logger.error("Exception encountered during ejbLoad():", e);
 
-                    throw new org.apache.openejb.OpenEJBException(e);
+                    throw new OpenEJBException(e);
                 } finally {
                     callContext.setCurrentOperation(orginalOperation);
                     callContext.setCurrentAllowedStates(EntityContext.getStates());
@@ -195,27 +193,25 @@
 
                 return bean;
             }
-        } else {  /*
-                If no transaction is associated with the thread or if its a create, find or home method (primaryKey == null), then no synchronized wrapper is needed.
-                if bean instance is used for a create method then a syncrhonziation wrapper may be assigned
-                when the bean is returned to the pool -- depending on if the tx is a client initiated or container initiated.
-                */
+        } else {
+            // If no transaction is associated with the thread or if its a create, find or home method
+            // (primaryKey == null), then no synchronized wrapper is needed. if bean instance is used
+            // for a create method then a syncrhonziation wrapper may be assigned when the bean is
+            // returned to the pool -- depending on if the tx is a client initiated or container initiated.
             return getPooledInstance(callContext);
         }
     }
 
-    protected void loadingBean(javax.ejb.EntityBean bean, org.apache.openejb.core.ThreadContext callContext) throws OpenEJBException {
+    protected void loadingBean(EntityBean bean, ThreadContext callContext) throws OpenEJBException {
     }
 
-    protected void reusingBean(javax.ejb.EntityBean bean, org.apache.openejb.core.ThreadContext callContext) throws OpenEJBException {
+    protected void reusingBean(EntityBean bean, ThreadContext callContext) throws OpenEJBException {
     }
 
-    protected EntityBean getPooledInstance(ThreadContext callContext)
-            throws org.apache.openejb.OpenEJBException {
+    protected EntityBean getPooledInstance(ThreadContext callContext) throws OpenEJBException {
         CoreDeploymentInfo deploymentInfo = callContext.getDeploymentInfo();
-        Stack methodReadyPool = (Stack) poolMap.get(deploymentInfo.getDeploymentID());
-        if (methodReadyPool == null)
-            throw new org.apache.openejb.SystemException("Invalid deployment id " + deploymentInfo.getDeploymentID() + " for this container");
+        Stack methodReadyPool = poolMap.get(deploymentInfo.getDeploymentID());
+        if (methodReadyPool == null) throw new SystemException("Invalid deployment id " + deploymentInfo.getDeploymentID() + " for this container");
 
         EntityBean bean = (EntityBean) methodReadyPool.pop();
         if (bean == null) {
@@ -223,11 +219,11 @@
                 bean = (EntityBean) deploymentInfo.getBeanClass().newInstance();
             } catch (Exception e) {
                 logger.error("Bean instantiation failed for class " + deploymentInfo.getBeanClass(), e);
-                throw new org.apache.openejb.SystemException(e);
+                throw new SystemException(e);
             }
 
             Operation currentOp = callContext.getCurrentOperation();
-            callContext.setCurrentOperation(org.apache.openejb.core.Operation.SET_CONTEXT);
+            callContext.setCurrentOperation(Operation.SET_CONTEXT);
             BaseContext.State[] originalStates = callContext.setCurrentAllowedStates(EntityContext.getStates());
 
             try {
@@ -241,16 +237,15 @@
                 * we don't want the TransactionScopeHandler commiting the transaction in afterInvoke() which is what it would attempt
                 * to do.
                 */
-                CoreDeploymentInfo deploymentInfo1 = callContext.getDeploymentInfo();
                 bean.setEntityContext(createEntityContext());
-            } catch (java.lang.Exception e) {
+            } catch (Exception e) {
                 /*
                 * The EJB 1.1 specification does not specify how exceptions thrown by setEntityContext impact the
                 * transaction, if there is one.  In this case we choose the least disruptive operation, throwing an
                 * application exception and NOT automatically marking the transaciton for rollback.
                 */
                 logger.error("Bean callback method failed ", e);
-                throw new org.apache.openejb.ApplicationException(e);
+                throw new ApplicationException(e);
             } finally {
                 callContext.setCurrentOperation(currentOp);
                 callContext.setCurrentAllowedStates(originalStates);
@@ -259,8 +254,7 @@
             reusingBean(bean, callContext);
         }
 
-        if ((callContext.getCurrentOperation() == org.apache.openejb.core.Operation.BUSINESS) ||
-                (callContext.getCurrentOperation() == org.apache.openejb.core.Operation.REMOVE)) {
+        if ((callContext.getCurrentOperation() == Operation.BUSINESS) || (callContext.getCurrentOperation() == Operation.REMOVE)) {
             /*
             * When a bean is retrieved from the bean pool to service a client's business method request it must be
             * notified that its about to enter service by invoking its ejbActivate( ) method. A bean instance
@@ -272,7 +266,7 @@
             */
             Operation currentOp = callContext.getCurrentOperation();
 
-            callContext.setCurrentOperation(org.apache.openejb.core.Operation.ACTIVATE);
+            callContext.setCurrentOperation(Operation.ACTIVATE);
             BaseContext.State[] originalStates = callContext.setCurrentAllowedStates(EntityContext.getStates());
             try {
                 /*
@@ -292,9 +286,9 @@
                     }
                 } catch (javax.transaction.SystemException se) {
                     logger.error("Transaction Manager getTransaction() failed.", se);
-                    throw new org.apache.openejb.SystemException(se);
+                    throw new SystemException(se);
                 }
-                throw new ApplicationException(new java.rmi.RemoteException("Exception thrown while attempting to call ejbActivate() on the instance. Exception message = " + e.getMessage()));
+                throw new ApplicationException(new RemoteException("Exception thrown while attempting to call ejbActivate() on the instance. Exception message = " + e.getMessage()));
             } finally {
                 callContext.setCurrentOperation(currentOp);
                 callContext.setCurrentAllowedStates(originalStates);
@@ -308,10 +302,8 @@
         return new EntityContext(transactionManager, securityService);
     }
 
-    public void poolInstance(ThreadContext callContext, EntityBean bean)
-            throws OpenEJBException {
+    public void poolInstance(ThreadContext callContext, EntityBean bean) throws OpenEJBException {
         if (bean == null) {
-
             return;
         }
         Object primaryKey = callContext.getPrimaryKey();// null if servicing a home ejbFind or ejbHome method.
@@ -320,11 +312,11 @@
             currentTx = getTransactionManager().getTransaction();
         } catch (javax.transaction.SystemException se) {
             logger.error("Transaction Manager getTransaction() failed.", se);
-            throw new org.apache.openejb.SystemException("TransactionManager failure");
+            throw new SystemException("TransactionManager failure");
         }
         if (currentTx != null && primaryKey != null) {// primary key is null for find and home methods
             Key key = new Key(currentTx, callContext.getDeploymentInfo().getDeploymentID(), primaryKey);
-            SyncronizationWrapper wrapper = (SyncronizationWrapper) txReadyPool.get(key);
+            SyncronizationWrapper wrapper = txReadyPool.get(key);
             if (wrapper != null) {
                 if (callContext.getCurrentOperation() == Operation.REMOVE) {
                     /*
@@ -338,7 +330,7 @@
                     * If the bean has been removed then the bean instance is no longer needed and can return to the methodReadyPool
                     * to service another identity.
                     */
-                    Stack methodReadyPool = (Stack) poolMap.get(callContext.getDeploymentInfo().getDeploymentID());
+                    Stack methodReadyPool = poolMap.get(callContext.getDeploymentInfo().getDeploymentID());
                     methodReadyPool.push(bean);
                 } else
                     wrapper.setEntityBean(bean);
@@ -356,9 +348,9 @@
                     currentTx.registerSynchronization(wrapper);
                 } catch (javax.transaction.SystemException se) {
                     logger.error("Transaction Manager registerSynchronization() failed.", se);
-                    throw new org.apache.openejb.SystemException(se);
-                } catch (javax.transaction.RollbackException re) {
-                    throw new org.apache.openejb.ApplicationException(new TransactionRolledbackException(re));
+                    throw new SystemException(se);
+                } catch (RollbackException re) {
+                    throw new ApplicationException(new TransactionRolledbackException(re));
                 }
 
                 txReadyPool.put(key, wrapper);
@@ -378,7 +370,7 @@
                 */
                 Operation currentOp = callContext.getCurrentOperation();
 
-                callContext.setCurrentOperation(org.apache.openejb.core.Operation.PASSIVATE);
+                callContext.setCurrentOperation(Operation.PASSIVATE);
                 BaseContext.State[] originalStates = callContext.setCurrentAllowedStates(EntityContext.getStates());
 
                 try {
@@ -398,9 +390,9 @@
                         }
                     } catch (javax.transaction.SystemException se) {
                         logger.error("Transaction Manager getTransaction() failed.", se);
-                        throw new org.apache.openejb.SystemException(se);
+                        throw new SystemException(se);
                     }
-                    throw new ApplicationException(new java.rmi.RemoteException("Reflection exception thrown while attempting to call ejbPassivate() on the instance. Exception message = " + e.getMessage()));
+                    throw new ApplicationException(new RemoteException("Reflection exception thrown while attempting to call ejbPassivate() on the instance. Exception message = " + e.getMessage()));
                 } finally {
                     callContext.setCurrentOperation(currentOp);
                     callContext.setCurrentAllowedStates(originalStates);
@@ -412,19 +404,18 @@
             * method and is not still part of a tx.  While in the method ready pool the bean instance is not associated with a
             * primary key and may be used to service a request for any bean of the same class.
             */
-            Stack methodReadyPool = (Stack) poolMap.get(callContext.getDeploymentInfo().getDeploymentID());
+            Stack methodReadyPool = poolMap.get(callContext.getDeploymentInfo().getDeploymentID());
             methodReadyPool.push(bean);
         }
 
     }
 
-    public void freeInstance(ThreadContext callContext, EntityBean bean)
-            throws org.apache.openejb.SystemException {
+    public void freeInstance(ThreadContext callContext, EntityBean bean) throws SystemException {
 
         discardInstance(callContext, bean);
 
         Operation currentOp = callContext.getCurrentOperation();
-        callContext.setCurrentOperation(org.apache.openejb.core.Operation.UNSET_CONTEXT);
+        callContext.setCurrentOperation(Operation.UNSET_CONTEXT);
         BaseContext.State[] originalStates = callContext.setCurrentAllowedStates(EntityContext.getStates());
 
         try {
@@ -439,7 +430,7 @@
             * to do.
             */
             bean.unsetEntityContext();
-        } catch (java.lang.Exception e) {
+        } catch (Exception e) {
             /*
             * The EJB 1.1 specification does not specify how exceptions thrown by unsetEntityContext impact the
             * transaction, if there is one.  In this case we choose to do nothing since the instance is being disposed
@@ -454,14 +445,13 @@
 
     }
 
-    public void discardInstance(ThreadContext callContext, EntityBean bean)
-            throws org.apache.openejb.SystemException {
+    public void discardInstance(ThreadContext callContext, EntityBean bean) throws SystemException {
         Transaction currentTx = null;
         try {
             currentTx = getTransactionManager().getTransaction();
         } catch (javax.transaction.SystemException se) {
             logger.error("Transaction Manager getTransaction() failed.", se);
-            throw new org.apache.openejb.SystemException("TransactionManager failure");
+            throw new SystemException("TransactionManager failure");
         }
         if (currentTx != null) {
             if (callContext.getPrimaryKey() == null)
@@ -475,7 +465,7 @@
                in the txReadyPool is indicative of an entity bean that has been removed via
                ejbRemove() rather than freed because of an error condition as is the case here.
             */
-            SyncronizationWrapper wrapper = (SyncronizationWrapper) txReadyPool.remove(key);
+            SyncronizationWrapper wrapper = txReadyPool.remove(key);
 
             if (wrapper != null) {
                 /*
@@ -554,7 +544,6 @@
         private final Key myIndex;
         private final CoreDeploymentInfo deploymentInfo;
         private final Object primaryKey;
-        private final Object securityIdentity;
 
         public SyncronizationWrapper(EntityBean bean, Key key, boolean available, ThreadContext callContext) throws OpenEJBException {
             if (bean == null || callContext == null || key == null) {
@@ -566,7 +555,6 @@
             isAssociated = true;
             deploymentInfo = callContext.getDeploymentInfo();
             primaryKey = callContext.getPrimaryKey();
-            securityIdentity = null;
         }
 
         public void disassociate() {
@@ -611,7 +599,7 @@
                     TransactionManager transactionManager = getTransactionManager();
                     try {
                         transactionManager.setRollbackOnly();
-                    } catch (SystemException se) {
+                    } catch (javax.transaction.SystemException se) {
                         logger.error("Transaction manager reported error during setRollbackOnly()", se);
                     }
 

Added: incubator/openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/core/entity/EntrancyTracker.java
URL: http://svn.apache.org/viewvc/incubator/openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/core/entity/EntrancyTracker.java?view=auto&rev=526994
==============================================================================
--- incubator/openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/core/entity/EntrancyTracker.java (added)
+++ incubator/openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/core/entity/EntrancyTracker.java Mon Apr  9 19:01:55 2007
@@ -0,0 +1,126 @@
+/**
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package org.apache.openejb.core.entity;
+
+import org.apache.openejb.DeploymentInfo;
+import org.apache.openejb.ApplicationException;
+
+import javax.transaction.TransactionSynchronizationRegistry;
+import java.util.Set;
+import java.util.HashSet;
+import java.rmi.RemoteException;
+
+public class EntrancyTracker {
+    /**
+     * Thread local used to track the insances in the current call stack so we can determine if an nonreentrant
+     * instance is being reentered.
+     */
+    private final ThreadLocal<Set<InstanceKey>> inCallThreadLocal = new ThreadLocal<Set<InstanceKey>>() {
+        protected Set<InstanceKey> initialValue() {
+            return new HashSet<InstanceKey>();
+        }
+    };
+
+    private final TransactionSynchronizationRegistry synchronizationRegistry;
+
+    public EntrancyTracker(TransactionSynchronizationRegistry synchronizationRegistry) {
+        this.synchronizationRegistry = synchronizationRegistry;
+    }
+
+    public void enter(DeploymentInfo deploymentInfo, Object primaryKey) throws ApplicationException {
+        if (primaryKey == null || deploymentInfo.isReentrant()) {
+            return;
+        }
+
+        Object deploymentId = deploymentInfo.getDeploymentID();
+        InstanceKey key = new InstanceKey(deploymentId, primaryKey);
+
+
+        Set<InstanceKey> inCall;
+        try {
+            //noinspection unchecked
+            inCall = (Set<InstanceKey>) synchronizationRegistry.getResource(EntrancyTracker.class);
+            if (inCall == null) {
+                inCall = new HashSet<InstanceKey>();
+                synchronizationRegistry.putResource(EntrancyTracker.class, inCall);
+            }
+        } catch (IllegalStateException e) {
+            inCall = inCallThreadLocal.get();
+        }
+
+        if (!inCall.add(key)) {
+            ApplicationException exception = new ApplicationException(new RemoteException("Attempted reentrant access. " + "Bean " + deploymentId + " is not reentrant and instance " + primaryKey + " has already been entered : " +inCall));
+            exception.printStackTrace();
+            throw exception;
+        }
+
+    }
+
+    public void exit(DeploymentInfo deploymentInfo, Object primaryKey) throws ApplicationException {
+        if (primaryKey == null || deploymentInfo.isReentrant()) {
+            return;
+        }
+
+        Object deploymentId = deploymentInfo.getDeploymentID();
+        InstanceKey key = new InstanceKey(deploymentId, primaryKey);
+
+        Set<InstanceKey> inCall = null;
+        try {
+            //noinspection unchecked
+            inCall = (Set<InstanceKey>) synchronizationRegistry.getResource(EntrancyTracker.class);
+        } catch (IllegalStateException e) {
+            inCall = inCallThreadLocal.get();
+        }
+
+        if (inCall != null) {
+            inCall.remove(key);
+        }
+    }
+
+    private static class InstanceKey {
+        private final Object deploymentId;
+        private final Object primaryKey;
+
+
+        public InstanceKey(Object deploymentId, Object primaryKey) {
+            this.deploymentId = deploymentId;
+            this.primaryKey = primaryKey;
+        }
+
+        public boolean equals(Object o) {
+            if (this == o) return true;
+            if (o == null || getClass() != o.getClass()) return false;
+
+            InstanceKey that = (InstanceKey) o;
+
+            return deploymentId.equals(that.deploymentId) && primaryKey.equals(that.primaryKey);
+        }
+
+        public int hashCode() {
+            int result;
+            result = deploymentId.hashCode();
+            result = 31 * result + primaryKey.hashCode();
+            return result;
+        }
+
+
+        public String toString() {
+            return deploymentId + ":" + primaryKey;
+        }
+    }
+}