[jboss-cvs] JBossAS SVN: r95454 - in branches/Branch_5_x: testsuite/src/main/org/jboss/test/cluster/testutil and 2 other directories.

jboss-cvs-commits at lists.jboss.org jboss-cvs-commits at lists.jboss.org
Thu Oct 22 19:04:06 EDT 2009


Author: bstansberry at jboss.com
Date: 2009-10-22 19:04:05 -0400 (Thu, 22 Oct 2009)
New Revision: 95454

Added:
   branches/Branch_5_x/testsuite/src/main/org/jboss/test/cluster/defaultcfg/simpleweb/test/ClusteredSessionUnitTestCase.java
   branches/Branch_5_x/testsuite/src/main/org/jboss/test/cluster/defaultcfg/simpleweb/test/ConcurrentFailoverRequestsTestCase.java
   branches/Branch_5_x/testsuite/src/main/org/jboss/test/cluster/testutil/JBossCacheUtil.java
   branches/Branch_5_x/testsuite/src/main/org/jboss/test/cluster/web/mocks/ConcurrentRequestHandler.java
Modified:
   branches/Branch_5_x/tomcat/src/main/org/jboss/web/tomcat/service/session/ClusteredSession.java
   branches/Branch_5_x/tomcat/src/main/org/jboss/web/tomcat/service/session/JBossCacheManager.java
Log:
[JBAS-7379] Handle concurrent requests trying to gravitate a session

Added: branches/Branch_5_x/testsuite/src/main/org/jboss/test/cluster/defaultcfg/simpleweb/test/ClusteredSessionUnitTestCase.java
===================================================================
--- branches/Branch_5_x/testsuite/src/main/org/jboss/test/cluster/defaultcfg/simpleweb/test/ClusteredSessionUnitTestCase.java	                        (rev 0)
+++ branches/Branch_5_x/testsuite/src/main/org/jboss/test/cluster/defaultcfg/simpleweb/test/ClusteredSessionUnitTestCase.java	2009-10-22 23:04:05 UTC (rev 95454)
@@ -0,0 +1,88 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2009 Red Hat Middleware, Inc. and individual contributors
+ * as indicated by the @author tags. See the copyright.txt file in the
+ * distribution for a full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+
+package org.jboss.test.cluster.defaultcfg.simpleweb.test;
+
+import junit.framework.TestCase;
+
+import org.jboss.metadata.web.jboss.ReplicationGranularity;
+import org.jboss.test.cluster.testutil.JGroupsSystemPropertySupport;
+import org.jboss.test.cluster.testutil.SessionTestUtil;
+import org.jboss.test.cluster.web.mocks.MockDistributedCacheManagerFactory;
+import org.jboss.web.tomcat.service.session.ClusteredSession;
+import org.jboss.web.tomcat.service.session.JBossCacheManager;
+
+/**
+ * Unit tests of {@link ClusteredSession}.
+ *
+ * @author Brian Stansberry
+ * 
+ * @version $Revision: $
+ */
+public class ClusteredSessionUnitTestCase extends TestCase
+{
+   /**
+    * Create a new ClusteredSessionUnitTestCase.
+    * 
+    * @param name
+    */
+   public ClusteredSessionUnitTestCase(String name)
+   {
+      super(name);
+   }
+   
+   /**
+    * Validates the behavior of isOutdated() with respect to returning
+    * true until a creation time is set.
+    * <p>
+    * Note: the use of creation time is a convenience; it's just a field that
+    * isn't set at construction but rather after the session is either loaded
+    * from the distributed cache or is added as a brand new session.
+    * 
+    * @throws Exception
+    */
+   public void testNewSessionIsOutdated() throws Exception
+   {
+      JBossCacheManager mgr = new JBossCacheManager(new MockDistributedCacheManagerFactory());
+      SessionTestUtil.setupContainer("test", null, mgr);
+      mgr.start();
+      
+      mgr.setReplicationGranularity(ReplicationGranularity.SESSION);
+      ClusteredSession sess = (ClusteredSession) mgr.createEmptySession();
+      assertTrue(sess.isOutdated());
+      sess.setCreationTime(System.currentTimeMillis());
+      assertFalse(sess.isOutdated());
+      
+      mgr.setReplicationGranularity(ReplicationGranularity.ATTRIBUTE);
+      sess = (ClusteredSession) mgr.createEmptySession();
+      assertTrue(sess.isOutdated());
+      sess.setCreationTime(System.currentTimeMillis());
+      assertFalse(sess.isOutdated());
+      
+      mgr.setReplicationGranularity(ReplicationGranularity.FIELD);
+      sess = (ClusteredSession) mgr.createEmptySession();
+      assertTrue(sess.isOutdated());
+      sess.setCreationTime(System.currentTimeMillis());
+      assertFalse(sess.isOutdated());
+   }
+
+}


Property changes on: branches/Branch_5_x/testsuite/src/main/org/jboss/test/cluster/defaultcfg/simpleweb/test/ClusteredSessionUnitTestCase.java
___________________________________________________________________
Name: svn:keywords
   + 

Added: branches/Branch_5_x/testsuite/src/main/org/jboss/test/cluster/defaultcfg/simpleweb/test/ConcurrentFailoverRequestsTestCase.java
===================================================================
--- branches/Branch_5_x/testsuite/src/main/org/jboss/test/cluster/defaultcfg/simpleweb/test/ConcurrentFailoverRequestsTestCase.java	                        (rev 0)
+++ branches/Branch_5_x/testsuite/src/main/org/jboss/test/cluster/defaultcfg/simpleweb/test/ConcurrentFailoverRequestsTestCase.java	2009-10-22 23:04:05 UTC (rev 95454)
@@ -0,0 +1,279 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2009 Red Hat, Inc. and individual contributors
+ * as indicated by the @author tags. See the copyright.txt file in the
+ * distribution for a full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+
+package org.jboss.test.cluster.defaultcfg.simpleweb.test;
+
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.TimeUnit;
+
+import junit.framework.TestCase;
+
+import org.apache.catalina.Manager;
+import org.apache.catalina.Session;
+import org.apache.catalina.Valve;
+import org.apache.catalina.connector.Request;
+import org.jboss.cache.Cache;
+import org.jboss.cache.Fqn;
+import org.jboss.cache.pojo.PojoCache;
+import org.jboss.logging.Logger;
+import org.jboss.metadata.web.jboss.JBossWebMetaData;
+import org.jboss.test.cluster.testutil.JBossCacheUtil;
+import org.jboss.test.cluster.testutil.JGroupsSystemPropertySupport;
+import org.jboss.test.cluster.testutil.SessionTestUtil;
+import org.jboss.test.cluster.web.mocks.BasicRequestHandler;
+import org.jboss.test.cluster.web.mocks.ConcurrentRequestHandler;
+import org.jboss.test.cluster.web.mocks.SetAttributesRequestHandler;
+import org.jboss.web.tomcat.service.session.JBossCacheManager;
+
+/**
+ * JBAS-7379. Tests that multiple concurrent failover requests for
+ * the same session are handled properly.
+ * 
+ * @author <a href="brian.stansberry at jboss.com">Brian Stansberry</a>
+ * @version $Revision: 85945 $
+ */
+public class ConcurrentFailoverRequestsTestCase extends TestCase
+{
+   private static final Logger log = Logger.getLogger(ConcurrentFailoverRequestsTestCase.class);
+   
+   private static long testCount = System.currentTimeMillis();
+   
+   private final JGroupsSystemPropertySupport jgSupport = new JGroupsSystemPropertySupport();
+   private Set<PojoCache> caches = new HashSet<PojoCache>();
+   
+   private ExecutorService threadPool;
+   
+   /**
+    * Create a new ConcurrentFailoverRequestsTestCase.
+    * 
+    * @param name
+    */
+   public ConcurrentFailoverRequestsTestCase(String name)
+   {
+      super(name);
+   } 
+
+   @Override
+   protected void setUp() throws Exception
+   {
+      super.setUp();
+      
+      jgSupport.setUpProperties();
+   }
+
+   @Override
+   protected void tearDown() throws Exception
+   {
+      try
+      {
+         super.tearDown();
+      }
+      finally
+      {      
+         jgSupport.restoreProperties();
+         
+         if (threadPool != null)
+         {
+            threadPool.shutdownNow();
+         }
+         
+         SessionTestUtil.clearDistributedCacheManagerFactory();
+         
+         for (PojoCache cache : caches)
+         { 
+            // Try to clean up so we avoid loading sessions 
+            // from storage in later tests
+            try
+            {
+               log.info("Removing /JSESSION from " + cache.getCache().getLocalAddress());
+               cache.getCache().removeNode(Fqn.fromString("/JSESSION"));
+            }
+            catch (Exception e)
+            {
+               log.error("Cache " + cache.getCache().getLocalAddress() + ": " + e.getMessage(), e);
+            }
+            
+            try
+            {
+               cache.stop();
+               cache.destroy();
+            }
+            catch (Exception e)
+            {
+               log.error("Cache " + cache.getCache().getLocalAddress() + ": " + e.getMessage(), e);
+            }
+            
+         }
+         
+         caches.clear();
+      }
+   }
+
+   public void testConcurrentFailoverRequests() throws Exception
+   {
+      ++testCount;
+      
+      JBossWebMetaData webMetaData = SessionTestUtil.createWebMetaData(100);
+      String warName = "test" + testCount;
+      JBossCacheManager jbcm0 = SessionTestUtil.createManager(warName, 30, false, null, false, false, null, caches);
+      jbcm0.init(warName, webMetaData);      
+      jbcm0.start();
+      
+      JBossCacheManager jbcm1 = SessionTestUtil.createManager(warName, 30, false, null, false, false, null, caches);
+      jbcm1.init(warName, webMetaData);      
+      jbcm1.start();
+      
+      Cache[] array = new Cache[caches.size()];
+      int index = 0;
+      for (PojoCache c : caches)
+      {
+         array[index] = c.getCache();
+         index++;
+      }
+      JBossCacheUtil.blockUntilViewsReceived(array, 10000);
+      
+      Object value = "0";
+      Map<String, Object> attrs = Collections.unmodifiableMap(Collections.singletonMap("count", value));
+      SetAttributesRequestHandler setHandler = new SetAttributesRequestHandler(attrs, false);
+      SessionTestUtil.invokeRequest(jbcm0, setHandler, null);
+      
+      String id1 = setHandler.getSessionId();
+      assertNotNull(id1);
+      
+      // Add a second session that we can check for replication; this is a proxy
+      // for checking that first session has replicated
+      setHandler = new SetAttributesRequestHandler(attrs, false);
+      SessionTestUtil.invokeRequest(jbcm0, setHandler, null);
+      
+      String id2 = setHandler.getSessionId();
+      assertNotNull(id1);
+      
+      assertFalse(id1.equals(id2));
+      
+      // Ensure replication of session 2 has occurred
+      boolean found = false;
+      for (int i = 0; i < 10; i++)
+      {
+         BasicRequestHandler getHandler = new BasicRequestHandler(attrs.keySet(), false);
+         SessionTestUtil.invokeRequest(jbcm1, getHandler, id2);
+         if (getHandler.getCheckedAttributes() != null && value.equals(getHandler.getCheckedAttributes().get("count")))
+         {
+            found = true;
+            break;
+         }
+         Thread.sleep(50);            
+      }
+      assertTrue("sessions replicated", found);
+      
+      jbcm0.stop();
+      
+      int THREADS = 10;
+      threadPool = Executors.newFixedThreadPool(THREADS);
+      
+      CountDownLatch startingGun = new CountDownLatch(THREADS + 1);
+      CountDownLatch finishedSignal = new CountDownLatch(THREADS);
+      ConcurrentRequestHandler concurrentHandler = new ConcurrentRequestHandler();
+      Valve pipelineHead = SessionTestUtil.setupPipeline(jbcm1, concurrentHandler);
+      Loader[] loaders = new Loader[THREADS];
+      
+      for (int i = 0; i < loaders.length; i++)
+      {
+         loaders[i] = new Loader(pipelineHead, concurrentHandler, jbcm1, id1, attrs.keySet(), startingGun, finishedSignal);
+         threadPool.execute(loaders[i]);
+      }
+      
+      startingGun.countDown();
+      
+      assertTrue("loaders completed on time", finishedSignal.await(45, TimeUnit.SECONDS));     
+      
+      for (int i = 0; i < loaders.length; i++)
+      {         
+         assertNotNull("got checked attributes for " + i, loaders[i].checkedAttributes);
+         assertTrue("checked 'count' attribute for " + i, loaders[i].checkedAttributes.containsKey("count"));
+         assertEquals("correct value for " + i, value, loaders[i].checkedAttributes.get("count"));
+      }
+   }
+   
+   private static class Loader implements Runnable
+   {
+      private final Valve pipelineHead;
+      private final ConcurrentRequestHandler concurrentHandler;
+      private final Manager manager;
+      private final String sessionId;
+      private final Set<String> attributeKeys;
+      private final CountDownLatch startingGun;
+      private final CountDownLatch finishedSignal;
+      
+      private Map<String, Object> checkedAttributes;
+
+      private Loader(Valve pipelineHead, ConcurrentRequestHandler concurrentHandler,
+            Manager manager, String sessionId, Set<String> attributeKeys, 
+            CountDownLatch startingGun, CountDownLatch finishedSignal)
+      {
+         this.pipelineHead = pipelineHead;
+         this.concurrentHandler = concurrentHandler;
+         this.manager = manager;
+         this.sessionId = sessionId;
+         this.attributeKeys = attributeKeys;
+         this.startingGun = startingGun;
+         this.finishedSignal = finishedSignal;
+      }
+      
+      public void run()
+      {
+         try
+         {
+            BasicRequestHandler getHandler = new BasicRequestHandler(attributeKeys, false);
+            concurrentHandler.registerHandler(getHandler);
+            Request request = SessionTestUtil.setupRequest(manager, sessionId);
+            startingGun.countDown();
+            startingGun.await();
+            System.out.println("started");
+            
+            SessionTestUtil.invokeRequest(pipelineHead, request);
+            this.checkedAttributes = getHandler.getCheckedAttributes();
+            if (this.checkedAttributes != null)
+            {
+               System.out.println(this.checkedAttributes.keySet());
+            }
+         }
+         catch (Exception e)
+         {
+            e.printStackTrace(System.out);
+         }
+         finally
+         {
+            finishedSignal.countDown();
+            
+            concurrentHandler.unregisterHandler();
+         }
+         
+      }
+      
+   }
+}


Property changes on: branches/Branch_5_x/testsuite/src/main/org/jboss/test/cluster/defaultcfg/simpleweb/test/ConcurrentFailoverRequestsTestCase.java
___________________________________________________________________
Name: svn:keywords
   + 

Added: branches/Branch_5_x/testsuite/src/main/org/jboss/test/cluster/testutil/JBossCacheUtil.java
===================================================================
--- branches/Branch_5_x/testsuite/src/main/org/jboss/test/cluster/testutil/JBossCacheUtil.java	                        (rev 0)
+++ branches/Branch_5_x/testsuite/src/main/org/jboss/test/cluster/testutil/JBossCacheUtil.java	2009-10-22 23:04:05 UTC (rev 95454)
@@ -0,0 +1,175 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2009 Red Hat, Inc. and individual contributors
+ * as indicated by the @author tags. See the copyright.txt file in the
+ * distribution for a full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+
+package org.jboss.test.cluster.testutil;
+
+import java.util.List;
+
+import org.jboss.cache.Cache;
+import org.jboss.cache.CacheSPI;
+
+/**
+ * Utilities related to dealing with JBoss Cache.
+ *
+ * @author Brian Stansberry
+ * 
+ * @version $Revision: $
+ */
+public class JBossCacheUtil
+{
+
+   /**
+    * Loops, continually calling {@link #areCacheViewsComplete(org.jboss.cache.Cache[])}
+    * until it either returns true or <code>timeout</code> ms have elapsed.
+    *
+    * @param caches  caches which must all have consistent views
+    * @param timeout max number of ms to loop
+    * @throws RuntimeException if <code>timeout</code> ms have elapse without
+    *                          all caches having the same number of members.
+    */
+   public static void blockUntilViewsReceived(Cache[] caches, long timeout)
+   {
+      long failTime = System.currentTimeMillis() + timeout;
+
+      while (System.currentTimeMillis() < failTime)
+      {
+         sleepThread(100);
+         if (areCacheViewsComplete(caches))
+         {
+            return;
+         }
+      }
+
+      throw new RuntimeException("timed out before caches had complete views" + views(caches));
+   }
+
+   /**
+    * Checks each cache to see if the number of elements in the array
+    * returned by {@link CacheSPI#getMembers()} matches the size of
+    * the <code>caches</code> parameter.
+    *
+    * @param caches caches that should form a View
+    * @return <code>true</code> if all caches have
+    *         <code>caches.length</code> members; false otherwise
+    * @throws IllegalStateException if any of the caches have MORE view
+    *                               members than caches.length
+    */
+   public static boolean areCacheViewsComplete(Cache[] caches)
+   {
+      return areCacheViewsComplete(caches, true);
+   }
+
+   public static boolean areCacheViewsComplete(Cache[] caches, boolean barfIfTooManyMembers)
+   {
+      int memberCount = caches.length;
+
+      for (int i = 0; i < memberCount; i++)
+      {
+         if (!isCacheViewComplete(caches[i], memberCount, barfIfTooManyMembers))
+         {
+            return false;
+         }
+      }
+
+      return true;
+   }
+
+   public static boolean isCacheViewComplete(Cache c, int memberCount)
+   {
+      return isCacheViewComplete(c, memberCount, true);
+   }
+
+   public static boolean isCacheViewComplete(Cache cache, int memberCount, boolean barfIfTooManyMembers)
+   {
+      List members = cache.getMembers();
+      if (members == null || memberCount > members.size())
+      {
+         return false;
+      }
+      else if (memberCount < members.size())
+      {
+         if (barfIfTooManyMembers)
+         {
+            // This is an exceptional condition
+            StringBuilder sb = new StringBuilder("Cache at address ");
+            sb.append(cache.getLocalAddress());
+            sb.append(" had ");
+            sb.append(members.size());
+            sb.append(" members; expecting ");
+            sb.append(memberCount);
+            sb.append(". Members were (");
+            for (int j = 0; j < members.size(); j++)
+            {
+               if (j > 0)
+               {
+                  sb.append(", ");
+               }
+               sb.append(members.get(j));
+            }
+            sb.append(')');
+
+            throw new IllegalStateException(sb.toString());
+         }
+         else return false;
+      }
+
+      return true;
+   }
+
+
+   /**
+    * Puts the current thread to sleep for the desired number of ms, suppressing
+    * any exceptions.
+    *
+    * @param sleeptime number of ms to sleep
+    */
+   public static void sleepThread(long sleeptime)
+   {
+      try
+      {
+         Thread.sleep(sleeptime);
+      }
+      catch (InterruptedException ie)
+      {
+      }
+   }
+
+   private static String views(Cache... caches)
+   {
+      StringBuilder builder = new StringBuilder("[\n");
+      for (Cache c:caches)
+      {
+         builder.append("   ").append(c.getLocalAddress()).append("->").append(c.getMembers()).append("\n");
+      }
+      builder.append("]");
+      return builder.toString();
+   }
+
+   /**
+    * Prevent instantiation 
+    */
+   private JBossCacheUtil()
+   {
+      // TODO Auto-generated constructor stub
+   }
+
+}


Property changes on: branches/Branch_5_x/testsuite/src/main/org/jboss/test/cluster/testutil/JBossCacheUtil.java
___________________________________________________________________
Name: svn:keywords
   + 

Added: branches/Branch_5_x/testsuite/src/main/org/jboss/test/cluster/web/mocks/ConcurrentRequestHandler.java
===================================================================
--- branches/Branch_5_x/testsuite/src/main/org/jboss/test/cluster/web/mocks/ConcurrentRequestHandler.java	                        (rev 0)
+++ branches/Branch_5_x/testsuite/src/main/org/jboss/test/cluster/web/mocks/ConcurrentRequestHandler.java	2009-10-22 23:04:05 UTC (rev 95454)
@@ -0,0 +1,72 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2009 Red Hat, Inc. and individual contributors
+ * as indicated by the @author tags. See the copyright.txt file in the
+ * distribution for a full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+
+package org.jboss.test.cluster.web.mocks;
+
+import java.util.HashSet;
+import java.util.Set;
+
+import org.apache.catalina.connector.Request;
+import org.apache.catalina.connector.Response;
+
+/**
+ * Uses a ThreadLocal to allow a single RequestHandlerValve to concurrently
+ * handle requests.
+ *
+ * @author Brian Stansberry
+ * 
+ * @version $Revision: $
+ */
+public class ConcurrentRequestHandler implements RequestHandler
+{
+   private final ThreadLocal<RequestHandler> threadHandler = new ThreadLocal<RequestHandler>();
+   private final Set<RequestHandler> handlers = new HashSet<RequestHandler>();
+   
+   public void registerHandler(RequestHandler handler)
+   {
+      threadHandler.set(handler);
+   }
+   
+   public void unregisterHandler()
+   {
+      threadHandler.remove();
+   }
+   
+   public void clear()
+   {
+      for (RequestHandler handler : handlers)
+      {
+         handler.clear();
+      }
+   }
+
+   public void handleRequest(Request request, Response response)
+   {
+      RequestHandler handler = threadHandler.get();
+      if (handler == null)
+      {
+         throw new IllegalStateException("No handler; call registerHandler before executing requet");
+      }
+      handler.handleRequest(request, response);      
+   }
+
+}


Property changes on: branches/Branch_5_x/testsuite/src/main/org/jboss/test/cluster/web/mocks/ConcurrentRequestHandler.java
___________________________________________________________________
Name: svn:keywords
   + 

Modified: branches/Branch_5_x/tomcat/src/main/org/jboss/web/tomcat/service/session/ClusteredSession.java
===================================================================
--- branches/Branch_5_x/tomcat/src/main/org/jboss/web/tomcat/service/session/ClusteredSession.java	2009-10-22 23:01:05 UTC (rev 95453)
+++ branches/Branch_5_x/tomcat/src/main/org/jboss/web/tomcat/service/session/ClusteredSession.java	2009-10-22 23:04:05 UTC (rev 95454)
@@ -1310,7 +1310,9 @@
     */
    public boolean isOutdated()
    {
-      return thisAccessedTime < outdatedTime;
+      // if creationTime == 0 we've neither been synced with the
+      // distributed cache nor had creation time set (i.e. brand new session)
+      return thisAccessedTime < outdatedTime || this.creationTime == 0;
    } 
    
    public boolean isSessionDirty()

Modified: branches/Branch_5_x/tomcat/src/main/org/jboss/web/tomcat/service/session/JBossCacheManager.java
===================================================================
--- branches/Branch_5_x/tomcat/src/main/org/jboss/web/tomcat/service/session/JBossCacheManager.java	2009-10-22 23:01:05 UTC (rev 95453)
+++ branches/Branch_5_x/tomcat/src/main/org/jboss/web/tomcat/service/session/JBossCacheManager.java	2009-10-22 23:04:05 UTC (rev 95454)
@@ -31,6 +31,7 @@
 import java.util.Set;
 import java.util.TreeSet;
 import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
 import java.util.concurrent.Semaphore;
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.atomic.AtomicInteger;
@@ -107,6 +108,13 @@
    private Map<String, OwnedSessionUpdate> unloadedSessions_ = 
          new ConcurrentHashMap<String, OwnedSessionUpdate>();
    
+   /** 
+    * Sessions that have been created but not yet loaded. Used to ensure
+    * concurrent threads trying to load the same session
+    */
+   private final ConcurrentMap<String, ClusteredSession<? extends OutgoingDistributableSessionData>> embryonicSessions = 
+      new ConcurrentHashMap<String, ClusteredSession<? extends OutgoingDistributableSessionData>>();
+   
    /** Number of passivated sessions */
    private AtomicInteger passivatedCount_ = new AtomicInteger();
    
@@ -2009,12 +2017,29 @@
                   mustAdd = true;
                   session = createEmptyClusteredSession();
                   
+                  // JBAS-7379 Ensure concurrent threads trying to load same session id
+                  // use the same session
+                  ClusteredSession<? extends OutgoingDistributableSessionData> embryo = 
+                     this.embryonicSessions.putIfAbsent(realId, session);
+                  if (embryo != null)
+                  {
+                     session = embryo;
+                  }
+                  
                   OwnedSessionUpdate osu = unloadedSessions_.get(realId);
                   passivated = (osu != null && osu.isPassivated());
                }
   
                synchronized (session)
                {
+                  // JBAS-7379 check if we lost the race to the sync block
+                  // and another thread has already loaded this session
+                  if (initialLoad && session.isOutdated() == false)
+                  {
+                     // some one else loaded this
+                     return session;
+                  }
+                  
                   ContextClassLoaderSwitcher.SwitchContext switcher = null; 
                   boolean doTx = false; 
                   boolean loadCompleted = false;
@@ -2137,6 +2162,14 @@
                      log_.trace("loadSession(): session " + realId +
                                 " not found in distributed cache");
                   }
+                  
+                  if (initialLoad)
+                  {                     
+                     // The session is now in the regular map, or the session
+                     // doesn't exist in the distributed cache. either way
+                     // it's now safe to stop tracking this embryonic session
+                     embryonicSessions.remove(realId);
+                  }
                }
             }
             finally




More information about the jboss-cvs-commits mailing list