[jboss-cvs] JBossAS SVN: r95458 - in trunk: 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 20:38:43 EDT 2009
Author: bstansberry at jboss.com
Date: 2009-10-22 20:38:42 -0400 (Thu, 22 Oct 2009)
New Revision: 95458
Added:
trunk/testsuite/src/main/org/jboss/test/cluster/defaultcfg/simpleweb/test/ClusteredSessionUnitTestCase.java
trunk/testsuite/src/main/org/jboss/test/cluster/defaultcfg/simpleweb/test/ConcurrentFailoverRequestsTestCase.java
trunk/testsuite/src/main/org/jboss/test/cluster/testutil/JBossCacheUtil.java
trunk/testsuite/src/main/org/jboss/test/cluster/web/mocks/ConcurrentRequestHandler.java
Modified:
trunk/testsuite/src/main/org/jboss/test/cluster/web/mocks/MockDistributedCacheManager.java
trunk/tomcat/src/main/java/org/jboss/web/tomcat/service/session/ClusteredSession.java
trunk/tomcat/src/main/java/org/jboss/web/tomcat/service/session/JBossCacheManager.java
Log:
[JBAS-7379] Handle concurrent requests trying to gravitate a session
Copied: trunk/testsuite/src/main/org/jboss/test/cluster/defaultcfg/simpleweb/test/ClusteredSessionUnitTestCase.java (from rev 95454, branches/Branch_5_x/testsuite/src/main/org/jboss/test/cluster/defaultcfg/simpleweb/test/ClusteredSessionUnitTestCase.java)
===================================================================
--- trunk/testsuite/src/main/org/jboss/test/cluster/defaultcfg/simpleweb/test/ClusteredSessionUnitTestCase.java (rev 0)
+++ trunk/testsuite/src/main/org/jboss/test/cluster/defaultcfg/simpleweb/test/ClusteredSessionUnitTestCase.java 2009-10-23 00:38:42 UTC (rev 95458)
@@ -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());
+ }
+
+}
Copied: trunk/testsuite/src/main/org/jboss/test/cluster/defaultcfg/simpleweb/test/ConcurrentFailoverRequestsTestCase.java (from rev 95454, branches/Branch_5_x/testsuite/src/main/org/jboss/test/cluster/defaultcfg/simpleweb/test/ConcurrentFailoverRequestsTestCase.java)
===================================================================
--- trunk/testsuite/src/main/org/jboss/test/cluster/defaultcfg/simpleweb/test/ConcurrentFailoverRequestsTestCase.java (rev 0)
+++ trunk/testsuite/src/main/org/jboss/test/cluster/defaultcfg/simpleweb/test/ConcurrentFailoverRequestsTestCase.java 2009-10-23 00:38:42 UTC (rev 95458)
@@ -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();
+ }
+
+ }
+
+ }
+}
Copied: trunk/testsuite/src/main/org/jboss/test/cluster/testutil/JBossCacheUtil.java (from rev 95454, branches/Branch_5_x/testsuite/src/main/org/jboss/test/cluster/testutil/JBossCacheUtil.java)
===================================================================
--- trunk/testsuite/src/main/org/jboss/test/cluster/testutil/JBossCacheUtil.java (rev 0)
+++ trunk/testsuite/src/main/org/jboss/test/cluster/testutil/JBossCacheUtil.java 2009-10-23 00:38:42 UTC (rev 95458)
@@ -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
+ }
+
+}
Copied: trunk/testsuite/src/main/org/jboss/test/cluster/web/mocks/ConcurrentRequestHandler.java (from rev 95454, branches/Branch_5_x/testsuite/src/main/org/jboss/test/cluster/web/mocks/ConcurrentRequestHandler.java)
===================================================================
--- trunk/testsuite/src/main/org/jboss/test/cluster/web/mocks/ConcurrentRequestHandler.java (rev 0)
+++ trunk/testsuite/src/main/org/jboss/test/cluster/web/mocks/ConcurrentRequestHandler.java 2009-10-23 00:38:42 UTC (rev 95458)
@@ -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);
+ }
+
+}
Modified: trunk/testsuite/src/main/org/jboss/test/cluster/web/mocks/MockDistributedCacheManager.java
===================================================================
--- trunk/testsuite/src/main/org/jboss/test/cluster/web/mocks/MockDistributedCacheManager.java 2009-10-23 00:36:56 UTC (rev 95457)
+++ trunk/testsuite/src/main/org/jboss/test/cluster/web/mocks/MockDistributedCacheManager.java 2009-10-23 00:38:42 UTC (rev 95458)
@@ -67,7 +67,7 @@
public BatchingManager getBatchingManager()
{
- return null;
+ return MockBatchingManager.INSTANCE;
}
public IncomingDistributableSessionData getSessionData(String realId, boolean initialLoad)
@@ -161,5 +161,35 @@
{
// no-op
}
+
+ private static class MockBatchingManager implements BatchingManager
+ {
+ private static final MockBatchingManager INSTANCE = new MockBatchingManager();
+
+ public void endBatch()
+ {
+ // TODO Auto-generated method stub
+
+ }
+ public boolean isBatchInProgress() throws Exception
+ {
+ // TODO Auto-generated method stub
+ return false;
+ }
+
+ public void setBatchRollbackOnly() throws Exception
+ {
+ // TODO Auto-generated method stub
+
+ }
+
+ public void startBatch() throws Exception
+ {
+ // TODO Auto-generated method stub
+
+ }
+
+ }
+
}
Modified: trunk/tomcat/src/main/java/org/jboss/web/tomcat/service/session/ClusteredSession.java
===================================================================
--- trunk/tomcat/src/main/java/org/jboss/web/tomcat/service/session/ClusteredSession.java 2009-10-23 00:36:56 UTC (rev 95457)
+++ trunk/tomcat/src/main/java/org/jboss/web/tomcat/service/session/ClusteredSession.java 2009-10-23 00:38:42 UTC (rev 95458)
@@ -1264,7 +1264,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: trunk/tomcat/src/main/java/org/jboss/web/tomcat/service/session/JBossCacheManager.java
===================================================================
--- trunk/tomcat/src/main/java/org/jboss/web/tomcat/service/session/JBossCacheManager.java 2009-10-23 00:36:56 UTC (rev 95457)
+++ trunk/tomcat/src/main/java/org/jboss/web/tomcat/service/session/JBossCacheManager.java 2009-10-23 00:38:42 UTC (rev 95458)
@@ -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;
@@ -105,6 +106,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();
@@ -2008,12 +2016,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;
@@ -2136,6 +2161,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