[jboss-cvs] JBossAS SVN: r112502 - in projects/ejb3/branches/jboss-ejb3-core-1.3.7_JBPAPP-7602/src: test/java/org/jboss/ejb3/core/test and 2 other directories.

jboss-cvs-commits at lists.jboss.org jboss-cvs-commits at lists.jboss.org
Sun Dec 4 23:54:07 EST 2011


Author: jameslivingston
Date: 2011-12-04 23:54:07 -0500 (Sun, 04 Dec 2011)
New Revision: 112502

Added:
   projects/ejb3/branches/jboss-ejb3-core-1.3.7_JBPAPP-7602/src/test/java/org/jboss/ejb3/core/test/jbpapp7523/
   projects/ejb3/branches/jboss-ejb3-core-1.3.7_JBPAPP-7602/src/test/java/org/jboss/ejb3/core/test/jbpapp7523/SimpleSFSB.java
   projects/ejb3/branches/jboss-ejb3-core-1.3.7_JBPAPP-7602/src/test/java/org/jboss/ejb3/core/test/jbpapp7523/unit/
   projects/ejb3/branches/jboss-ejb3-core-1.3.7_JBPAPP-7602/src/test/java/org/jboss/ejb3/core/test/jbpapp7523/unit/SimpleCachePassivationDeadlockTestCase.java
Modified:
   projects/ejb3/branches/jboss-ejb3-core-1.3.7_JBPAPP-7602/src/main/java/org/jboss/ejb3/cache/simple/SimpleStatefulCache.java
Log:
apply backported patch for JBPAPP7523

Modified: projects/ejb3/branches/jboss-ejb3-core-1.3.7_JBPAPP-7602/src/main/java/org/jboss/ejb3/cache/simple/SimpleStatefulCache.java
===================================================================
--- projects/ejb3/branches/jboss-ejb3-core-1.3.7_JBPAPP-7602/src/main/java/org/jboss/ejb3/cache/simple/SimpleStatefulCache.java	2011-12-05 03:54:53 UTC (rev 112501)
+++ projects/ejb3/branches/jboss-ejb3-core-1.3.7_JBPAPP-7602/src/main/java/org/jboss/ejb3/cache/simple/SimpleStatefulCache.java	2011-12-05 04:54:07 UTC (rev 112502)
@@ -21,14 +21,17 @@
  */
 package org.jboss.ejb3.cache.simple;
 
+import java.util.HashMap;
 import java.util.Iterator;
 import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Map;
+import java.util.Map.Entry;
 import java.util.Queue;
-import java.util.Map.Entry;
+import java.util.concurrent.Callable;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.FutureTask;
 import java.util.concurrent.LinkedBlockingQueue;
-
 import javax.ejb.EJBException;
 import javax.ejb.NoSuchEJBException;
 
@@ -68,6 +71,8 @@
    
    private Queue<StatefulBeanContext> passivationQueue = new LinkedBlockingQueue<StatefulBeanContext>();
 
+   private Map<Object, FutureTask<StatefulBeanContext>> activations = new HashMap<Object, FutureTask<StatefulBeanContext>>();
+
    protected class CacheMap extends LinkedHashMap<Object, StatefulBeanContext>
    {
       private static final long serialVersionUID = 4514182777643616159L;
@@ -430,7 +435,7 @@
       return get(key, true);
    }
    
-   public StatefulBeanContext get(Object key, boolean markInUse) throws EJBException
+   public StatefulBeanContext get(final Object key, boolean markInUse) throws EJBException
    {
       StatefulBeanContext entry = null;
       synchronized (cacheMap)
@@ -465,34 +470,69 @@
       }
       if (entry == null)
       {
+         // JBPAPP-7523: create a task for activation while holding the a lock,
+         // then execute said task outside the lock
+         FutureTask<StatefulBeanContext> activation;
+         final boolean executeActivation;
          synchronized(pm)
          {
-            synchronized (cacheMap)
+            activation = activations.get(key);
+            if (activation == null)
             {
-               entry = cacheMap.get(key);
-            }
-            if(entry == null)
-            {
-               entry = pm.activateSession(key);
-               if (entry == null)
+               activation = new FutureTask<StatefulBeanContext>(new Callable<StatefulBeanContext>()
                {
-                  throw new NoSuchEJBException("Could not find stateful bean: " + key);
-               }
-               --passivatedCount;
-               
-               // We cache the entry even if we will throw an exception below
-               // as we may still need it for its children and XPC references
-               if (log.isTraceEnabled())
-               {
-                  log.trace("Caching activated context " + entry.getId() + " of type " + entry.getClass());
-               }
-               
-               synchronized (cacheMap)
-               {
-                  cacheMap.put(key, entry);
-               }
+                  @Override
+                  public StatefulBeanContext call() throws Exception {
+                      StatefulBeanContext entry;
+                      synchronized (cacheMap)
+                      {
+                         entry = cacheMap.get(key);
+                      }
+                      if(entry == null)
+                      {
+                         entry = pm.activateSession(key);
+                         if (entry == null)
+                         {
+                            throw new NoSuchEJBException("Could not find stateful bean: " + key);
+                         }
+                         --passivatedCount;
+
+                         // We cache the entry even if we will throw an exception below
+                         // as we may still need it for its children and XPC references
+                         if (log.isTraceEnabled())
+                         {
+                            log.trace("Caching activated context " + entry.getId() + " of type " + entry.getClass());
+                         }
+
+                         synchronized (cacheMap)
+                         {
+                            cacheMap.put(key, entry);
+                         }
+                      }
+                      return entry;
+                  }
+               });
+               activations.put(key, activation);
+               executeActivation = true;
             }
+            else
+               executeActivation = false;
          }
+         if (executeActivation)
+            activation.run();
+         try {
+            entry = activation.get();
+         }
+         catch (InterruptedException e)
+         {
+            throw new EJBException(e);
+         }
+         catch (ExecutionException e)
+         {
+            if (e.getCause() instanceof RuntimeException)
+               throw (RuntimeException) e.getCause();
+            throw (EJBException) new EJBException().initCause(e.getCause());
+         }
       }
       
       // Now we know entry isn't null

Added: projects/ejb3/branches/jboss-ejb3-core-1.3.7_JBPAPP-7602/src/test/java/org/jboss/ejb3/core/test/jbpapp7523/SimpleSFSB.java
===================================================================
--- projects/ejb3/branches/jboss-ejb3-core-1.3.7_JBPAPP-7602/src/test/java/org/jboss/ejb3/core/test/jbpapp7523/SimpleSFSB.java	                        (rev 0)
+++ projects/ejb3/branches/jboss-ejb3-core-1.3.7_JBPAPP-7602/src/test/java/org/jboss/ejb3/core/test/jbpapp7523/SimpleSFSB.java	2011-12-05 04:54:07 UTC (rev 112502)
@@ -0,0 +1,13 @@
+package org.jboss.ejb3.core.test.jbpapp7523;
+
+import javax.ejb.Stateful;
+
+import org.jboss.ejb3.annotation.CacheConfig;
+
+/**
+ * @author <a href="mailto:cdewolf at redhat.com">Carlo de Wolf</a>
+ */
+ at Stateful
+ at CacheConfig(maxSize = 1) // passivate on second creation
+public class SimpleSFSB {
+}

Added: projects/ejb3/branches/jboss-ejb3-core-1.3.7_JBPAPP-7602/src/test/java/org/jboss/ejb3/core/test/jbpapp7523/unit/SimpleCachePassivationDeadlockTestCase.java
===================================================================
--- projects/ejb3/branches/jboss-ejb3-core-1.3.7_JBPAPP-7602/src/test/java/org/jboss/ejb3/core/test/jbpapp7523/unit/SimpleCachePassivationDeadlockTestCase.java	                        (rev 0)
+++ projects/ejb3/branches/jboss-ejb3-core-1.3.7_JBPAPP-7602/src/test/java/org/jboss/ejb3/core/test/jbpapp7523/unit/SimpleCachePassivationDeadlockTestCase.java	2011-12-05 04:54:07 UTC (rev 112502)
@@ -0,0 +1,149 @@
+package org.jboss.ejb3.core.test.jbpapp7523.unit;
+
+import java.util.concurrent.BrokenBarrierException;
+import java.util.concurrent.Callable;
+import java.util.concurrent.CyclicBarrier;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+
+import org.jboss.ejb3.cache.simple.SimpleStatefulCache;
+import org.jboss.ejb3.core.test.common.AbstractEJB3TestCase;
+import org.jboss.ejb3.core.test.jbpapp7523.SimpleSFSB;
+import org.jboss.ejb3.session.SessionContainer;
+import org.jboss.ejb3.stateful.StatefulBeanContext;
+import org.junit.After;
+import org.junit.AfterClass;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+import static java.util.concurrent.TimeUnit.SECONDS;
+import static org.junit.Assert.fail;
+
+/**
+ * @author <a href="mailto:cdewolf at redhat.com">Carlo de Wolf</a>
+ */
+public class SimpleCachePassivationDeadlockTestCase extends AbstractEJB3TestCase {
+    private static SessionContainer container;
+
+    private static SimpleStatefulCache cache;
+
+    private static final CyclicBarrier barrier = new CyclicBarrier(2);
+
+    private ExecutorService service;
+
+    @After
+    public void after() {
+        service.shutdown();
+    }
+
+    @AfterClass
+    public static void afterClass() throws Exception {
+        cache.stop();
+
+        undeployEjb(container);
+
+        AbstractEJB3TestCase.afterClass();
+    }
+
+    private static int await(final CyclicBarrier barrier, final long timeout, final TimeUnit unit) {
+        try {
+            return barrier.await(timeout, unit);
+        } catch (InterruptedException e) {
+            throw new RuntimeException(e);
+        } catch (BrokenBarrierException e) {
+            throw new RuntimeException(e);
+        } catch (TimeoutException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    @Before
+    public void before() {
+        service = Executors.newFixedThreadPool(2);
+    }
+
+    @BeforeClass
+    public static void beforeClass() throws Exception {
+       AbstractEJB3TestCase.beforeClass();
+
+       // Deploy the test SLSB
+       container = deploySessionEjb(SimpleSFSB.class);
+
+       cache = new SimpleStatefulCache() {
+           protected void passivate(final StatefulBeanContext ctx) {
+               if (!barrier.isBroken())
+                   await(barrier, 10, SECONDS);
+               //await(barrier, 10, SECONDS);
+               super.passivate(ctx);
+           }
+       };
+       cache.initialize(container);
+       cache.start();
+    }
+
+    @Test
+    public void testSmallCacheCreates() throws Exception {
+        // Fill the cache.
+        final StatefulBeanContext bean1 = cache.create();
+        cache.release(bean1);
+        final Future<StatefulBeanContext> futureBean2 = service.submit(new Callable<StatefulBeanContext>() {
+            @Override
+            public StatefulBeanContext call() throws Exception {
+                final StatefulBeanContext ctx = cache.create();
+                cache.release(ctx);
+                return ctx;
+            }
+        });
+        // Force bean1 in the first cacheMap sync.
+        final Future<StatefulBeanContext> futureBean1 = service.submit(new Callable<StatefulBeanContext>() {
+            @Override
+            public StatefulBeanContext call() throws Exception {
+                return cache.get(bean1.getId());
+            }
+        });
+        // Setup another create run to lock the cacheMap after bean1 proceeds.
+        final Future<StatefulBeanContext> futureBean3 = service.submit(new Callable<StatefulBeanContext>() {
+            @Override
+            public StatefulBeanContext call() throws Exception {
+                final StatefulBeanContext ctx = cache.create();
+                cache.release(ctx);
+                return ctx;
+            }
+        });
+        // Go bean 2! This will make either bean1 or bean3 move forward as well.
+        barrier.await(10, SECONDS);
+        // Either bean1 should be at SimpleStatefulCachemap:492 (or bean3 moved).
+        // Since there is nothing in SimpleStatefulCache.get where we can control movement, we can only hope.
+        /*
+        final Future<StatefulBeanContext> futureBean4 = service.submit(new Callable<StatefulBeanContext>() {
+            @Override
+            public StatefulBeanContext call() throws Exception {
+                final StatefulBeanContext ctx = cache.create();
+                cache.release(ctx);
+                return ctx;
+            }
+        });
+        */
+        // Go bean 3!
+        barrier.await(10, SECONDS);
+        // Break the barrier (bean3 needs to be passivated to make room for bean1).
+        try {
+            barrier.await(0, SECONDS);
+        } catch (TimeoutException e) {
+            // good
+        }
+        // Now we should have a response within 10 seconds.
+        try {
+            futureBean1.get(10, SECONDS);
+        } catch (TimeoutException e) {
+            fail("JBPAPP-7523: cache failed to respond in time");
+        }
+        futureBean2.get(10, SECONDS);
+        futureBean3.get(10, SECONDS);
+        //futureBean4.get(10, SECONDS);
+    }
+}



More information about the jboss-cvs-commits mailing list