User: fleury
Date: 00/09/26 11:41:15
Modified: src/main/org/jboss/ejb/plugins
EntityInstanceInterceptor.java
Log:
The new interceptor deals with locking instances
Revision Changes Path
1.8 +261 -150
jboss/src/main/org/jboss/ejb/plugins/EntityInstanceInterceptor.java
Index: EntityInstanceInterceptor.java
===================================================================
RCS file:
/products/cvs/ejboss/jboss/src/main/org/jboss/ejb/plugins/EntityInstanceInterceptor.java,v
retrieving revision 1.7
retrieving revision 1.8
diff -u -r1.7 -r1.8
--- EntityInstanceInterceptor.java 2000/08/25 01:58:13 1.7
+++ EntityInstanceInterceptor.java 2000/09/26 18:41:15 1.8
@@ -1,150 +1,261 @@
-/*
-* jBoss, the OpenSource EJB server
-*
-* Distributable under GPL license.
-* See terms of license at gnu.org.
-*/
-package org.jboss.ejb.plugins;
-
-import java.lang.reflect.Method;
-import java.rmi.RemoteException;
-import java.util.Map;
-import java.util.HashMap;
-import java.util.ArrayList;
-
-import javax.ejb.EJBObject;
-import javax.ejb.CreateException;
-import javax.ejb.EJBException;
-import javax.ejb.NoSuchEntityException;
-import javax.ejb.RemoveException;
-import javax.ejb.EntityBean;
-import javax.transaction.Status;
-import javax.transaction.Synchronization;
-import javax.transaction.Transaction;
-import javax.transaction.TransactionManager;
-import javax.transaction.RollbackException;
-import javax.transaction.SystemException;
-
-import org.jboss.ejb.Container;
-import org.jboss.ejb.EntityContainer;
-import org.jboss.ejb.EntityPersistenceManager;
-import org.jboss.ejb.EntityEnterpriseContext;
-import org.jboss.ejb.EnterpriseContext;
-import org.jboss.ejb.InstanceCache;
-import org.jboss.ejb.InstancePool;
-import org.jboss.ejb.MethodInvocation;
-import org.jboss.ejb.CacheKey;
-
-/**
-* This container acquires the given instance.
-*
-* @see <related>
-* @author Rickard �berg ([EMAIL PROTECTED])
-* @author <a href="[EMAIL PROTECTED]">Marc Fleury</a>
-* @version $Revision: 1.7 $
-*/
-public class EntityInstanceInterceptor
-extends AbstractInterceptor
-{
- // Constants -----------------------------------------------------
-
- // Attributes ----------------------------------------------------
- protected EntityContainer container;
-
- // Static --------------------------------------------------------
-
- // Constructors --------------------------------------------------
-
- // Public --------------------------------------------------------
- public void setContainer(Container container)
- {
- this.container = (EntityContainer)container;
- }
-
- public Container getContainer()
- {
- return container;
- }
-
- // Interceptor implementation --------------------------------------
- public Object invokeHome(MethodInvocation mi)
- throws Exception
- {
- // Get context
-
mi.setEnterpriseContext(((EntityContainer)getContainer()).getInstancePool().get());
-
- try
- {
- // Invoke through interceptors
- return getNext().invokeHome(mi);
- } finally
- {
- // Still free? Not free if create() was called successfully
- if (mi.getEnterpriseContext().getId() == null)
- {
-
container.getInstancePool().free(mi.getEnterpriseContext());
- } else
- {
- // Logger.log("Entity was created; not
returned to pool");
-
((EntityContainer)getContainer()).getInstanceCache().release(mi.getEnterpriseContext());
- }
- }
- }
-
- public Object invoke(MethodInvocation mi)
- throws Exception
- {
- // The id store is a CacheKey in the case of Entity
- CacheKey key = (CacheKey) mi.getId();
-
- // Get context
- // The cache will properly managed the tx-ctx locking, in case the mi
transaction is different.
-
mi.setEnterpriseContext(((EntityContainer)getContainer()).getInstanceCache().get(key));
- try
- {
- // Invoke through interceptors
- return getNext().invoke(mi);
- } catch (RemoteException e)
- {
- // Discard instance
- // EJB 1.1 spec 12.3.1
-
((EntityContainer)getContainer()).getInstanceCache().remove(key.id);
-
- throw e;
- } catch (RuntimeException e)
- {
- // Discard instance
- // EJB 1.1 spec 12.3.1
-
((EntityContainer)getContainer()).getInstanceCache().remove(key.id);
-
- throw e;
- } catch (Error e)
- {
- // Discard instance
- // EJB 1.1 spec 12.3.1
-
((EntityContainer)getContainer()).getInstanceCache().remove(key.id);
-
- throw e;
- } finally
- {
- // Logger.log("Release instance for "+id);
- EnterpriseContext ctx = mi.getEnterpriseContext();
- if (ctx != null)
- {
- if (ctx.getId() == null)
- {
- // Remove from cache
-
((EntityContainer)getContainer()).getInstanceCache().remove(key.id);
-
- // It has been removed -> send to free pool
- container.getInstancePool().free(ctx);
- }
- {
- // Return context
-
((EntityContainer)getContainer()).getInstanceCache().release(ctx);
- }
- }
- }
- }
-}
-
+/*
+* jBoss, the OpenSource EJB server
+*
+* Distributable under GPL license.
+* See terms of license at gnu.org.
+*/
+package org.jboss.ejb.plugins;
+
+import java.lang.reflect.Method;
+import java.rmi.RemoteException;
+import java.util.Map;
+import java.util.HashMap;
+import java.util.ArrayList;
+
+import javax.ejb.EJBObject;
+import javax.ejb.CreateException;
+import javax.ejb.EJBException;
+import javax.ejb.NoSuchEntityException;
+import javax.ejb.RemoveException;
+import javax.ejb.EntityBean;
+import javax.transaction.Status;
+import javax.transaction.Synchronization;
+import javax.transaction.Transaction;
+import javax.transaction.TransactionManager;
+import javax.transaction.RollbackException;
+import javax.transaction.SystemException;
+
+import org.jboss.ejb.Container;
+import org.jboss.ejb.EntityContainer;
+import org.jboss.ejb.EntityPersistenceManager;
+import org.jboss.ejb.EntityEnterpriseContext;
+import org.jboss.ejb.EnterpriseContext;
+import org.jboss.ejb.InstanceCache;
+import org.jboss.ejb.InstancePool;
+import org.jboss.ejb.MethodInvocation;
+import org.jboss.ejb.CacheKey;
+import org.jboss.metadata.EntityMetaData;
+
+/**
+* This container acquires the given instance.
+*
+* @see <related>
+* @author Rickard �berg ([EMAIL PROTECTED])
+* @author <a href="[EMAIL PROTECTED]">Marc Fleury</a>
+* @version $Revision: 1.8 $
+*/
+public class EntityInstanceInterceptor
+extends AbstractInterceptor
+{
+ // Constants -----------------------------------------------------
+
+ // Attributes ----------------------------------------------------
+ protected EntityContainer container;
+
+ // Static --------------------------------------------------------
+
+ // Constructors --------------------------------------------------
+
+ // Public --------------------------------------------------------
+ public void setContainer(Container container)
+ {
+ this.container = (EntityContainer)container;
+ }
+
+ public Container getContainer()
+ {
+ return container;
+ }
+
+ // Interceptor implementation --------------------------------------
+ public Object invokeHome(MethodInvocation mi)
+ throws Exception
+ {
+ // Get context
+ EnterpriseContext ctx =
((EntityContainer)getContainer()).getInstancePool().get();
+ mi.setEnterpriseContext(ctx);
+
+ // It is a new context for sure so we can lock it (no need for sync
(not in cache))
+ ctx.lock();
+
+ try
+ {
+ // Invoke through interceptors
+ return getNext().invokeHome(mi);
+ } finally
+ {
+ // Still free? Not free if create() was called successfully
+ if (mi.getEnterpriseContext().getId() == null)
+ {
+ // Free that context
+ ctx.unlock();
+
+
container.getInstancePool().free(mi.getEnterpriseContext());
+ }
+ else
+ {
+ // DEBUG Logger.log("Entity was created; not
returned to pool");
+ synchronized (ctx) {
+
+ // Release the lock
+ ctx.unlock();
+
+ //Let the waiters know
+ ctx.notifyAll();
+ }
+ }
+ }
+ }
+
+ public Object invoke(MethodInvocation mi)
+ throws Exception
+ {
+ // The id store is a CacheKey in the case of Entity
+ CacheKey key = (CacheKey) mi.getId();
+
+ // Get context
+ EnterpriseContext ctx =
((EntityContainer)getContainer()).getInstanceCache().get(key);
+
+ // Set it on the method invocation
+ mi.setEnterpriseContext(ctx);
+
+ // We synchronize the locking logic (so that the invoke is
unsynchronized and can be reentrant)
+ synchronized (ctx)
+ {
+ // Do we have a running transaction with the context
+ if (ctx.getTransaction() != null &&
+ // And are we trying to enter with another transaction
+ !ctx.getTransaction().equals(mi.getTransaction()))
+ {
+ // Let's put the thread to sleep a lock release will
wake the thread
+ try{ctx.wait();}
+ catch (InterruptedException ie) {}
+
+ // Try your luck again
+ return invoke(mi);
+ }
+
+ if (!ctx.isLocked()){
+
+ //take it!
+ ctx.lock();
+ }
+
+ else
+ {
+ if (!isCallAllowed(mi)) {
+
+ // Let's put the thread to sleep a lock
release will wake the thread
+ try{ctx.wait();}
+ catch (InterruptedException ie) {}
+
+ // Try your luck again
+ return invoke(mi);
+ }
+
+ // The call is allowed, do increment the lock though
(ctx already locked)
+ ctx.lock();
+ }
+
+ }
+
+ try {
+
+ // Go on, you won
+ return getNext().invoke(mi);
+
+ }
+ catch (RemoteException e)
+ {
+ // Discard instance
+ // EJB 1.1 spec 12.3.1
+
((EntityContainer)getContainer()).getInstanceCache().remove(key.id);
+
+ throw e;
+ } catch (RuntimeException e)
+ {
+ // Discard instance
+ // EJB 1.1 spec 12.3.1
+
((EntityContainer)getContainer()).getInstanceCache().remove(key.id);
+
+ throw e;
+ } catch (Error e)
+ {
+ // Discard instance
+ // EJB 1.1 spec 12.3.1
+
((EntityContainer)getContainer()).getInstanceCache().remove(key.id);
+
+ throw e;
+ } finally
+ {
+ // Logger.log("Release instance for "+id);
+ if (ctx != null)
+ {
+
+ synchronized (ctx) {
+
+ // unlock the context
+ ctx.unlock();
+
+ if (ctx.getId() == null)
+ {
+ // Remove from cache
+
((EntityContainer)getContainer()).getInstanceCache().remove(key.id);
+
+ // It has been removed -> send to free
pool
+ container.getInstancePool().free(ctx);
+ }
+
+ // notify the thread waiting on ctx
+ ctx.notifyAll();
+ }
+ }
+ }
+ }
+
+ // Private --------------------------------------------------------
+
+ private static Method getEJBHome;
+ private static Method getHandle;
+ private static Method getPrimaryKey;
+ private static Method isIdentical;
+ private static Method remove;
+ static
+ {
+ try
+ {
+ Class[] noArg = new Class[0];
+ getEJBHome = EJBObject.class.getMethod("getEJBHome", noArg);
+ getHandle = EJBObject.class.getMethod("getHandle", noArg);
+ getPrimaryKey = EJBObject.class.getMethod("getPrimaryKey",
noArg);
+ isIdentical = EJBObject.class.getMethod("isIdentical", new
Class[] {EJBObject.class});
+ remove = EJBObject.class.getMethod("remove", noArg);
+ }
+ catch (Exception x) {x.printStackTrace();}
+ }
+
+ private boolean isCallAllowed(MethodInvocation mi)
+ {
+ boolean reentrant =
((EntityMetaData)container.getBeanMetaData()).isReentrant();
+
+ if (reentrant)
+ {
+ return true;
+ }
+ else
+ {
+ Method m = mi.getMethod();
+ if (m.equals(getEJBHome) ||
+ m.equals(getHandle) ||
+ m.equals(getPrimaryKey) ||
+ m.equals(isIdentical) ||
+ m.equals(remove))
+ {
+ return true;
+ }
+ }
+
+ return false;
+ }
+}
+