[jboss-cvs] JBossAS SVN: r77496 - in trunk: testsuite/src/main/org/jboss/test/cluster/testutil and 8 other directories.

jboss-cvs-commits at lists.jboss.org jboss-cvs-commits at lists.jboss.org
Tue Aug 26 12:41:17 EDT 2008


Author: bstansberry at jboss.com
Date: 2008-08-26 12:41:17 -0400 (Tue, 26 Aug 2008)
New Revision: 77496

Added:
   trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/JBossCacheManager.java
   trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/JBossCacheManagerMBean.java
   trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/distributedcache/
   trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/distributedcache/impl/
   trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/distributedcache/impl/DistributedCacheManagerFactoryImpl.java
   trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/distributedcache/impl/jbc2/
   trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/distributedcache/impl/jbc2/BatchingManagerImpl.java
   trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/distributedcache/impl/jbc2/CacheListener.java
   trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/distributedcache/impl/jbc2/CacheListenerBase.java
   trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/distributedcache/impl/jbc2/FieldBasedJBossCacheService.java
   trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/distributedcache/impl/jbc2/JBossCacheService.java
   trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/distributedcache/impl/jbc2/JBossCacheWrapper.java
   trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/distributedcache/impl/jbc2/PassivationListener.java
   trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/distributedcache/impl/jbc2/Util.java
   trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/distributedcache/spi/
   trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/distributedcache/spi/BatchingManager.java
   trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/distributedcache/spi/ClusteringNotSupportedException.java
   trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/distributedcache/spi/DistributableSession.java
   trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/distributedcache/spi/DistributableSessionMetadata.java
   trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/distributedcache/spi/DistributableSessionTimestamp.java
   trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/distributedcache/spi/DistributedCacheManager.java
   trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/distributedcache/spi/DistributedCacheManagerFactory.java
   trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/distributedcache/spi/DistributedCacheManagerFactoryFactory.java
   trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/distributedcache/spi/FieldBasedDistributedCacheManager.java
   trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/distributedcache/spi/JBossSerializationObjectStreamSource.java
   trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/distributedcache/spi/LocalDistributableSessionManager.java
   trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/distributedcache/spi/SessionSerializationFactory.java
   trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/distributedcache/spi/TomcatClusterConfig.java
   trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/distributedcache/spi/TomcatClusterDistributedCacheManagerFactory.java
Removed:
   trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/CacheListener.java
   trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/CacheListenerBase.java
   trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/ClusteringNotSupportedException.java
   trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/FieldBasedJBossCacheService.java
   trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/JBossCacheManager.java
   trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/JBossCacheManagerMBean.java
   trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/JBossCacheService.java
   trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/JBossCacheWrapper.java
   trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/JBossSerializationObjectStreamSource.java
   trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/PassivationListener.java
   trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/SessionSerializationFactory.java
Modified:
   trunk/testsuite/src/main/org/jboss/test/cluster/defaultcfg/simpleweb/test/CacheListenerUnitTestCase.java
   trunk/testsuite/src/main/org/jboss/test/cluster/defaultcfg/simpleweb/test/JBossCacheManagerConfigurationUnitTestCase.java
   trunk/testsuite/src/main/org/jboss/test/cluster/defaultcfg/simpleweb/test/SessionBasedMaxUnreplicatedIntervalTestCase.java
   trunk/testsuite/src/main/org/jboss/test/cluster/defaultcfg/simpleweb/test/SessionCountUnitTestCase.java
   trunk/testsuite/src/main/org/jboss/test/cluster/defaultcfg/simpleweb/test/SessionExpirationUnitTestCase.java
   trunk/testsuite/src/main/org/jboss/test/cluster/testutil/SessionTestUtil.java
   trunk/testsuite/src/main/org/jboss/test/cluster/web/CacheHelper.java
   trunk/testsuite/src/main/org/jboss/test/cluster/web/jvmroute/MockJBossManager.java
   trunk/tomcat/src/main/org/jboss/web/tomcat/service/deployers/TomcatDeployment.java
   trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/AbstractJBossManager.java
   trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/ClusteredSession.java
   trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/ClusteredSessionValve.java
   trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/FieldBasedClusteredSession.java
   trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/JBossCacheCluster.java
   trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/JBossCacheClusteredSession.java
   trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/JBossManager.java
   trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/SessionBasedClusteredSession.java
   trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/Util.java
Log:
[JBAS-5820] Refactor JBC integration into an SPI and an impl

Modified: trunk/testsuite/src/main/org/jboss/test/cluster/defaultcfg/simpleweb/test/CacheListenerUnitTestCase.java
===================================================================
--- trunk/testsuite/src/main/org/jboss/test/cluster/defaultcfg/simpleweb/test/CacheListenerUnitTestCase.java	2008-08-26 16:10:33 UTC (rev 77495)
+++ trunk/testsuite/src/main/org/jboss/test/cluster/defaultcfg/simpleweb/test/CacheListenerUnitTestCase.java	2008-08-26 16:41:17 UTC (rev 77496)
@@ -26,10 +26,10 @@
 
 import org.jboss.cache.Fqn;
 import org.jboss.cache.buddyreplication.BuddyManager;
-import org.jboss.web.tomcat.service.session.CacheListener;
-import org.jboss.web.tomcat.service.session.CacheListenerBase;
-import org.jboss.web.tomcat.service.session.FieldBasedJBossCacheService;
-import org.jboss.web.tomcat.service.session.JBossCacheService;
+import org.jboss.web.tomcat.service.session.distributedcache.impl.jbc2.CacheListener;
+import org.jboss.web.tomcat.service.session.distributedcache.impl.jbc2.CacheListenerBase;
+import org.jboss.web.tomcat.service.session.distributedcache.impl.jbc2.FieldBasedJBossCacheService;
+import org.jboss.web.tomcat.service.session.distributedcache.impl.jbc2.JBossCacheService;
 
 /**
  * A CacheListenerUnitTestCase.

Modified: trunk/testsuite/src/main/org/jboss/test/cluster/defaultcfg/simpleweb/test/JBossCacheManagerConfigurationUnitTestCase.java
===================================================================
--- trunk/testsuite/src/main/org/jboss/test/cluster/defaultcfg/simpleweb/test/JBossCacheManagerConfigurationUnitTestCase.java	2008-08-26 16:10:33 UTC (rev 77495)
+++ trunk/testsuite/src/main/org/jboss/test/cluster/defaultcfg/simpleweb/test/JBossCacheManagerConfigurationUnitTestCase.java	2008-08-26 16:41:17 UTC (rev 77496)
@@ -72,6 +72,8 @@
    {
       super.tearDown();
       
+      SessionTestUtil.clearDistributedCacheManagerFactory();
+      
       for (PojoCache cache : caches)
       { 
          if (CacheStatus.STARTED.equals(cache.getCache().getCacheStatus()))
@@ -118,16 +120,17 @@
       
       ++testCount;
       JBossCacheManager jbcm = SessionTestUtil.createManager("test" + testCount, 5, true, null, false, false, null, caches);
-      
+      PojoCache cache = SessionTestUtil.getDistributedCacheManagerFactoryPojoCache();
       JBossWebMetaData webMetaData = createWebMetaData(null, null, null, null, null);
       jbcm.init("test.war", webMetaData);      
       jbcm.start();
 
       assertFalse("With no config, not using JK", jbcm.getUseJK());
       
-      cleanupManager(jbcm);
+      cleanupManager(jbcm, cache);
       
       jbcm = SessionTestUtil.createManager("test" + ++testCount, 5, true, null, false, false, null, caches);
+      cache = SessionTestUtil.getDistributedCacheManagerFactoryPojoCache();
       
       webMetaData = createWebMetaData(null, null, null, null, Boolean.TRUE);
       jbcm.init("test.war", webMetaData);      
@@ -135,9 +138,10 @@
 
       assertTrue("With no jvmRoute but a config, using JK", jbcm.getUseJK());
       
-      cleanupManager(jbcm);
+      cleanupManager(jbcm, cache);
       
       jbcm = SessionTestUtil.createManager("test" + ++testCount, 5, true, null, false, false, "test", caches);
+      cache = SessionTestUtil.getDistributedCacheManagerFactoryPojoCache();
       
       webMetaData = createWebMetaData(null, null, null, null, null);
       jbcm.init("test.war", webMetaData);      
@@ -145,9 +149,10 @@
 
       assertTrue("With jvmRoute set, using JK", jbcm.getUseJK());
       
-      cleanupManager(jbcm);
+      cleanupManager(jbcm, cache);
       
       jbcm = SessionTestUtil.createManager("test" + ++testCount, 5, true, null, false, false, "test", caches);
+      cache = SessionTestUtil.getDistributedCacheManagerFactoryPojoCache();
       
       webMetaData = createWebMetaData(null, null, null, null, Boolean.FALSE);
       jbcm.init("test.war", webMetaData);      
@@ -155,8 +160,7 @@
 
       assertFalse("With a jvmRoute but config=false, not using JK", jbcm.getUseJK());
       
-      cleanupManager(jbcm);
-      
+      cleanupManager(jbcm, cache);      
    }
    
    public void testSnapshot() throws Exception
@@ -165,16 +169,18 @@
       
       ++testCount;
       JBossCacheManager jbcm = SessionTestUtil.createManager("test" + testCount, 5, true, null, false, false, null, caches);
-       
+      PojoCache cache = SessionTestUtil.getDistributedCacheManagerFactoryPojoCache();
+      
       JBossWebMetaData webMetaData = createWebMetaData(null, null, null, null, null);
       jbcm.init("test.war", webMetaData);      
       jbcm.start();
 
       assertEquals("With no config, using instant", SnapshotMode.INSTANT, jbcm.getSnapshotMode());
       
-      cleanupManager(jbcm);
+      cleanupManager(jbcm, cache);
       
       jbcm = SessionTestUtil.createManager("test" + ++testCount, 5, true, null, false, false, null, caches);
+      cache = SessionTestUtil.getDistributedCacheManagerFactoryPojoCache();
       
       webMetaData = createWebMetaData(null, null, null, null, Boolean.TRUE);
       webMetaData.getReplicationConfig().setSnapshotMode(SnapshotMode.INTERVAL);
@@ -185,13 +191,12 @@
       assertEquals("With config, using interval", SnapshotMode.INTERVAL, jbcm.getSnapshotMode());
       assertEquals("With config, using 2 second interval", 2, jbcm.getSnapshotInterval());
       
-      cleanupManager(jbcm);
+      cleanupManager(jbcm, cache);
       
    }
    
-   private void cleanupManager(JBossCacheManager mgr) throws Exception
+   private void cleanupManager(JBossCacheManager mgr, PojoCache cache) throws Exception
    {
-      PojoCache cache = mgr.getPojoCache();
       mgr.stop();
       cache.stop();
       cache.destroy();

Modified: trunk/testsuite/src/main/org/jboss/test/cluster/defaultcfg/simpleweb/test/SessionBasedMaxUnreplicatedIntervalTestCase.java
===================================================================
--- trunk/testsuite/src/main/org/jboss/test/cluster/defaultcfg/simpleweb/test/SessionBasedMaxUnreplicatedIntervalTestCase.java	2008-08-26 16:10:33 UTC (rev 77495)
+++ trunk/testsuite/src/main/org/jboss/test/cluster/defaultcfg/simpleweb/test/SessionBasedMaxUnreplicatedIntervalTestCase.java	2008-08-26 16:41:17 UTC (rev 77496)
@@ -37,13 +37,13 @@
 import org.jboss.metadata.web.jboss.ReplicationGranularity;
 import org.jboss.metadata.web.jboss.ReplicationTrigger;
 import org.jboss.test.JBossTestCase;
+import org.jboss.test.cluster.testutil.CacheConfigTestSetup;
+import org.jboss.test.cluster.testutil.SessionTestUtil;
 import org.jboss.test.cluster.web.mocks.BasicRequestHandler;
-import org.jboss.test.cluster.testutil.CacheConfigTestSetup;
 import org.jboss.test.cluster.web.mocks.MutableObject;
 import org.jboss.test.cluster.web.mocks.SetAttributesRequestHandler;
-import org.jboss.test.cluster.testutil.SessionTestUtil;
 import org.jboss.web.tomcat.service.session.JBossCacheManager;
-import org.jboss.web.tomcat.service.session.JBossCacheService;
+import org.jboss.web.tomcat.service.session.distributedcache.impl.jbc2.JBossCacheService;
 
 /**
  * Tests of handling of ClusteredSession.maxUnreplicatedInterval.  This base
@@ -104,6 +104,8 @@
    {
       super.tearDown();
       
+      SessionTestUtil.clearDistributedCacheManagerFactory();
+      
       for (JBossCacheManager manager : managers)      
          manager.stop();
       

Modified: trunk/testsuite/src/main/org/jboss/test/cluster/defaultcfg/simpleweb/test/SessionCountUnitTestCase.java
===================================================================
--- trunk/testsuite/src/main/org/jboss/test/cluster/defaultcfg/simpleweb/test/SessionCountUnitTestCase.java	2008-08-26 16:10:33 UTC (rev 77495)
+++ trunk/testsuite/src/main/org/jboss/test/cluster/defaultcfg/simpleweb/test/SessionCountUnitTestCase.java	2008-08-26 16:41:17 UTC (rev 77496)
@@ -89,6 +89,8 @@
       }
       finally
       {      
+         SessionTestUtil.clearDistributedCacheManagerFactory();
+         
          // Restore any system properties we set in setUp
          if (jgroupsSupport != null)
          {
@@ -658,7 +660,7 @@
       ++testCount;
       String passDir = getPassivationDir(testCount, 1);
       JBossCacheManager jbcm = SessionTestUtil.createManager("test" + testCount, 300, true, passDir, false, false, null, caches);
-      PojoCache cache = jbcm.getPojoCache();
+      PojoCache cache = SessionTestUtil.getDistributedCacheManagerFactoryPojoCache();
       
       JBossWebMetaData webMetaData = SessionTestUtil.createWebMetaData(2, true, 3, 1);
       jbcm.init("test.war", webMetaData);
@@ -757,7 +759,7 @@
       ++testCount;
       String passDir = getPassivationDir(testCount, 1);
       JBossCacheManager jbcm = SessionTestUtil.createManager("test" + testCount, 300, false, passDir, totalReplication, marshalling, null, caches);
-      PojoCache cache = jbcm.getPojoCache();
+      PojoCache cache = SessionTestUtil.getDistributedCacheManagerFactoryPojoCache();
       
       JBossWebMetaData webMetaData = SessionTestUtil.createWebMetaData(2, true, 30, 1);
       jbcm.init("test.war", webMetaData);
@@ -771,7 +773,7 @@
       
       passDir = getPassivationDir(testCount, 2);
       JBossCacheManager jbcm1 = SessionTestUtil.createManager("test" + testCount, 300, false, passDir, totalReplication, marshalling, null, caches);
-      PojoCache cache1 = jbcm1.getPojoCache();
+      PojoCache cache1 = SessionTestUtil.getDistributedCacheManagerFactoryPojoCache();
       
       jbcm1.init("test.war", webMetaData);
       

Modified: trunk/testsuite/src/main/org/jboss/test/cluster/defaultcfg/simpleweb/test/SessionExpirationUnitTestCase.java
===================================================================
--- trunk/testsuite/src/main/org/jboss/test/cluster/defaultcfg/simpleweb/test/SessionExpirationUnitTestCase.java	2008-08-26 16:10:33 UTC (rev 77495)
+++ trunk/testsuite/src/main/org/jboss/test/cluster/defaultcfg/simpleweb/test/SessionExpirationUnitTestCase.java	2008-08-26 16:41:17 UTC (rev 77496)
@@ -79,7 +79,9 @@
          super.tearDown();
       }
       finally
-      {
+      {         
+         SessionTestUtil.clearDistributedCacheManagerFactory();
+         
          // Restore any system properties we set in setUp
          if (jgroupsSupport != null)
          {

Modified: trunk/testsuite/src/main/org/jboss/test/cluster/testutil/SessionTestUtil.java
===================================================================
--- trunk/testsuite/src/main/org/jboss/test/cluster/testutil/SessionTestUtil.java	2008-08-26 16:10:33 UTC (rev 77495)
+++ trunk/testsuite/src/main/org/jboss/test/cluster/testutil/SessionTestUtil.java	2008-08-26 16:41:17 UTC (rev 77496)
@@ -64,6 +64,9 @@
 import org.jboss.test.cluster.web.mocks.RequestHandler;
 import org.jboss.test.cluster.web.mocks.RequestHandlerValve;
 import org.jboss.web.tomcat.service.session.JBossCacheManager;
+import org.jboss.web.tomcat.service.session.distributedcache.impl.DistributedCacheManagerFactoryImpl;
+import org.jboss.web.tomcat.service.session.distributedcache.spi.ClusteringNotSupportedException;
+import org.jboss.web.tomcat.service.session.distributedcache.spi.DistributedCacheManagerFactoryFactory;
 
 /**
  * Utilities for session testing.
@@ -76,7 +79,18 @@
    private static final String[] STRING_ONLY_TYPES = { String.class.getName() };
    private static final String[] STRING_BOOLEAN_TYPES = { String.class.getName(), boolean.class.getName() };
    private static final String CONFIG_LOCATION = "cluster/http/jboss-web-test-service.xml";
-   
+   private static DistributedCacheManagerFactoryImpl distributedManagerFactory;
+   static
+   {
+      try
+      {
+         distributedManagerFactory = (DistributedCacheManagerFactoryImpl) DistributedCacheManagerFactoryFactory.getInstance().getDistributedCacheManagerFactory();
+      }
+      catch (ClusteringNotSupportedException e)
+      {
+         e.printStackTrace();
+      }
+   }
    public static JBossCacheManager createManager(String warName, int maxInactiveInterval, 
                                                  boolean local, String passivationDir, 
                                                  boolean totalReplication, boolean marshalling, 
@@ -91,7 +105,11 @@
                                                  PojoCache cache, 
                                                  String jvmRoute)
    {
-      JBossCacheManager jbcm = new JBossCacheManager(cache);
+      if (distributedManagerFactory == null)
+         throw new IllegalStateException("Failed to initialize distributedManagerFactory");
+      
+      distributedManagerFactory.setPojoCache(cache);
+      JBossCacheManager jbcm = new JBossCacheManager(distributedManagerFactory);
       jbcm.setSnapshotMode(SnapshotMode.INSTANT);
       
       MockEngine engine = new MockEngine();
@@ -108,10 +126,8 @@
       // the container's setting will override ours
       // Can't just set the container as their config is per minute not per second
       jbcm.setMaxInactiveInterval(maxInactiveInterval);
-      
-      
    
-   return jbcm;      
+      return jbcm;      
    }
    
    public static PojoCache createCache(boolean local, String passivationDir, 
@@ -157,6 +173,21 @@
       return config;
    } 
    
+   public static PojoCache getDistributedCacheManagerFactoryPojoCache()
+   {
+      return distributedManagerFactory.getPojoCache();
+   } 
+   
+   public static Cache getDistributedCacheManagerFactoryPlainCache()
+   {
+      return distributedManagerFactory.getPlainCache();
+   }
+   
+   public static void clearDistributedCacheManagerFactory()
+   {
+      distributedManagerFactory.clearCaches();
+   }
+   
    public static JBossWebMetaData createWebMetaData(int maxSessions)
    {
       return createWebMetaData(ReplicationGranularity.SESSION,

Modified: trunk/testsuite/src/main/org/jboss/test/cluster/web/CacheHelper.java
===================================================================
--- trunk/testsuite/src/main/org/jboss/test/cluster/web/CacheHelper.java	2008-08-26 16:10:33 UTC (rev 77495)
+++ trunk/testsuite/src/main/org/jboss/test/cluster/web/CacheHelper.java	2008-08-26 16:41:17 UTC (rev 77496)
@@ -40,11 +40,14 @@
 import org.jboss.ha.framework.server.PojoCacheManagerLocator;
 import org.jboss.mx.util.ObjectNameFactory;
 import org.jboss.system.ServiceMBeanSupport;
-import org.jboss.web.tomcat.service.session.JBossCacheService;
 
 /**
  * Helper class to locate and invoke methods on the cache mbeans used by JBossWeb.
  *
+ * TODO. Update the DistributedCacheManager SPI to provide the data we use
+ * here and use a factory to create the SPI impl rather than directly accessing
+ * the cache.
+ * 
  * @author Ben Wang  Date: Aug 16, 2005
  * @author Brian Stansberry
  * 
@@ -60,7 +63,7 @@
    public static final ObjectName OBJECT_NAME = 
       ObjectNameFactory.create("jboss.test:service=WebTestCacheHelper");
    
-   private static final String VERSION_KEY = JBossCacheService.VERSION_KEY;   
+   private static final String VERSION_KEY = "V";   
    
    private String cacheConfigName;
    private Cache cache;

Modified: trunk/testsuite/src/main/org/jboss/test/cluster/web/jvmroute/MockJBossManager.java
===================================================================
--- trunk/testsuite/src/main/org/jboss/test/cluster/web/jvmroute/MockJBossManager.java	2008-08-26 16:10:33 UTC (rev 77495)
+++ trunk/testsuite/src/main/org/jboss/test/cluster/web/jvmroute/MockJBossManager.java	2008-08-26 16:41:17 UTC (rev 77496)
@@ -32,7 +32,7 @@
 import org.jboss.metadata.web.jboss.JBossWebMetaData;
 import org.jboss.metadata.web.jboss.ReplicationTrigger;
 import org.jboss.web.tomcat.service.session.AbstractJBossManager;
-import org.jboss.web.tomcat.service.session.ClusteringNotSupportedException;
+import org.jboss.web.tomcat.service.session.distributedcache.spi.ClusteringNotSupportedException;
 
 /**
  * @author Brian Stansberry

Modified: trunk/tomcat/src/main/org/jboss/web/tomcat/service/deployers/TomcatDeployment.java
===================================================================
--- trunk/tomcat/src/main/org/jboss/web/tomcat/service/deployers/TomcatDeployment.java	2008-08-26 16:10:33 UTC (rev 77495)
+++ trunk/tomcat/src/main/org/jboss/web/tomcat/service/deployers/TomcatDeployment.java	2008-08-26 16:41:17 UTC (rev 77496)
@@ -72,7 +72,7 @@
 import org.jboss.web.tomcat.service.TomcatInjectionContainer;
 import org.jboss.web.tomcat.service.WebCtxLoader;
 import org.jboss.web.tomcat.service.session.AbstractJBossManager;
-import org.jboss.web.tomcat.service.session.ClusteringNotSupportedException;
+import org.jboss.web.tomcat.service.session.distributedcache.spi.ClusteringNotSupportedException;
 import org.jboss.wsf.spi.deployment.UnifiedVirtualFile;
 import org.omg.CORBA.ORB;
 

Modified: trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/AbstractJBossManager.java
===================================================================
--- trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/AbstractJBossManager.java	2008-08-26 16:10:33 UTC (rev 77495)
+++ trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/AbstractJBossManager.java	2008-08-26 16:41:17 UTC (rev 77496)
@@ -26,6 +26,7 @@
 import org.apache.catalina.Manager;
 import org.apache.catalina.Session;
 import org.jboss.metadata.web.jboss.JBossWebMetaData;
+import org.jboss.web.tomcat.service.session.distributedcache.spi.ClusteringNotSupportedException;
 
 /** Common interface for the http session replication managers.
  * 

Deleted: trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/CacheListener.java
===================================================================
--- trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/CacheListener.java	2008-08-26 16:10:33 UTC (rev 77495)
+++ trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/CacheListener.java	2008-08-26 16:41:17 UTC (rev 77496)
@@ -1,187 +0,0 @@
-/*
- * JBoss, Home of Professional Open Source.
- * Copyright 2008, Red Hat Middleware LLC, 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.web.tomcat.service.session;
-
-import java.util.Map;
-
-import org.jboss.cache.Fqn;
-import org.jboss.cache.notifications.annotation.NodeModified;
-import org.jboss.cache.notifications.annotation.NodeRemoved;
-import org.jboss.cache.notifications.event.NodeModifiedEvent;
-import org.jboss.cache.notifications.event.NodeRemovedEvent;
-import org.jboss.logging.Logger;
-import org.jboss.metadata.web.jboss.ReplicationGranularity;
-import org.jboss.web.tomcat.service.session.ClusteredSession.SessionMetadata;
-import org.jboss.web.tomcat.service.session.ClusteredSession.SessionTimestamp;
-
-/**
- * Listens for removals and modifications in the cache, notifying the 
- * session manager of significant events.
- * 
- * @author Brian Stansberry
- */
- at org.jboss.cache.notifications.annotation.CacheListener
-public class CacheListener extends CacheListenerBase
-{
-   // Element within an FQN that is the root of a Pojo attribute map
-   private static final int POJO_ATTRIBUTE_FQN_INDEX = SESSION_ID_FQN_INDEX + 1;
-   // Element within an FQN that is the root of an individual Pojo attribute
-   private static final int POJO_KEY_FQN_INDEX = POJO_ATTRIBUTE_FQN_INDEX + 1;
-   // Element within an FQN that is the root of a session's internal pojo storage area
-   private static final int POJO_INTERNAL_FQN_INDEX = SESSION_ID_FQN_INDEX + 1;
-   // Minimum size of an FQN that is below the root of a session's internal pojo storage area
-   private static final int POJO_INTERNAL_FQN_SIZE = POJO_INTERNAL_FQN_INDEX + 1;
-   private static Logger log_ = Logger.getLogger(CacheListener.class);
-   private boolean fieldBased_;
-
-   CacheListener(JBossCacheWrapper wrapper, JBossCacheManager manager, String hostname, String webapp)
-   {      
-      super(manager, hostname, webapp);
-      fieldBased_ = (manager_.getReplicationGranularity() == ReplicationGranularity.FIELD);
-   }
-
-   // --------------- CacheListener methods ------------------------------------
-
-   @NodeRemoved
-   public void nodeRemoved(NodeRemovedEvent event)
-   {      
-      if (event.isPre())
-         return;
-      
-      boolean local = event.isOriginLocal();
-      if (!fieldBased_ && local)
-         return;
-      
-      Fqn fqn = event.getFqn();
-      boolean isBuddy = isBuddyFqn(fqn);
-      
-      if (!local 
-            && isFqnSessionRootSized(fqn, isBuddy) 
-            && isFqnForOurWebapp(fqn, isBuddy))
-      {
-         // A session has been invalidated from another node;
-         // need to inform manager
-         String sessId = getIdFromFqn(fqn, isBuddy);
-         manager_.notifyRemoteInvalidation(sessId);
-      }
-      else if (local && !isBuddy
-                  && isPossibleInternalPojoFqn(fqn) 
-                  && isFqnForOurWebapp(fqn, isBuddy))
-      {
-         // One of our sessions' pojos is modified; need to inform
-         // the manager so it can mark the session dirty
-         String sessId = getIdFromFqn(fqn, isBuddy);
-         manager_.notifyLocalAttributeModification(sessId);
-      }
-   }
-   
-   @NodeModified
-   public void nodeModified(NodeModifiedEvent event)
-   {      
-      if (event.isPre())
-         return;
-      
-      boolean local = event.isOriginLocal();
-      if (!fieldBased_ && local)
-         return;
-      
-      Fqn fqn = event.getFqn();
-      boolean isBuddy = isBuddyFqn(fqn);      
-      
-      if (isFqnSessionRootSized(fqn, isBuddy)
-             &&isFqnForOurWebapp(fqn, isBuddy))
-      {
-         // Query if we have version value in the distributed cache. 
-         // If we have a version value, compare the version and invalidate if necessary.
-         Map data = event.getData();
-         Integer version = (Integer) data.get(JBossCacheService.VERSION_KEY);
-         if(version != null)
-         {
-            String realId = getIdFromFqn(fqn, isBuddy);
-            String owner = isBuddy ? getBuddyOwner(fqn) : null;
-            // Notify the manager that a session has been updated
-            boolean updated = manager_.sessionChangedInDistributedCache(realId, owner, 
-                                               version.intValue(), 
-                                               (SessionTimestamp) data.get(JBossCacheService.TIMESTAMP_KEY), 
-                                               (SessionMetadata) data.get(JBossCacheService.METADATA_KEY));
-            if (!updated && !isBuddy)
-            {
-               log_.warn("Possible concurrency problem: Replicated version id " + 
-                         version + " matches in-memory version for session " + realId); 
-            }
-            /*else 
-            {
-               We have a local session but got a modification for the buddy tree.
-               This means another node is in the process of taking over the session;
-               we don't worry about it
-            }
-             */
-         }
-         else
-         {
-            log_.warn("No VERSION_KEY attribute found in " + fqn);
-         }
-      }
-      else if (local && !isBuddy
-            && isPossibleInternalPojoFqn(fqn) 
-            && isFqnForOurWebapp(fqn, isBuddy))
-      {
-         // One of our sessions' pojos is modified; need to inform
-         // the manager so it can mark the session dirty
-         String sessId = getIdFromFqn(fqn, isBuddy);
-         manager_.notifyLocalAttributeModification(sessId);
-      }
-   }
-   
-   public static String getPojoKeyFromFqn(Fqn fqn, boolean isBuddy)
-   {
-      return (String) fqn.get(isBuddy ? BUDDY_BACKUP_ROOT_OWNER_SIZE + POJO_KEY_FQN_INDEX: POJO_KEY_FQN_INDEX);
-   }
-   
-   /**
-    * Check if the fqn is big enough to be in the internal pojo area but
-    * isn't in the regular attribute area.
-    * 
-    * Structure in the cache is:
-    * 
-    * /JSESSION
-    * ++ /hostname
-    * ++++ /contextpath
-    * ++++++ /sessionid
-    * ++++++++ /ATTRIBUTE
-    * ++++++++ /_JBossInternal_
-    * ++++++++++ etc etc
-    * 
-    * If the Fqn size is big enough to be "etc etc" or lower, but the 4th
-    * level is "ATTRIBUTE", it must be under _JBossInternal_. We discriminate
-    * based on != ATTRIBUTE to avoid having to code to the internal PojoCache
-    * _JBossInternal_ name.
-    * 
-    * @param fqn
-    * @return
-    */
-   public static boolean isPossibleInternalPojoFqn(Fqn fqn)
-   {      
-      return (fqn.size() > POJO_INTERNAL_FQN_SIZE 
-            && JBossCacheService.ATTRIBUTE.equals(fqn.get(POJO_INTERNAL_FQN_INDEX)) == false);
-   }
-}

Deleted: trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/CacheListenerBase.java
===================================================================
--- trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/CacheListenerBase.java	2008-08-26 16:10:33 UTC (rev 77495)
+++ trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/CacheListenerBase.java	2008-08-26 16:41:17 UTC (rev 77496)
@@ -1,104 +0,0 @@
-/*
- * JBoss, Home of Professional Open Source.
- * Copyright 2008, Red Hat Middleware LLC, 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.web.tomcat.service.session;
-
-import org.jboss.cache.Fqn;
-import org.jboss.cache.buddyreplication.BuddyManager;
-
-/**
- * Base class for JBC cache listener impls.
- * 
- * @author Brian Stansberry
- */
-public class CacheListenerBase
-{
-
-   private static final int JSESSION_FQN_INDEX = 0;
-   private static final int HOSTNAME_FQN_INDEX = 1;
-   private static final int WEBAPP_FQN_INDEX = 2;
-   protected static final int SESSION_ID_FQN_INDEX = 3;
-   private static final int SESSION_FQN_SIZE = SESSION_ID_FQN_INDEX + 1;
-   private static final int BUDDY_BACKUP_ROOT_OWNER_INDEX = BuddyManager.BUDDY_BACKUP_SUBTREE_FQN.size();
-   protected static final int BUDDY_BACKUP_ROOT_OWNER_SIZE = BUDDY_BACKUP_ROOT_OWNER_INDEX + 1;
-   protected JBossCacheManager manager_;
-   private String webapp_;
-   private String hostname_;
-
-   CacheListenerBase(JBossCacheManager manager, String hostname, String webapp)
-   {      
-      manager_ = manager;
-      hostname_ = hostname;
-      webapp_ =  webapp;
-   }
-
-   protected boolean isFqnForOurWebapp(Fqn fqn, boolean isBuddy)
-   {
-      try
-      {
-         if (webapp_.equals(fqn.get(isBuddy ? BUDDY_BACKUP_ROOT_OWNER_SIZE + WEBAPP_FQN_INDEX : WEBAPP_FQN_INDEX))
-               && hostname_.equals(fqn.get(isBuddy ? BUDDY_BACKUP_ROOT_OWNER_SIZE + HOSTNAME_FQN_INDEX : HOSTNAME_FQN_INDEX))
-               && JBossCacheService.SESSION.equals(fqn.get(isBuddy ? BUDDY_BACKUP_ROOT_OWNER_SIZE + JSESSION_FQN_INDEX : JSESSION_FQN_INDEX)))
-            return true;
-      }
-      catch (IndexOutOfBoundsException e)
-      {
-         // can't be ours; too small; just fall through
-      }
-   
-      return false;
-   }
-
-   public static boolean isFqnSessionRootSized(Fqn fqn, boolean isBuddy)
-   {
-      return fqn.size() == (isBuddy ? BUDDY_BACKUP_ROOT_OWNER_SIZE + SESSION_FQN_SIZE : SESSION_FQN_SIZE);
-   }
-
-   public static String getIdFromFqn(Fqn fqn, boolean isBuddy)
-   {
-      return (String)fqn.get(isBuddy ? BUDDY_BACKUP_ROOT_OWNER_SIZE + SESSION_ID_FQN_INDEX : SESSION_ID_FQN_INDEX);
-   }
-
-   public static boolean isBuddyFqn(Fqn fqn)
-   {
-      try
-      {
-         return BuddyManager.BUDDY_BACKUP_SUBTREE.equals(fqn.get(0));
-      }
-      catch (IndexOutOfBoundsException e)
-      {
-         // Can only happen if fqn is ROOT, and we shouldn't get
-         // notifications for ROOT.
-         // If it does, just means it's not a buddy
-         return false;
-      }      
-   }
-
-   /**
-    * Extracts the owner portion of an buddy subtree Fqn.
-    * 
-    * @param fqn An Fqn that is a child of the buddy backup root node.
-    */
-   public static String getBuddyOwner(Fqn fqn)
-   {
-      return (String) fqn.get(BUDDY_BACKUP_ROOT_OWNER_INDEX);     
-   }
-}
\ No newline at end of file

Modified: trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/ClusteredSession.java
===================================================================
--- trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/ClusteredSession.java	2008-08-26 16:10:33 UTC (rev 77495)
+++ trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/ClusteredSession.java	2008-08-26 16:41:17 UTC (rev 77496)
@@ -46,6 +46,8 @@
 import org.apache.catalina.util.StringManager;
 import org.jboss.logging.Logger;
 import org.jboss.metadata.web.jboss.ReplicationTrigger;
+import org.jboss.web.tomcat.service.session.distributedcache.spi.DistributableSessionMetadata;
+import org.jboss.web.tomcat.service.session.distributedcache.spi.DistributableSessionTimestamp;
 
 /**
  * Abstract base class for session clustering based on StandardSession. Different session
@@ -108,14 +110,14 @@
     * we can store it in JBoss Cache w/o concern that a transaction
     * rollback will revert the cached ref to an older object.
     */
-   protected transient SessionTimestamp timestamp = new SessionTimestamp();
+   protected transient DistributableSessionTimestamp timestamp = new DistributableSessionTimestamp();
    
    /** 
     * Object wrapping other metadata for this session. Create once and mutate so
     * we can store it in JBoss Cache w/o concern that a transaction
     * rollback will revert the cached ref to an older object.
     */
-   protected transient SessionMetadata metadata = new SessionMetadata();
+   protected transient DistributableSessionMetadata metadata = new DistributableSessionMetadata();
    
    /**
     * The last version that was passed to {@link #setDistributedVersion} or
@@ -359,20 +361,20 @@
       return exceeds;
    }
    
-   public SessionTimestamp getSessionTimestamp()
+   public DistributableSessionTimestamp getSessionTimestamp()
    {
-      this.timestamp.timestamp = this.thisAccessedTime;
+      this.timestamp.setTimestamp(this.thisAccessedTime);
       return this.timestamp;
    }
    
-   public SessionMetadata getSessionMetadata()
+   public DistributableSessionMetadata getSessionMetadata()
    {
-      this.metadata.id = id;
-      this.metadata.creationTime = creationTime;
-      this.metadata.maxInactiveInterval = maxInactiveInterval;
-      this.metadata.invalidationPolicy = invalidationPolicy;
-      this.metadata.isNew = isNew;
-      this.metadata.isValid = isValid;
+      this.metadata.setId(id);
+      this.metadata.setCreationTime(creationTime);
+      this.metadata.setMaxInactiveInterval(maxInactiveInterval);
+      this.metadata.setInvalidationPolicy(invalidationPolicy);
+      this.metadata.setNew(isNew);
+      this.metadata.setValid(isValid);
       
       return this.metadata;
    }
@@ -1080,8 +1082,8 @@
       return excluded;      
    }
    
-   protected void update(Integer version, SessionTimestamp timestamp, 
-                         SessionMetadata metadata, Map attributes)
+   public void update(Integer version, DistributableSessionTimestamp timestamp, 
+                      DistributableSessionMetadata metadata, Map attributes)
    {
       assert version != null : "version is null";
       assert timestamp != null : "timestamp is null";
@@ -1089,15 +1091,15 @@
       
       this.version = version.intValue();
       
-      this.lastAccessedTime = this.thisAccessedTime = timestamp.timestamp;
+      this.lastAccessedTime = this.thisAccessedTime = timestamp.getTimestamp();
       this.timestamp = timestamp;
       
-      this.id = metadata.id;
-      this.creationTime = metadata.creationTime;
-      this.maxInactiveInterval = metadata.maxInactiveInterval;
-      this.isNew = metadata.isNew;
-      this.isValid = metadata.isValid;
-      this.invalidationPolicy = metadata.invalidationPolicy;
+      this.id = metadata.getId();
+      this.creationTime = metadata.getCreationTime();
+      this.maxInactiveInterval = metadata.getMaxInactiveInterval();
+      this.isNew = metadata.isNew();
+      this.isValid = metadata.isValid();
+      this.invalidationPolicy = metadata.getInvalidationPolicy();
       this.metadata = metadata;
       
       // Get our id without any jvmRoute appended
@@ -1349,40 +1351,5 @@
             || (maxUnreplicatedInterval > 0 && maxInactiveInterval >= 0 
                   && maxUnreplicatedInterval > (maxInactiveInterval * 1000)));
    }
-   
-   /**
-    * Encapsulates the "thisAccessedTime" timestamp. The wrapped timestamp long
-    * can be mutated, allowing the same object to always be stored in 
-    * JBoss Cache. Always storing the same object avoids an earlier, no
-    * longer accurate, object being reverted into the cache during a 
-    * transaction rollback.
-    */
-   public static class SessionTimestamp implements Serializable
-   {
-      /** The serialVersionUID */
-      private static final long serialVersionUID = 2317131983552715467L;
-      
-      public long timestamp = 0L;
-   }
-   
-   /**
-    * Encapsulates the replicated metadata for a session. The wrapped data
-    * can be mutated, allowing the same object to always be stored in 
-    * JBoss Cache. Always storing the same object avoids an earlier, no
-    * longer accurate, object being reverted into the cache during a 
-    * transaction rollback.
-    */   
-   public static class SessionMetadata implements Serializable
-   {
-    /** The serialVersionUID */
-      private static final long serialVersionUID = -6845914023373746866L;
-      
-      public String id;
-      public long creationTime;
-      public int maxInactiveInterval;
-      public boolean isNew;
-      public boolean isValid;
-      public ReplicationTrigger invalidationPolicy;      
-   }
 
 }

Modified: trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/ClusteredSessionValve.java
===================================================================
--- trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/ClusteredSessionValve.java	2008-08-26 16:10:33 UTC (rev 77495)
+++ trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/ClusteredSessionValve.java	2008-08-26 16:41:17 UTC (rev 77496)
@@ -26,8 +26,6 @@
 import java.util.Map;
 
 import javax.servlet.ServletException;
-import javax.transaction.Transaction;
-import javax.transaction.TransactionManager;
 
 import org.apache.catalina.Lifecycle;
 import org.apache.catalina.LifecycleException;
@@ -37,6 +35,7 @@
 import org.apache.catalina.connector.Response;
 import org.apache.catalina.util.LifecycleSupport;
 import org.apache.catalina.valves.ValveBase;
+import org.jboss.web.tomcat.service.session.distributedcache.spi.BatchingManager;
 
 /**
  * This Valve detects all sessions that were used in a request. All sessions are given to a snapshot
@@ -59,12 +58,12 @@
 
    private final Manager manager;
    
-   private final TransactionManager tm;
+   private final BatchingManager tm;
    
    /**
     * Create a new Valve.
     */
-   public ClusteredSessionValve(Manager manager, TransactionManager tm)
+   public ClusteredSessionValve(Manager manager, BatchingManager tm)
    {
       assert manager != null : "manager is null";
       
@@ -94,7 +93,7 @@
       // for any clustering code that has no direct access to these objects
       SessionReplicationContext.enterWebapp(request, response, true);
       
-      Transaction tx = startBatchTransaction();
+      boolean startedBatch = startBatchTransaction();
       try
       {  
          // Workaround to JBAS-5735. Ensure we get the session from the manager
@@ -138,7 +137,10 @@
          finally
          {
             SessionReplicationContext.finishCacheActivity();
-            commitBatchTransaction(tx);
+            if (startedBatch)
+            {
+               tm.endBatch();
+            }
          }
          
       }
@@ -170,15 +172,15 @@
       support.fireLifecycleEvent(STOP_EVENT, this);
    }
 
-   private Transaction startBatchTransaction() throws ServletException
+   private boolean startBatchTransaction() throws ServletException
    {
-      Transaction result = null;
+      boolean started = false;
       try
       {
-         if (this.tm != null && this.tm.getTransaction() == null)
+         if (this.tm != null && this.tm.isBatchInProgress() == false)
          {
-            this.tm.begin();
-            result = this.tm.getTransaction();
+            this.tm.startBatch();
+            started = true;
          }
       }
       catch (RuntimeException re)
@@ -190,26 +192,7 @@
          throw new ServletException("Failed to initiate batch replication transaction", e);
       }
       
-      return result;
+      return started;
    }
 
-   private void commitBatchTransaction(Transaction tx) throws ServletException
-   {
-      if (tx != null)
-      {
-         try
-         {
-            tx.commit();
-         }
-         catch (RuntimeException re)
-         {
-            throw re;
-         }
-         catch (Exception e)
-         {
-            throw new ServletException("Failed to commit batch replication transaction", e);
-         }
-      }      
-   }
-
 }

Deleted: trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/ClusteringNotSupportedException.java
===================================================================
--- trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/ClusteringNotSupportedException.java	2008-08-26 16:10:33 UTC (rev 77495)
+++ trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/ClusteringNotSupportedException.java	2008-08-26 16:41:17 UTC (rev 77496)
@@ -1,46 +0,0 @@
-/*
-* JBoss, Home of Professional Open Source
-* Copyright 2005, JBoss Inc., and individual contributors as indicated
-* by the @authors tag. See the copyright.txt 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.web.tomcat.service.session;
-
-
-/**
- * This exception is thrown when the clustered HTTPSession-service
- * is not found
- *
- * @author Thomas Peuss <jboss at peuss.de>
- * @version $Revision: 56007 $
- */
-public class ClusteringNotSupportedException extends Exception
-{
-   /** The serialVersionUID */
-   private static final long serialVersionUID = -2365801349845482947L;
-
-   public ClusteringNotSupportedException(String message)
-   {
-      super(message);
-   }
-   
-   public ClusteringNotSupportedException(String message, Throwable cause)
-   {
-      super(message, cause);
-   }
-}

Modified: trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/FieldBasedClusteredSession.java
===================================================================
--- trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/FieldBasedClusteredSession.java	2008-08-26 16:10:33 UTC (rev 77495)
+++ trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/FieldBasedClusteredSession.java	2008-08-26 16:41:17 UTC (rev 77496)
@@ -32,6 +32,7 @@
 import javax.servlet.http.HttpSessionActivationListener;
 
 import org.jboss.aop.Advised;
+import org.jboss.web.tomcat.service.session.distributedcache.spi.FieldBasedDistributedCacheManager;
 
 /**
  * <p>
@@ -79,7 +80,7 @@
    protected static final String info = "FieldBasedClusteredSession/1.0";
    
    protected transient Map attributes_ = Collections.synchronizedMap(new HashMap());
-   protected transient FieldBasedJBossCacheService fieldProxy_;
+   protected transient FieldBasedDistributedCacheManager fieldProxy_;
    protected transient boolean needRegion_ = true;
    
    public FieldBasedClusteredSession(JBossCacheManager manager)
@@ -176,7 +177,7 @@
    {
       if (fieldProxy_ == null)
       {
-         fieldProxy_ = ((JBossCacheManager) manager).getPojoCacheService();
+         fieldProxy_ = ((JBossCacheManager) manager).getFieldBasedDistributedCacheManager();
 
          // still null???
          if (fieldProxy_ == null)

Deleted: trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/FieldBasedJBossCacheService.java
===================================================================
--- trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/FieldBasedJBossCacheService.java	2008-08-26 16:10:33 UTC (rev 77495)
+++ trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/FieldBasedJBossCacheService.java	2008-08-26 16:41:17 UTC (rev 77496)
@@ -1,241 +0,0 @@
-/*
- * JBoss, Home of Professional Open Source.
- * Copyright 2008, Red Hat Middleware LLC, 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.web.tomcat.service.session;
-
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Set;
-import java.util.StringTokenizer;
-
-import org.jboss.cache.CacheException;
-import org.jboss.cache.Fqn;
-import org.jboss.cache.pojo.PojoCache;
-
-/**
- * Adds PojoCache-specific capabilities to JBossCacheService.
- * 
- * @author Brian Stansberry
- *
- */
-public class FieldBasedJBossCacheService extends JBossCacheService
-{
-   private final PojoCache pojoCache_;
-   
-   /**
-    * Create a new FieldBasedJBossCacheService.
-    * 
-    * @param cache
-    */
-   public FieldBasedJBossCacheService(PojoCache cache)
-   {
-      super(cache.getCache());
-      this.pojoCache_ = cache;
-      setCache(pojoCache_.getCache());
-   }
-   
-   private Fqn getFieldFqn(String id, String key)
-   {
-      return getFieldFqn(hostName_, webAppPath_, id, key);
-   }
-
-
-   private static void breakKeys(String key, List list)
-   {
-      StringTokenizer token = new StringTokenizer(key, FQN_DELIMITER);
-      while(token.hasMoreTokens())
-      {
-         list.add(token.nextToken());
-      }
-   }
-   public static Fqn getFieldFqn(String hostName, String contextPath, String sessionId, String attributeKey)
-   {
-      List list = new ArrayList(6);
-      list.add(SESSION);
-      list.add(hostName);
-      list.add(contextPath);
-      list.add(sessionId);
-      list.add(ATTRIBUTE);
-      // Guard against string with delimiter.
-      breakKeys(attributeKey, list);
-      return new Fqn(list);      
-   }
-
-   /**
-    * store the pojo instance in the cache. Note that this is for the aop cache.
-    * THe pojo needs to be "aspectized".
-    * 
-    * @param realId the session id with any jvmRoute removed
-    * @param key    the attribute key
-    * @param pojo
-    * @param createRegion TODO
-    */
-   public Object setPojo(String realId, String key, Object pojo, JBossCacheClusteredSession session)
-   {
-      if(log_.isTraceEnabled())
-      {
-         log_.trace("setPojo(): session id: " + realId + " key: " + key + 
-                    " object: " + pojo.toString());
-      }      
-      
-      if (session.needRegionForSession())
-      {
-         Fqn sessionRegion = getSessionFqn(realId);
-         getCache().getRegion(sessionRegion, true);
-         session.createdRegionForSession();
-         if (log_.isTraceEnabled())
-         {
-            log_.trace("Created region for session at " + sessionRegion);
-         }         
-      }
-      
-      // Construct the fqn.
-      Fqn fqn = getFieldFqn(realId, key);
-      try {
-         // Ignore any cache notifications that our own work generates 
-         SessionReplicationContext.startCacheActivity();            
-         return pojoCache_.attach(fqn.toString(), pojo);
-      } catch (CacheException e) {
-         throw new RuntimeException("JBossCacheService: exception occurred in cache setPojo ... ", e);
-      }
-      finally {
-         SessionReplicationContext.finishCacheActivity();
-      }
-   }
-
-   /**
-    * Remove pojo from the underlying cache store.
-    * @param realId the session id with any jvmRoute removed
-    * @param key    the attribute key
-    * @return pojo that just removed. Null if there none.
-    */
-   public Object removePojo(String realId, String key)
-   {
-      if(log_.isTraceEnabled())
-      {
-         log_.trace("removePojo(): session id: " +realId + " key: " +key);
-      }
-      // Construct the fqn.
-      Fqn fqn = getFieldFqn(realId, key);
-      try {
-         // Ignore any cache notifications that our own work generates 
-         SessionReplicationContext.startCacheActivity();
-         return pojoCache_.detach(fqn.toString());
-      } catch (CacheException e) {
-         throw new RuntimeException("JBossCacheService: exception occurred in cache removePojo ... ", e);
-      }
-      finally {
-         SessionReplicationContext.finishCacheActivity();
-      }
-   }
-
-   /**
-    * Remove all the pojos from the underlying cache store locally 
-    * without replication.
-    * 
-    * @param realId the session id with any jvmRoute removed
-    */
-   public void removePojosLocal(String realId)
-   {
-      if(log_.isTraceEnabled())
-      {
-         log_.trace("removePojoLocal(): session id: " +realId);
-      }
-      // Construct the fqn.
-      Fqn fqn = getAttributeFqn(realId);
-      try {
-         // Ignore any cache notifications that our own work generates 
-         SessionReplicationContext.startCacheActivity();
-         cacheWrapper_.removeLocal(fqn);
-      }
-      finally {
-         SessionReplicationContext.finishCacheActivity();
-      }
-   }
-
-   /**
-    * Remove all the pojos from the underlying cache store locally 
-    * without replication.
-    * 
-    * @param realId the session id with any jvmRoute removed
-    */
-   public void removePojoLocal(String realId, String key)
-   {
-      if(log_.isTraceEnabled())
-      {
-         log_.trace("removePojoLocal(): session id: " + realId + " key: " +key);
-      }
-      // Construct the fqn.
-      Fqn fqn = getFieldFqn(realId, key);
-      try {
-         // Ignore any cache notifications that our own work generates 
-         SessionReplicationContext.startCacheActivity();
-         cacheWrapper_.removeLocal(fqn);
-      }
-      finally {
-         SessionReplicationContext.finishCacheActivity();
-      }
-   }
-   
-   public Set getPojoKeys(String realId)
-   {
-      Set keys = null;
-      Fqn fqn = getAttributeFqn(realId);
-      try
-      {
-         keys = getChildrenNames(fqn);
-      }
-      catch (CacheException e)
-      {
-         log_.error("getPojoKeys(): Exception getting keys for session " + realId, e);
-      }
-      
-      return keys;      
-   }
-   
-
-   /**
-    *
-    * @param realId the session id with any jvmRoute removed
-    * @param key    the attribute key
-    * @return Pojo that is associated with the attribute
-    */
-   public Object getPojo(String realId, String key)
-   {
-      Fqn fqn = getFieldFqn(realId, key);
-      if(log_.isTraceEnabled())
-      {
-         log_.trace("getPojo(): session id: " +realId + " key: " + key +
-                    " fqn: " + fqn);
-      }
-      
-      try 
-      {
-         return pojoCache_.find(fqn);
-      }
-      catch (CacheException e) 
-      {
-         throw new RuntimeException("JBossCacheService: exception occurred in cache find ... ", e);
-      }
-   }
-
-}

Modified: trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/JBossCacheCluster.java
===================================================================
--- trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/JBossCacheCluster.java	2008-08-26 16:10:33 UTC (rev 77495)
+++ trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/JBossCacheCluster.java	2008-08-26 16:41:17 UTC (rev 77496)
@@ -41,16 +41,13 @@
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
 import org.apache.tomcat.util.modeler.Registry;
-import org.jboss.cache.config.Configuration;
-import org.jboss.cache.pojo.PojoCacheFactory;
-import org.jboss.cache.pojo.jmx.PojoCacheJmxWrapper;
-import org.jboss.cache.pojo.jmx.PojoCacheJmxWrapperMBean;
-import org.jboss.cache.transaction.BatchModeTransactionManagerLookup;
 import org.jboss.metadata.web.jboss.ReplicationGranularity;
 import org.jboss.metadata.web.jboss.ReplicationTrigger;
 import org.jboss.metadata.web.jboss.SnapshotMode;
-import org.jboss.mx.util.MBeanProxyExt;
 import org.jboss.mx.util.MBeanServerLocator;
+import org.jboss.web.tomcat.service.session.distributedcache.spi.DistributedCacheManagerFactoryFactory;
+import org.jboss.web.tomcat.service.session.distributedcache.spi.TomcatClusterConfig;
+import org.jboss.web.tomcat.service.session.distributedcache.spi.TomcatClusterDistributedCacheManagerFactory;
 
 /**
  * A Tomcat <code>Cluster</code> implementation that uses a JBoss
@@ -64,7 +61,7 @@
  * @version $Revision: 61125 $
  */
 public class JBossCacheCluster
-   implements JBossCacheClusterMBean, Lifecycle 
+   implements TomcatClusterConfig, JBossCacheClusterMBean, Lifecycle 
 {
    //  -------------------------------------------------------  Static Fields
    
@@ -72,21 +69,6 @@
 
    public static Log log = LogFactory.getLog(JBossCacheCluster.class);
 
-   public static final String DEFAULT_CLUSTER_NAME = "Tomcat-Cluster";
-
-   /** TreeCache's isolation level */
-   public static final String DEFAULT_ISOLATION_LEVEL = "REPEATABLE_READ";
-   
-   /** TreeCache's cache mode */
-   public static final String DEFAULT_CACHE_MODE = "REPL_ASYNC";
-   
-   /** TreeCache's lock aquisition timeout */
-   public static final long DEFAULT_LOCK_TIMEOUT = 15000;
-   
-   /** TransactionManagerLookup implementation that the TreeCache should use. */
-   public static final String DEFAULT_TM_LOOKUP = 
-      BatchModeTransactionManagerLookup.class.getName();
-
    public static final String DEFAULT_CACHE_CONFIG_PATH = "conf/cluster-cache.xml";
    
    //  -------------------------------------------------------  Instance Fields
@@ -100,21 +82,18 @@
    /** Name under which we are registered in JMX */
    private ObjectName objectName = null;
    
+   /** The factory we use to create our DistributedCacheManager */
+   private TomcatClusterDistributedCacheManagerFactory factory = null;
+   
    /** Are we started? */
    private boolean started = false;
 
    /** The lifecycle event support for this component. */
    private LifecycleSupport lifecycle = new LifecycleSupport(this);
 
-   /** Our tree cache */
-   private PojoCacheJmxWrapperMBean pojoCache = null;
-   
    /** Name under which our TreeCache is registered in JMX */
    private String pojoCacheObjectName = "jboss.cache:service=TomcatClusteringCache";
    
-   /** Did we create the tree cache, or was it already registered in JMX? */
-   private boolean pojoCacheLocal = false;
-   
    /** Name of the tree cache's JGroups channel */
    private String clusterName = null;
    
@@ -539,9 +518,9 @@
       
       if (manager == null)
       {
-         if (pojoCache == null)
+         if (factory == null)
             throw new IllegalStateException("PojoCache not initialized");
-         manager = new JBossCacheManager(pojoCache.getPojoCache());
+         manager = new JBossCacheManager(factory);
       }
       
       manager.setDistributable(true);      
@@ -686,16 +665,13 @@
          }
          
          // Initialize the tree cache
-         PojoCacheJmxWrapperMBean cache = getTreeCache();
+         
+         factory = DistributedCacheManagerFactoryFactory.getInstance().getTomcatClusterDistributedCacheManagerFactory(this);
 
          registerMBeans();
-         
-         if (pojoCacheLocal)
-         {
-            cache.create();
-            cache.start();
-         }
 
+         factory.start();
+
          started = true;
          
          // Notify our interested LifecycleListeners
@@ -729,11 +705,14 @@
       // Notify our interested LifecycleListeners
       lifecycle.fireLifecycleEvent(BEFORE_STOP_EVENT, this);
       
-      if (pojoCacheLocal)
+      try
       {
-         pojoCache.stop();
-         pojoCache.destroy();
+         factory.stop();
       }
+      catch (Exception e)
+      {
+         throw new LifecycleException("Failed to stop DistributedCacheManagerFactory", e);
+      }
 
       started = false;
       // Notify our interested LifecycleListeners
@@ -767,67 +746,10 @@
    }
    
    // -------------------------------------------------------- Private Methods
- 
-   /**
-    * Gets our TreeCache, either from a local reference or the JMX
-    * server.  If one is not found, creates and configures it.
-    */
-   private PojoCacheJmxWrapperMBean getTreeCache() throws Exception
-   {
-      if (pojoCache == null) {
-         
-         MBeanServer server = getMBeanServer();
-         ObjectName objName = new ObjectName(pojoCacheObjectName);
-         if (server.isRegistered(objName))
-         {
-            // Get a proxy to the existing TreeCache
-            pojoCache = (PojoCacheJmxWrapperMBean) 
-                           MBeanProxyExt.create(PojoCacheJmxWrapperMBean.class, objName);
-         }
-         else
-         {
-            // See if there is an XML descriptor file to configure the cache
-            File configFile = getCacheConfigFile();
-            
-            if (configFile != null)
-            {
-               pojoCache = new PojoCacheJmxWrapper(PojoCacheFactory.createCache(configFile.getAbsolutePath(), false));
-               Configuration config = pojoCache.getConfiguration();
-                              
-               if (clusterName != null)
-               {
-                  // Override the XML config with the name provided in
-                  // server.xml.  Method setClusterName is specified in the
-                  // Cluster interface, otherwise we would not do this
-                  config.setClusterName(clusterName);
-               }
-            }
-            else
-            {
-               // User did not try to configure the cache.
-               // Configure it using defaults.  Only exception
-               // is the clusterName, which user can specify in server.xml.
-               Configuration config = new Configuration();
-               String channelName = (clusterName == null) ? DEFAULT_CLUSTER_NAME
-                                                          : clusterName;
-               config.setClusterName(channelName);
-               config.setIsolationLevel(DEFAULT_ISOLATION_LEVEL);
-               config.setCacheMode(DEFAULT_CACHE_MODE);
-               config.setLockAcquisitionTimeout(DEFAULT_LOCK_TIMEOUT);
-               config.setTransactionManagerLookupClass(DEFAULT_TM_LOOKUP);
-               
-               pojoCache = new PojoCacheJmxWrapper(PojoCacheFactory.createCache(config, false));
-            }
-            
-            pojoCacheLocal = true;
-         }
-      }
-      return pojoCache;
-   }   
    
    
 
-   private File getCacheConfigFile() throws FileNotFoundException
+   public File getCacheConfigFile() throws FileNotFoundException
    {
       boolean useDefault = (this.cacheConfigPath == null);
       String path = (useDefault) ? DEFAULT_CACHE_CONFIG_PATH : cacheConfigPath;
@@ -893,13 +815,6 @@
             server.registerMBean(this, objectName);
          }
 
-         if (pojoCacheLocal)
-         {
-            // Register the treeCache
-            ObjectName treeCacheName = new ObjectName(pojoCacheObjectName);
-            server.registerMBean(getTreeCache(), treeCacheName);
-         }
-
       }
       catch (Exception ex)
       {
@@ -919,10 +834,6 @@
             if (objectName != null) {
                mserver.unregisterMBean(objectName);
             }
-            if (pojoCacheLocal)
-            {
-               mserver.unregisterMBean(new ObjectName(pojoCacheObjectName));
-            }
          }
          catch (Exception e)
          {

Modified: trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/JBossCacheClusteredSession.java
===================================================================
--- trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/JBossCacheClusteredSession.java	2008-08-26 16:10:33 UTC (rev 77495)
+++ trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/JBossCacheClusteredSession.java	2008-08-26 16:41:17 UTC (rev 77496)
@@ -22,6 +22,9 @@
 
 package org.jboss.web.tomcat.service.session;
 
+import org.jboss.web.tomcat.service.session.distributedcache.spi.DistributableSession;
+import org.jboss.web.tomcat.service.session.distributedcache.spi.DistributedCacheManager;
+
 /**
  * Common superclass of ClusteredSession types that use JBossCache
  * as their distributed cache.
@@ -30,12 +33,13 @@
  * 
  * @version $Revision: 56542 $
  */
-public abstract class JBossCacheClusteredSession extends ClusteredSession
+public abstract class JBossCacheClusteredSession 
+   extends ClusteredSession implements DistributableSession
 {
    /**
     * Our proxy to the cache.
     */
-   protected transient JBossCacheService proxy_;
+   protected transient DistributedCacheManager proxy_;
    
    /**
     * Create a new JBossCacheClusteredSession.
@@ -97,7 +101,7 @@
    {
       if (proxy_ == null)
       {
-         proxy_ = ((JBossCacheManager) manager).getCacheService();
+         proxy_ = ((JBossCacheManager) manager).getDistributedCacheManager();
 
          // still null???
          if (proxy_ == null)

Deleted: trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/JBossCacheManager.java
===================================================================
--- trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/JBossCacheManager.java	2008-08-26 16:10:33 UTC (rev 77495)
+++ trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/JBossCacheManager.java	2008-08-26 16:41:17 UTC (rev 77496)
@@ -1,2296 +0,0 @@
-/*
-* JBoss, Home of Professional Open Source
-* Copyright 2005, JBoss Inc., and individual contributors as indicated
-* by the @authors tag. See the copyright.txt 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.web.tomcat.service.session;
-
-import java.security.AccessController;
-import java.util.Date;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Iterator;
-import java.util.Map;
-import java.util.Set;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.atomic.AtomicInteger;
-
-import javax.management.MalformedObjectNameException;
-import javax.management.ObjectName;
-import javax.transaction.RollbackException;
-import javax.transaction.Status;
-import javax.transaction.TransactionManager;
-
-import org.apache.catalina.Context;
-import org.apache.catalina.LifecycleException;
-import org.apache.catalina.Session;
-import org.apache.catalina.Valve;
-import org.apache.catalina.core.ContainerBase;
-import org.jboss.cache.Cache;
-import org.jboss.cache.CacheException;
-import org.jboss.cache.CacheManager;
-import org.jboss.cache.CacheStatus;
-import org.jboss.cache.pojo.PojoCache;
-import org.jboss.ha.framework.server.CacheManagerLocator;
-import org.jboss.ha.framework.server.PojoCacheManager;
-import org.jboss.ha.framework.server.PojoCacheManagerLocator;
-import org.jboss.metadata.web.jboss.JBossWebMetaData;
-import org.jboss.metadata.web.jboss.ReplicationConfig;
-import org.jboss.metadata.web.jboss.ReplicationGranularity;
-import org.jboss.metadata.web.jboss.ReplicationTrigger;
-import org.jboss.metadata.web.jboss.SnapshotMode;
-import org.jboss.util.loading.ContextClassLoaderSwitcher;
-import org.jboss.web.tomcat.service.session.ClusteredSession.SessionMetadata;
-import org.jboss.web.tomcat.service.session.ClusteredSession.SessionTimestamp;
-
-/**
- * Implementation of a clustered session manager for
- * catalina using JBossCache replication.
- *
- * @author Ben Wang
- * @author Brian Stansberry
- * @author Hany Mesha
- * @version $Revision: 58587 $
- */
-public class JBossCacheManager
-   extends JBossManager
-   implements JBossCacheManagerMBean
-{
-   // --------------------------------------------------------------- Constants
-   
-   /**
-    * Informational name for this Catalina component
-    */
-   private static final String info_ = "JBossCacheManager/1.0";
-
-   // ------------------------------------------------------------------ Fields
-
-   /** The transaction manager. */
-   private TransactionManager tm;
-
-   /** Proxy-object for the JBossCache */
-   private JBossCacheService proxy_;
-
-   /** Id/timestamp of sessions in distributedcache that we haven't loaded locally*/
-   private Map<String, OwnedSessionUpdate> unloadedSessions_ = 
-         new ConcurrentHashMap<String, OwnedSessionUpdate>();
-   
-   /** Number of passivated sessions */
-   private AtomicInteger passivatedCount_ = new AtomicInteger();
-   
-   /** Maximum number of concurrently passivated sessions */
-   private AtomicInteger maxPassivatedCount_ = new AtomicInteger();
-   
-   /** Injected pojo cache */
-   private PojoCache pojoCache_;
-   
-   /** Injected plain cache */
-   private Cache plainCache_;
-
-   /** If set to true, will add a JvmRouteFilter to the request. */
-   private Boolean useJK_;
-
-   /** Are we running embedded in JBoss? */
-   private boolean embedded_ = false;
-
-   /** Our ClusteredSessionValve's snapshot mode. */
-   private SnapshotMode snapshotMode_ = null;
-
-   /** Our ClusteredSessionValve's snapshot interval. */
-   private int snapshotInterval_ = 0;
-   
-   /** Replication granularity. */
-   private ReplicationGranularity replicationGranularity_;
-
-   /** Policy to determine if a session is dirty */
-   private ReplicationTrigger replicationTrigger_;
-
-   /**
-    * Whether we use batch mode replication for field level granularity.
-    * We store this in a Boolean rather than a primitive so JBossCacheCluster
-    * can determine if this was set via a <Manager> element.
-    */
-   private Boolean replicationFieldBatchMode_;
-   
-   /** Class loader for this web app. */
-   private ClassLoader tcl_;
-   
-   /** The snapshot manager we are using. */
-   private SnapshotManager snapshotManager_;
-   
-   /** The name of our cache's configuration */
-   private String cacheConfigName_;
-   
-   /** Did we get our cache from a cache manager? */
-   private boolean cacheFromCacheManager_;
-   
-   private int maxUnreplicatedInterval_ = -1;
-
-   //  ----------------------------------------------------------  Constructors
-
-   public JBossCacheManager()
-   {
-      super();
-   }
-   
-   public JBossCacheManager(PojoCache cache)
-   {
-      this(cache.getCache());
-      this.pojoCache_ = cache;
-   }
-   
-   public JBossCacheManager(Cache cache)
-   {
-      super();
-      this.plainCache_ = cache;
-   }
-
-   // ---------------------------------------------------- AbstractJBossManager
-
-   /**
-    * {@inheritDoc}
-    * <p>
-    * <strong>NOTE:</strong> This method should not be called when
-    * running unembedded.
-    * </p>
-    */
-   @Override
-   public void init(String name, JBossWebMetaData webMetaData)
-      throws ClusteringNotSupportedException
-   {
-      super.init(name, webMetaData);
-      
-      ReplicationConfig repCfg = webMetaData.getReplicationConfig();
-      if (repCfg != null)
-      {         
-         replicationTrigger_ = repCfg.getReplicationTrigger();
-         replicationGranularity_ = repCfg.getReplicationGranularity();
-      }       
-      
-      // Only configure JK usage if it was explicitly set; otherwise
-      // wait until we're starting when we can check for a jvmRoute
-      // in our containing Engine
-      Boolean jk = repCfg.getUseJK();
-      if (jk != null)
-      {
-         this.useJK_ = jk;
-      }
-      
-      Boolean batch = repCfg.getReplicationFieldBatchMode();
-      this.replicationFieldBatchMode_ = (batch == null ? Boolean.TRUE : batch);
-      setSnapshotMode(repCfg.getSnapshotMode());
-      Integer snapshotInt = repCfg.getSnapshotInterval();
-      setSnapshotInterval(snapshotInt == null ? 0 : snapshotInt.intValue());
-      
-      Integer maxUnrep = repCfg.getMaxUnreplicatedInterval();
-      if (maxUnrep != null)
-      {
-         this.maxUnreplicatedInterval_ = maxUnrep.intValue();
-      }
-      
-      log_.debug("init(): replicationGranularity_ is " + replicationGranularity_ +
-         " and replicationTrigger is " + replicationTrigger_ +
-         " and replicationFieldBatchMode is " + replicationFieldBatchMode_ +
-         " and useJK is " + useJK_ +
-         " and snapshotMode is " + snapshotMode_ +
-         " and snapshotInterval is " + snapshotInterval_);     
-      
-      this.cacheConfigName_ = repCfg.getCacheName();
-      
-      // Initing the proxy would be better in start, but we do it here so we
-      // can detect ClusteringNotSupportedException at this deploy stage
-      initCacheProxy();
-
-      // Confirm our replication granularity is compatible with the cache
-      // Throws ISE if not
-      validateFieldMarshalling();
-
-      embedded_ = true;
-   }
-
-   /**
-    * {@inheritDoc}
-    * <p>
-    * Removes the session from this Manager's collection of actively managed
-    * sessions.  Also removes the session from this server's copy of the
-    * distributed cache (but does not remove it from other servers'
-    * distributed cache).
-    * </p>
-    */
-   public void removeLocal(Session session)
-   {
-      ClusteredSession clusterSess = (ClusteredSession) session;
-      synchronized (clusterSess)
-      {
-         String realId = clusterSess.getRealId();
-         if (realId == null) return;
-
-         if (trace_)
-         {
-            log_.trace("Removing session from local store with id: " + realId);
-         }
-
-         try {
-            // Ignore any cache notifications that our own work generates
-            SessionReplicationContext.startCacheActivity();
-            clusterSess.removeMyselfLocal();
-         }
-         finally
-         {
-            SessionReplicationContext.finishCacheActivity();
-            
-            // We don't want to replicate this session at the end
-            // of the request; the removal process took care of that
-            SessionReplicationContext.sessionExpired(clusterSess, realId, snapshotManager_);
-            
-            sessions_.remove(realId);
-            stats_.removeStats(realId);
-
-            // Compute how long this session has been alive, and update
-            // our statistics accordingly
-            int timeAlive = (int) ((System.currentTimeMillis() - clusterSess.getCreationTimeInternal())/1000);
-            sessionExpired(timeAlive);
-         }
-      }
-   }
-
-   /**
-    * {@inheritDoc}
-    */
-   public boolean storeSession(Session baseSession)
-   {
-      boolean stored = false;
-      if(baseSession != null && started_)
-      {
-         ClusteredSession session = (ClusteredSession) baseSession;
-
-         synchronized (session)
-         {
-            if (trace_)
-            {
-               log_.trace("check to see if needs to store and replicate " +
-                          "session with id " + session.getIdInternal());
-            }
-
-            if (session.isValid() &&
-                  (session.isSessionDirty() || session.getMustReplicateTimestamp()))
-            {
-               String realId = session.getRealId();
-
-               // Notify all session attributes that they get serialized (SRV 7.7.2)
-               long begin = System.currentTimeMillis();
-               session.passivate();
-               long elapsed = System.currentTimeMillis() - begin;
-               stats_.updatePassivationStats(realId, elapsed);
-
-               // Do the actual replication
-               begin = System.currentTimeMillis();
-               processSessionRepl(session);
-               elapsed = System.currentTimeMillis() - begin;
-               stored = true;
-               stats_.updateReplicationStats(realId, elapsed);
-            }
-            else if (trace_)
-            {
-               log_.trace("Session " + session.getIdInternal() + 
-                          " did not require replication.");
-            }
-         }
-      }
-
-      return stored;
-   }
-
-   // ----------------------------------------------------------------- Manager
-
-   /**
-    * {@inheritDoc}
-    */
-   public void add(Session session)
-   {
-      if (session == null)
-         return;
-
-      if (!(session instanceof ClusteredSession))
-      {
-         throw new IllegalArgumentException("You can only add instances of " +
-               "type ClusteredSession to this Manager. Session class name: " +
-               session.getClass().getName());
-      }
-
-//      add((ClusteredSession) session, true);
-      add((ClusteredSession) session, false); // wait to replicate until req end
-   }
-
-   /**
-    * Adds the given session to the collection of those being managed by this
-    * Manager.
-    *
-    * @param session   the session. Cannot be <code>null</code>.
-    * @param replicate whether the session should be replicated
-    *
-    * @throws NullPointerException if <code>session</code> is <code>null</code>.
-    */
-   private void add(ClusteredSession session, boolean replicate)
-   {
-      // TODO -- why are we doing this check? The request checks session 
-      // validity and will expire the session; this seems redundant
-      if (!session.isValid())
-      {
-         // Not an error; this can happen if a failover request pulls in an
-         // outdated session from the distributed cache (see TODO above)
-         log_.debug("Cannot add session with id=" + session.getIdInternal() +
-                    " because it is invalid");
-         return;
-      }
-
-      String realId = session.getRealId();
-      Object existing = sessions_.put(realId, session);
-      unloadedSessions_.remove(realId);
-
-      if (!session.equals(existing))
-      {
-         if (replicate)
-         {
-            storeSession(session);
-         }
-
-         // Update counters
-         calcActiveSessions();
-         
-         if (trace_)
-         {
-            log_.trace("Session with id=" + session.getIdInternal() + " added. " +
-                       "Current active sessions " + localActiveCounter_.get());
-         }
-      }
-   }
-
-   // Satisfy the Manager interface.  Internally we use
-   // createEmptyClusteredSession to avoid a cast
-   public Session createEmptySession()
-   {
-      if (trace_)
-      {
-         log_.trace("Creating an empty ClusteredSession");
-      }
-      
-      return createEmptyClusteredSession();
-   }
-   
-   /**
-    * {@inheritDoc}
-    */
-   public Session createSession()
-   {
-      return createSession(null);
-   }
-
-   /**
-    * {@inheritDoc}
-    */
-   public Session createSession(String sessionId)
-   {      
-      // First check if we've reached the max allowed sessions, 
-      // then try to expire/passivate sessions to free memory
-      // maxActiveAllowed_ -1 is unlimited
-      // We check here for maxActive instead of in add().  add() gets called
-      // when we load an already existing session from the distributed cache
-      // (e.g. in a failover) and we don't want to fail in that situation.
-
-      if(maxActiveAllowed_ != -1 && calcActiveSessions() >= maxActiveAllowed_)
-      {
-         if (trace_)
-         {
-            log_.trace("createSession(): active sessions = " + calcActiveSessions() +
-                       " and max allowed sessions = " + maxActiveAllowed_);
-         }
-         
-         processExpirationPassivation();
-         
-         if (calcActiveSessions() >= maxActiveAllowed_)
-         {
-            // Exceeds limit. We need to reject it.
-            rejectedCounter_.incrementAndGet();
-            // Catalina api does not specify what happens
-            // but we will throw a runtime exception for now.
-            String msgEnd = (sessionId == null) ? "" : " id " + sessionId;
-            throw new IllegalStateException("createSession(): number of " +
-                   "active sessions exceeds the maximum limit: " +
-                   maxActiveAllowed_ + " when trying to create session" + msgEnd);
-         }
-      }
-
-      ClusteredSession session = createEmptyClusteredSession();
-
-      session.setNew(true);
-      session.setCreationTime(System.currentTimeMillis());
-      session.setMaxInactiveInterval(this.maxInactiveInterval_);
-      session.setValid(true);
-
-      if (sessionId == null)
-      {
-          sessionId = this.getNextId();
-
-          // We are using mod_jk for load balancing. Append the JvmRoute.
-          if (getUseJK())
-          {
-              if (trace_)
-              {
-                  log_.trace("createSession(): useJK is true. Will append JvmRoute: " + this.getJvmRoute());
-              }
-              sessionId += "." + this.getJvmRoute();
-          }
-      }
-
-      session.setId(sessionId); // Setting the id leads to a call to add()
-
-      if (trace_)
-      {
-         log_.trace("Created a ClusteredSession with id: " + sessionId);
-      }
-
-      createdCounter_.incrementAndGet(); // the call to add() handles the other counters 
-      
-      // Add this session to the set of those potentially needing replication
-      SessionReplicationContext.bindSession(session, snapshotManager_);
-      
-      return session;
-   }
-
-   /**
-    * Attempts to find the session in the collection of those being managed
-    * locally, and if not found there, in the distributed cache of sessions.
-    * <p>
-    * If a session is found in the distributed cache, it is added to the
-    * collection of those being managed locally.
-    * </p>
-    *
-    * @param id the session id, which may include an appended jvmRoute
-    *
-    * @return the session, or <code>null</code> if no such session could
-    *         be found
-    */
-   public Session findSession(String id)
-   {
-      String realId = getRealId(id);
-      // Find it from the local store first
-      ClusteredSession session = findLocalSession(realId);
-      
-      // If we didn't find it locally, only check the distributed cache
-      // if we haven't previously handled this session id on this request.
-      // If we handled it previously but it's no longer local, that means
-      // it's been invalidated. If we request an invalidated session from
-      // the distributed cache, it will be missing from the local cache but
-      // may still exist on other nodes (i.e. if the invalidation hasn't 
-      // replicated yet because we are running in a tx). With buddy replication,
-      // asking the local cache for the session will cause the out-of-date
-      // session from the other nodes to be gravitated, thus resuscitating
-      // the session.
-      if (session == null 
-            && !SessionReplicationContext.isSessionBoundAndExpired(realId, snapshotManager_))
-      {
-         if (trace_)
-            log_.trace("Checking for session " + realId + " in the distributed cache");
-         
-         session = loadSession(realId);
-         if (session != null)
-         {
-            add(session);
-            // TODO should we advise of a new session?
-            //tellNew();
-         }
-      }
-      else if (session != null && session.isOutdated())
-      {
-         if (trace_)
-            log_.trace("Updating session " + realId + " from the distributed cache");
-         
-         // Need to update it from the cache
-         loadSession(realId);
-      }
-
-      if (session != null)
-      {
-         // Add this session to the set of those potentially needing replication
-         SessionReplicationContext.bindSession(session, snapshotManager_);
-      }
-
-      return session;
-   }
-
-   /**
-    * Return the sessions. Note that this will return not only the local
-    * in-memory sessions, but also any sessions that are in the distributed
-    * cache but have not previously been accessed on this server.  Invoking
-    * this method will bring all such sessions into local memory and can
-    * potentially be quite expensive.
-    *
-    * <p>
-    * Note also that when sessions are loaded from the distributed cache, no
-    * check is made as to whether the number of local sessions will thereafter
-    * exceed the maximum number allowed on this server.
-    * </p>
-    *
-    * @return an array of all the sessions
-    */
-   public Session[] findSessions()
-   {
-      // Need to load all the unloaded sessions
-      if(unloadedSessions_.size() > 0)
-      {
-         // Make a thread-safe copy of the new id list to work with
-         Set ids = new HashSet(unloadedSessions_.keySet());
-
-         if(trace_) {
-            log_.trace("findSessions: loading sessions from distributed cache: " + ids);
-         }
-
-         for(Iterator it = ids.iterator(); it.hasNext();) {
-            loadSession((String) it.next());
-         }
-      }
-
-      // All sessions are now "local" so just return the local sessions
-      return findLocalSessions();
-   }
-
-   /**
-    * {@inheritDoc}
-    */
-   public String getInfo()
-   {
-      return info_;
-   }
-
-   /**
-    * Removes the session from this Manager's collection of actively managed
-    * sessions.  Also removes the session from the distributed cache, both
-    * on this server and on all other server to which this one replicates.
-    */
-   public void remove(Session session)
-   {
-      ClusteredSession clusterSess = (ClusteredSession) session;
-      synchronized (clusterSess)
-      {
-         String realId = clusterSess.getRealId();
-         if (realId == null)
-            return;
-
-         if (trace_)
-         {
-            log_.trace("Removing session from store with id: " + realId);
-         }
-
-         try {
-            // Ignore any cache notifications that our own work generates
-            SessionReplicationContext.startCacheActivity();
-            clusterSess.removeMyself();
-         }
-         finally {
-            SessionReplicationContext.finishCacheActivity();
-            
-            // We don't want to replicate this session at the end
-            // of the request; the removal process took care of that
-            SessionReplicationContext.sessionExpired(clusterSess, realId, snapshotManager_);
-            
-            sessions_.remove(realId);
-            stats_.removeStats(realId);
-
-            // Compute how long this session has been alive, and update
-            // our statistics accordingly
-            int timeAlive = (int) ((System.currentTimeMillis() - clusterSess.getCreationTimeInternal())/1000);
-            sessionExpired(timeAlive);
-         }
-      }
-   }
-
-   // --------------------------------------------------------------- Lifecycle
-   
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public void start() throws LifecycleException
-   {
-      if (embedded_)
-      {
-         startEmbedded();
-      }
-      else
-      {
-         startUnembedded();
-      }
-      
-      log_.debug("JBossCacheManager for " + getContainer().getName() + " started");
-   }
-
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public void stop() throws LifecycleException
-   {
-      if (!started_)
-      {
-         throw new IllegalStateException("Manager not started");
-      }
-      
-      log_.debug("Stopping");
-      
-      // Block for any ongoing backgroundProcess, then disable
-      synchronized (backgroundProcessAllowed)
-      {
-         backgroundProcessAllowed.set(false);
-      }
-      
-      resetStats();
-      
-      // Notify our interested LifecycleListeners
-      lifecycle_.fireLifecycleEvent(BEFORE_STOP_EVENT, this);
-      
-      clearSessions();
-      
-      // Don't leak the classloader
-      tcl_ = null;
-      
-      proxy_.stop();
-      proxy_ = null;
-      
-      tm = null;
-      
-      if (cacheFromCacheManager_)
-      {
-         // Let the manager know we are done with the cache
-         releaseCacheToManager();
-      }
-      
-      snapshotManager_.stop();
-      
-      // Clean up maps
-      sessions_.clear();
-      unloadedSessions_.clear();
-      
-      passivatedCount_.set(0);
-      
-      started_ = false;
-      
-      // Notify our interested LifecycleListeners
-      lifecycle_.fireLifecycleEvent(AFTER_STOP_EVENT, this);
-      
-      unregisterManagerMBean();
-   }
-   
-
-   // -------------------------------------------------- JBossCacheManagerMBean
-
-   /**
-    * {@inheritDoc}
-    */
-   public void expireSession(String sessionId)
-   {
-      Session session = findSession(sessionId);
-      if (session != null)
-         session.expire();
-   }
-
-   /**
-    * {@inheritDoc}
-    */
-   public String getCacheConfigName()
-   {
-      return (cacheFromCacheManager_ ? cacheConfigName_ : null);
-   }
-
-   /**
-    * {@inheritDoc}
-    */
-   public String getCreationTime(String sessionId)
-   {
-      Session session = findSession(sessionId);
-      if(session == null) 
-      {
-         log_.info("getCreationTime(): Session " + sessionId + 
-                       " not found");
-         return "";
-      }
-     return new Date(session.getCreationTime()).toString();
-   }
-   
-   /**
-    * {@inheritDoc}
-    */
-   public int getDuplicates()
-   {
-      return duplicates_.get();
-   }
-
-   /**
-    * {@inheritDoc}
-    */
-   public String getLastAccessedTime(String sessionId)
-   {
-      Session session = findSession(sessionId);
-      if(session == null) {
-         log_.info("getLastAccessedTime(): Session " + sessionId + 
-                   " not found");
-         return "";
-      }
-     return new Date(session.getLastAccessedTime()).toString();
-   }
-
-   /**
-    * {@inheritDoc}
-    */
-   public String getSessionAttribute(String sessionId, String key)
-   {
-      Object attr = null;
-      ClusteredSession session = (ClusteredSession) findSession(sessionId);
-      if (session != null)
-      {
-         attr = session.getAttribute(key);
-      }
-      
-      return attr == null ? null : attr.toString();
-   }
-
-   /**
-    * {@inheritDoc}
-    */
-   public long getMaxPassivatedSessionCount()
-   {
-      return maxPassivatedCount_.get();
-   }
-
-   /**
-    * {@inheritDoc}
-    */
-   public int getMaxUnreplicatedInterval()
-   {
-      return maxUnreplicatedInterval_;
-   }
-
-   /**
-    * {@inheritDoc}
-    */
-   public void setMaxUnreplicatedInterval(int maxUnreplicatedInterval)
-   {
-      this.maxUnreplicatedInterval_ = maxUnreplicatedInterval;
-   }
-
-   /**
-    * {@inheritDoc}
-    */
-   public long getPassivatedSessionCount()
-   {
-      return passivatedCount_.get();
-   }
-
-   /**
-    * {@inheritDoc}
-    */
-   public long getPassivationMaxIdleTime()
-   {
-      return passivationMaxIdleTime_;
-   }
-
-   /**
-    * {@inheritDoc}
-    */
-   public long getPassivationMinIdleTime()
-   {
-      return passivationMinIdleTime_;
-   }
-
-   /**
-    * {@inheritDoc}
-    */
-   public ReplicationGranularity getReplicationGranularity()
-   {
-      return replicationGranularity_;
-   }    
-   
-   /**
-    * {@inheritDoc}
-    */
-   public ReplicationTrigger getReplicationTrigger()
-   {
-      return this.replicationTrigger_;
-   }
-
-   /**
-    * {@inheritDoc}
-    */
-   public int getSnapshotInterval()
-   {
-      return snapshotInterval_;
-   }
-
-   /**
-    * {@inheritDoc}
-    */
-   public SnapshotMode getSnapshotMode()
-   {
-      return snapshotMode_;
-   }
-
-   /**
-    * {@inheritDoc}
-    */
-   public boolean getUseJK()
-   {
-      return useJK_ == null ? false : useJK_.booleanValue();
-   }
-   
-   /**
-    * {@inheritDoc}
-    */
-   public boolean isPassivationEnabled()
-   {
-      return (passivationMode_ && proxy_.isCachePassivationEnabled());
-   }
-
-   /**
-    * {@inheritDoc}
-    */
-   public Boolean isReplicationFieldBatchMode()
-   {
-      return replicationFieldBatchMode_;
-   }
-   
-   /**
-    * {@inheritDoc}
-    */
-   public String listLocalSessionIds()
-   {
-      return reportSessionIds(sessions_.keySet());
-   }
-   
-   /**
-    * {@inheritDoc}
-    */
-   public String listSessionIds()
-   {
-      Set ids = new HashSet(sessions_.keySet());
-      ids.addAll(unloadedSessions_.keySet());
-      return reportSessionIds(ids);
-   }
-   
-   // -------------------------------- Callbacks from Distributed Caching Layer
-
-   /**
-    * Notifies the manager that a session in the distributed cache has
-    * been invalidated
-    * 
-    * @param realId the session id excluding any jvmRoute
-    */
-   public void notifyRemoteInvalidation(String realId)
-   {
-      // Remove the session from our local map
-      ClusteredSession session = (ClusteredSession) sessions_.remove(realId);
-      if (session == null)
-      {
-         // We weren't managing the session anyway.  But remove it
-         // from the list of cached sessions we haven't loaded
-         if (unloadedSessions_.remove(realId) != null)
-         {
-            if (trace_)
-               log_.trace("Removed entry for session " + realId + " from unloaded session map");
-         }
-      }
-      else
-      {
-         // Expire the session
-         // DON'T SYNCHRONIZE ON SESSION HERE -- isValid() and
-         // expire() are meant to be multi-threaded and synchronize
-         // properly internally; synchronizing externally can lead
-         // to deadlocks!!
-         boolean notify = false; // Don't notify listeners. SRV.10.7
-                                 // allows this, and sending notifications
-                                 // leads to all sorts of issues; e.g.
-                                 // circular calls with ClusteredSSO
-         boolean localCall = false; // this call originated from the cache;
-                                    // we have already removed session
-         boolean localOnly = true; // Don't pass attr removals to cache
-         
-         // Ensure the correct TCL is in place
-         ClassLoader prevTcl = Thread.currentThread().getContextClassLoader();
-         try
-         {
-            Thread.currentThread().setContextClassLoader(tcl_);
-            session.expire(notify, localCall, localOnly);
-         }
-         finally
-         {
-            Thread.currentThread().setContextClassLoader(prevTcl);
-         }
-
-         // Remove any stats for this session
-         stats_.removeStats(realId);
-      }
-   }
-   
-   /**
-    * Callback from the distributed cache notifying of a local modification
-    * to a session's attributes.  Meant for use with FIELD granularity,
-    * where the session may not be aware of modifications.
-    * 
-    * @param realId the session id excluding any jvmRoute
-    */
-   public void notifyLocalAttributeModification(String realId)
-   {
-      ClusteredSession session = (ClusteredSession) sessions_.get(realId);
-      if (session != null)
-      {
-         session.sessionAttributesDirty();
-      }
-      else
-      {
-         log_.warn("");
-      }
-   }
-   
-   public void sessionActivated()
-   {
-      int pc = passivatedCount_.decrementAndGet();
-      // Correct for drift since we don't know the true passivation
-      // count when we started.  We can get activations of sessions
-      // we didn't know were passivated.
-      // FIXME -- is the above statement still correct? Is this needed?
-      if (pc < 0) 
-      {
-         // Just reverse our decrement.
-         passivatedCount_.incrementAndGet();
-      }
-   }
-   
-   /**
-    * Callback from the distributed cache to notify us that a session
-    * has been modified remotely.
-    * 
-    * @param realId the session id, without any trailing jvmRoute
-    * @param dataOwner  the owner of the session.  Can be <code>null</code> if
-    *                   the owner is unknown.
-    * @param distributedVersion the session's version per the distributed cache
-    * @param timestamp the session's timestamp per the distributed cache
-    * @param metadata the session's metadata per the distributed cache
-    */
-   public boolean sessionChangedInDistributedCache(String realId, 
-                                         String dataOwner,
-                                         int distributedVersion,
-                                         SessionTimestamp timestamp, 
-                                         SessionMetadata metadata)
-   {
-      boolean updated = true;
-      
-      ClusteredSession session = findLocalSession(realId);
-      if (session != null)
-      {
-         // Need to invalidate the loaded session. We get back whether
-         // this an actual version increment
-         updated = session.setVersionFromDistributedCache(distributedVersion);
-         if (updated && trace_)      
-         {            
-            log_.trace("session in-memory data is invalidated for id: " + realId + 
-                       " new version: " + distributedVersion);
-         }         
-      }
-      else
-      {
-         long lastMod = timestamp == null ? System.currentTimeMillis() : timestamp.timestamp;
-         int maxLife = metadata == null ? getMaxInactiveInterval() : metadata.maxInactiveInterval;
-         
-         Object existing = unloadedSessions_.put(realId, new OwnedSessionUpdate(dataOwner, lastMod, maxLife, false));
-         if (existing == null)
-         {
-            calcActiveSessions();
-            if (trace_)
-            {
-               log_.trace("New session " + realId + " added to unloaded session map");
-            }
-         }
-         else if (trace_)
-         {
-            log_.trace("Updated timestamp for unloaded session " + realId);
-         }
-      }
-      
-      return updated;
-   }
-   
-   // ----------------------------------------------- JBossCacheCluster Support
-
-   /**
-    * Sets how often session changes should be replicated to other nodes.
-    *
-    * @param snapshotInterval the number of milliseconds between
-    *                         session replications.
-    */
-   public void setSnapshotInterval(int snapshotInterval)
-   {
-      this.snapshotInterval_ = snapshotInterval;
-   }
-
-   /**
-    * Sets when sessions are replicated to the other nodes. Valid values are:
-    * <ul>
-    * <li>instant</li>
-    * <li>interval</li>
-    * </ul>
-    */
-   public void setSnapshotMode(SnapshotMode snapshotMode)
-   {
-      this.snapshotMode_ = snapshotMode;
-   }
-
-   /**
-    * Sets whether the <code>Engine</code> in which we are running
-    * uses <code>mod_jk</code>.
-    */
-   public void setUseJK(boolean useJK)
-   {
-      this.useJK_ = Boolean.valueOf(useJK);
-   }
-
-   /**
-    * Sets the granularity of session data replicated across the cluster.
-    * Valid values are:
-    * <ul>
-    * <li>SESSION</li>
-    * <li>ATTRIBUTE</li>
-    * <li>FIELD</li>
-    * </ul>
-    */
-   public void setReplicationGranularity(ReplicationGranularity granularity)
-   {
-      this.replicationGranularity_ = granularity;
-   }
-
-   /**
-    * Returns the replication granularity.
-    *
-    *  @see JBossWebMetaData#REPLICATION_GRANULARITY_ATTRIBUTE
-    *  @see JBossWebMetaData#REPLICATION_GRANULARITY_FIELD
-    *  @see JBossWebMetaData#REPLICATION_GRANULARITY_SESSION
-    */
-   public String getReplicationGranularityString()
-   {
-      return replicationGranularity_ == null ? null : replicationGranularity_.toString();
-   }
-
-   /**
-    * Sets the granularity of session data replicated across the cluster.
-    * Valid values are:
-    * <ul>
-    * <li>SESSION</li>
-    * <li>ATTRIBUTE</li>
-    * <li>FIELD</li>
-    * </ul>
-    */
-   public void setReplicationGranularityString(String granularity)
-   {
-      setReplicationGranularity(granularity == null ? null : 
-                  ReplicationGranularity.fromString(granularity.toUpperCase()));
-   } 
-   
-   /**
-    * Sets the type of operations on a <code>HttpSession</code> that
-    * trigger replication.  Valid values are:
-    * <ul>
-    * <li>SET_AND_GET</li>
-    * <li>SET_AND_NON_PRIMITIVE_GET</li>
-    * <li>SET</li>
-    * </ul>
-    */
-   public void setReplicationTrigger(ReplicationTrigger trigger)
-   {
-      this.replicationTrigger_ = trigger;
-   }
-
-   public String getReplicationTriggerString()
-   {      
-      return replicationTrigger_ == null ? null : replicationTrigger_.toString();
-   }
-
-   public void setReplicationTriggerString(String trigger)
-   {      
-      setReplicationTrigger(trigger == null ? null : ReplicationTrigger.fromString(trigger.toUpperCase()));
-   }
-
-   /**
-    * Sets whether, if replication granularity is set to <code>FIELD</code>,
-    * replication should be done in batch mode.  Ignored if field-level
-    * granularity is not used.
-    */
-   public void setReplicationFieldBatchMode(boolean replicationFieldBatchMode)
-   {
-      this.replicationFieldBatchMode_ = Boolean.valueOf(replicationFieldBatchMode);
-   }
-
-   // -------------------------------------------------------------  Properties
-   
-   public PojoCache getPojoCache()
-   {
-      return pojoCache_;
-   }
-   
-   public Cache getPlainCache()
-   {
-      return plainCache_;
-   }
-   
-   /**
-    * Gets the <code>FieldBasedJBossCacheService</code> through which we interact
-    * with the PojoCache.
-    * 
-    * @throws IllegalStateException if we are not using field based replication
-    */
-   public FieldBasedJBossCacheService getPojoCacheService()
-   {
-      if (proxy_ != null && !(proxy_ instanceof FieldBasedJBossCacheService))
-         throw new IllegalStateException("PojoCache not being used");
-      return (FieldBasedJBossCacheService) proxy_;
-   }
-
-   /**
-    * Gets the <code>JBossCacheService</code> through which we interact
-    * with the <code>Cache</code>.
-    */
-   public JBossCacheService getCacheService()
-   {
-      return proxy_;
-   }
-
-   // --------------------------------------------------------------- Overrides
-
-   /**
-    * {@inheritDoc}
-    * <p>
-    * Overrides the superclass version to ensure that the generated id
-    * does not duplicate the id of any other session this manager is aware of.
-    * </p>
-    */
-   @Override
-   protected String getNextId()
-   {
-      while (true)
-      {
-         String id = super.getNextId();
-         if (sessions_.containsKey(id) || unloadedSessions_.containsKey(id))
-         {
-            duplicates_.incrementAndGet();
-         }
-         else
-         {
-            return id;
-         }
-      }
-   }
-
-   protected int getTotalActiveSessions()
-   {
-      return localActiveCounter_.get() + unloadedSessions_.size() - passivatedCount_.get();
-   }
-
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   protected void processExpirationPassivation()
-   {      
-      boolean expire = maxInactiveInterval_ >= 0;
-      boolean passivate = isPassivationEnabled();
-      
-      long passivationMax = passivationMaxIdleTime_ * 1000L;
-      long passivationMin = passivationMinIdleTime_ * 1000L;
-
-      if (trace_)
-      { 
-         log_.trace("processExpirationPassivation(): Looking for sessions that have expired ...");
-         log_.trace("processExpirationPassivation(): active sessions = " + calcActiveSessions());
-         log_.trace("processExpirationPassivation(): expired sessions = " + expiredCounter_);
-         if (passivate)
-         {
-            log_.trace("processExpirationPassivation(): passivated count = " + getPassivatedSessionCount());
-         }
-      }
-      
-      try
-      {
-         // First, handle the sessions we are actively managing
-         Session sessions[] = findLocalSessions();
-         for (int i = 0; i < sessions.length; ++i)
-         {
-            try
-            {
-               ClusteredSession session = (ClusteredSession) sessions[i];
-               if(session == null)
-               {
-                  log_.warn("processExpirationPassivation(): processing null session at index " +i);
-                  continue;
-               }
-
-               if (expire)
-               {
-                  // JBAS-2403. Check for outdated sessions where we think
-                  // the local copy has timed out.  If found, refresh the
-                  // session from the cache in case that might change the timeout
-                  if (session.isOutdated() && !(session.isValid(false)))
-                  {
-                     // JBAS-2792 don't assign the result of loadSession to session
-                     // just update the object from the cache or fall through if
-                     // the session has been removed from the cache
-                     loadSession(session.getRealId());
-                  }
-   
-                  // Do a normal invalidation check that will expire the
-                  // session if it has timed out
-                  // DON'T SYNCHRONIZE on session here -- isValid() and
-                  // expire() are meant to be multi-threaded and synchronize
-                  // properly internally; synchronizing externally can lead
-                  // to deadlocks!!
-                  if (!session.isValid()) continue;
-               }
-                
-               // we now have a valid session; see if we need to passivate it
-               if (passivate)
-               {
-                  long timeNow = System.currentTimeMillis();
-                  long timeIdle = timeNow - session.getLastAccessedTimeInternal();
-                  // if maxIdle time configured, means that we need to passivate sessions that have
-                  // exceeded the max allowed idle time
-                  if (passivationMax >= 0 
-                        && timeIdle > passivationMax)
-                  {
-                     processSessionPassivation(session.getRealId());
-                  }
-                  // If the session didn't exceed the passivationMaxIdleTime_, see   
-                  // if the number of sessions managed by this manager greater than the max allowed 
-                  // active sessions, passivate the session if it exceed passivationMinIdleTime_ 
-                  else if (maxActiveAllowed_ > 0 
-                              && passivationMin > 0 
-                              && calcActiveSessions() >= maxActiveAllowed_ 
-                              && timeIdle > passivationMin)
-                  {
-                     processSessionPassivation(session.getRealId());
-                  }                  
-               }
-               
-            }
-            catch (Exception ex)
-            {
-               log_.error("processExpirationPassivation(): failed handling " + 
-                          sessions[i].getIdInternal() + " with exception: " + 
-                          ex, ex);
-            }
-         }
-
-         // Next, handle any unloaded sessions
-
-         
-         // We may have not gotten replication of a timestamp for requests 
-         // that occurred w/in maxUnreplicatedInterval_ of the previous
-         // request. So we add a grace period to avoid flushing a session early
-         // and permanently losing part of its node structure in JBoss Cache.
-         long maxUnrep = maxUnreplicatedInterval_ < 0 ? 60 : maxUnreplicatedInterval_;
-         
-         Map unloaded = new HashMap(unloadedSessions_);
-         for (Iterator<Map.Entry<String, OwnedSessionUpdate>> it = unloaded.entrySet().iterator(); it.hasNext(); )
-         {
-            Map.Entry<String, OwnedSessionUpdate> entry = it.next();
-            String realId = entry.getKey();
-            OwnedSessionUpdate osu = entry.getValue();
-            
-            // Ignore marker entries for our own passivated sessions
-            // Also skip if the session isn't configured to expire
-            if (osu.passivated || osu.maxInactive < 1)
-               continue;
-            
-            long now = System.currentTimeMillis();
-            long elapsed = (now - osu.updateTime);
-            try
-            {
-               if (expire && elapsed >= (osu.maxInactive + maxUnrep) * 1000L)
-               {
-                  proxy_.removeSessionLocal(realId, osu.owner);
-                  unloadedSessions_.remove(realId);
-               }
-               else if (passivate)
-               {
-                  // if maxIdle time configured, means that we need to passivate sessions that have
-                  // exceeded the max allowed idle time
-                  if (passivationMax >= 0 
-                        && elapsed > passivationMax)
-                  {
-                     processUnloadedSessionPassivation(realId, osu);
-                  }
-                  // If the session didn't exceed the passivationMaxIdleTime_, see   
-                  // if the number of sessions managed by this manager is greater than the max allowed 
-                  // active sessions; passivate the session if it exceed passivationMinIdleTime_ 
-                  else if (maxActiveAllowed_ > 0 
-                              && passivationMin >= 0 
-                              && elapsed >= passivationMin
-                              && calcActiveSessions() >= maxActiveAllowed_)
-                  {
-                     processUnloadedSessionPassivation(realId, osu);
-                  }               
-               }
-            } 
-            catch (Exception ex)
-            {
-               log_.error("processExpirationPassivation(): failed handling unloaded session " + 
-                       realId, ex);
-            }
-         }
-      }
-      catch (Exception ex)
-      {
-         log_.error("processExpirationPassivation(): failed with exception: " + ex, ex);
-      }
-      
-      if (trace_)
-      { 
-         log_.trace("processExpirationPassivation(): Completed ...");
-         log_.trace("processExpirationPassivation(): active sessions = " + calcActiveSessions());
-         log_.trace("processExpirationPassivation(): expired sessions = " + expiredCounter_);
-         if (passivate)
-         {
-            log_.trace("processExpirationPassivation(): passivated count = " + getPassivatedSessionCount());
-         }
-      }
-   }
-   
-   @Override
-   public void resetStats()
-   {
-      super.resetStats();
-      
-      this.maxPassivatedCount_.set(this.passivatedCount_.get());
-   }
-   
-   
-   
-   // --------------------------------------------------------------- Protected
-
-   /**
-    * Gets the classloader of the webapp we are managing.
-    * 
-    * FIXME try and remove the need for this
-    */
-   protected ClassLoader getWebappClassLoader()
-   {
-      return tcl_;
-   }
-   
-   // ------------------------------------------------------ Session Management
-
-   private JBossCacheClusteredSession createEmptyClusteredSession()
-   {     
-
-      JBossCacheClusteredSession session = null;
-      switch (replicationGranularity_)
-      {
-         case ATTRIBUTE:
-         session = new AttributeBasedClusteredSession(this);
-            break;
-         case FIELD:
-            session = new FieldBasedClusteredSession(this);
-            break;
-         default:
-            session = new SessionBasedClusteredSession(this);
-            break;
-      }
-      return session;
-   }
-   
-   /**
-    * Loads a session from the distributed store.  If an existing session with
-    * the id is already under local management, that session's internal state
-    * will be updated from the distributed store.  Otherwise a new session
-    * will be created and added to the collection of those sessions under
-    * local management.
-    *
-    * @param realId  id of the session-id with any jvmRoute removed
-    *
-    * @return the session or <code>null</code> if the session cannot be found
-    *         in the distributed store
-    */
-   private ClusteredSession loadSession(String realId)
-   {
-      if (realId == null)
-      {
-         return null;
-      }
-
-      long begin = System.currentTimeMillis();
-      boolean mustAdd = false;
-      JBossCacheClusteredSession session = (JBossCacheClusteredSession) sessions_.get(realId);
-      
-      if (session == null)
-      {                 
-         // This is either the first time we've seen this session on this
-         // server, or we previously expired it and have since gotten
-         // a replication message from another server
-         mustAdd = true;
-         session = createEmptyClusteredSession();
-      }
-
-      synchronized (session)
-      {
-         boolean doTx = false; 
-         try
-         {
-            // We need transaction so any data gravitation replication 
-            // is sent in batch.
-            // Don't do anything if there is already transaction context
-            // associated with this thread.
-            if(tm.getTransaction() == null)
-               doTx = true;
-
-            if(doTx)
-               tm.begin();
-            
-            // Ignore cache notifications we may generate for this 
-            // session if data gravitation occurs. 
-            SessionReplicationContext.startCacheActivity();
-            
-            session = proxy_.loadSession(realId, session);
-            
-            if (session != null)
-            {
-               session.initAfterLoad(this);
-            }
-         }
-         catch (Exception ex)
-         {
-            try
-            {
-//                  if(doTx)
-               // Let's set it no matter what.
-               tm.setRollbackOnly();
-            }
-            catch (Exception exn)
-            {
-               log_.error("Caught exception rolling back transaction", exn);
-            }
-            // We will need to alert Tomcat of this exception.
-            if (ex instanceof RuntimeException)
-               throw (RuntimeException) ex;
-            
-            throw new RuntimeException("loadSession(): failed to load session " +
-                                       realId, ex);
-         }
-         finally
-         {
-            try {
-               if(doTx)
-                  endTransaction(realId);
-            }
-            finally {
-               SessionReplicationContext.finishCacheActivity();
-            }
-         }
-
-         if (session != null)
-         {            
-            if (mustAdd)
-               add(session, false); // don't replicate
-            long elapsed = System.currentTimeMillis() - begin;
-            stats_.updateLoadStats(realId, elapsed);
-
-            if (trace_)
-            {
-               log_.trace("loadSession(): id= " + realId + ", session=" + session);
-            }
-         }
-         else if (trace_)
-         {
-            log_.trace("loadSession(): session " + realId +
-                       " not found in distributed cache");
-         }
-      }
-
-      return session;
-   }
-
-   /**
-    * Places the current session contents in the distributed cache and
-    * replicates them to the cluster
-    *
-    * @param session  the session.  Cannot be <code>null</code>.
-    */
-   private void processSessionRepl(ClusteredSession session)
-   {
-      // If we are using SESSION granularity, we don't want to initiate a TX
-      // for a single put
-      boolean notSession = (replicationGranularity_ != ReplicationGranularity.SESSION);
-      boolean doTx = false;
-      try
-      {
-         // We need transaction so all the replication are sent in batch.
-         // Don't do anything if there is already transaction context
-         // associated with this thread.
-         if(notSession && tm.getTransaction() == null)
-            doTx = true;
-
-         if(doTx)
-            tm.begin();
-
-         // Tell the proxy to ignore cache notifications we are about
-         // to generate for this session. We have to do this
-         // at this level because we don't want to resume handling
-         // notifications until any compensating changes resulting
-         // from a tx rollback are done.
-         SessionReplicationContext.startCacheActivity();
-
-         session.processSessionRepl();
-      }
-      catch (Exception ex)
-      {
-         log_.debug("processSessionRepl(): failed with exception", ex);
-         
-         try
-         {
-            //if(doTx)
-            // Let's setRollbackOnly no matter what.
-            // (except if there's no tx due to SESSION (JBAS-3840))
-            if (notSession)
-               tm.setRollbackOnly();
-         }
-         catch (Exception exn)
-         {
-            log_.error("Caught exception rolling back transaction", exn);
-         }
-         
-         // We will need to alert Tomcat of this exception.
-         if (ex instanceof RuntimeException)
-            throw (RuntimeException) ex;
-         
-         throw new RuntimeException("JBossCacheManager.processSessionRepl(): " +
-                                    "failed to replicate session.", ex);
-      }
-      finally
-      {
-         try {
-            if(doTx)
-               endTransaction(session.getId());
-         }
-         finally {
-            SessionReplicationContext.finishCacheActivity();
-         }
-      }
-   }
-   
-   /**
-    * Session passivation logic for an actively managed session.
-    * 
-    * @param realId the session id, minus any jvmRoute
-    */
-   private void processSessionPassivation(String realId)
-   {
-      // get the session from the local map
-      ClusteredSession session = findLocalSession(realId);
-      // Remove actively managed session and add to the unloaded sessions
-      // if it's already unloaded session (session == null) don't do anything, 
-      if (session != null)
-      {
-         synchronized (session)
-         {
-            if (trace_)
-            {
-               log_.trace("Passivating session with id: " + realId);
-            }
-
-            try {
-               // Tell the proxy to ignore cache notifications we are about
-               // to generate for this session.
-               SessionReplicationContext.startCacheActivity();
-               session.passivate();
-               proxy_.evictSession(realId);
-               sessionPassivated();
-            }
-            finally {
-               SessionReplicationContext.finishCacheActivity();
-            }
-            
-            // Put the session in the unloadedSessions map. This will
-            // expose the session to regular invalidation.
-            Object obj = unloadedSessions_.put(realId, 
-                  new OwnedSessionUpdate(null, session.getLastAccessedTime(), session.getMaxInactiveInterval(), true));
-            if (trace_)
-            {
-               if (obj == null)
-               {
-                  log_.trace("New session " + realId + " added to unloaded session map");
-               }
-               else
-               {
-                  log_.trace("Updated timestamp for unloaded session " + realId);
-               }
-            }
-            sessions_.remove(realId);
-         }
-      }
-      else if (trace_)
-      {
-         log_.trace("processSessionPassivation():  could not find session " + realId);
-      }
-   }
-   
-   /**
-    * Session passivation logic for sessions only in the distributed store.
-    * 
-    * @param realId the session id, minus any jvmRoute
-    */
-   private void processUnloadedSessionPassivation(String realId, OwnedSessionUpdate osu)
-   {
-      if (trace_)
-      {
-         log_.trace("Passivating session with id: " + realId);
-      }
-
-      try {
-         // Tell the proxy to ignore cache notifications we are about
-         // to generate for this session.
-         SessionReplicationContext.startCacheActivity();
-         proxy_.evictSession(realId, osu.owner);
-         osu.passivated = true;
-         sessionPassivated();
-      }
-      finally {
-         SessionReplicationContext.finishCacheActivity();
-      }
-      
-   }
-   
-   private void sessionPassivated()
-   {
-      int pc = passivatedCount_.incrementAndGet();
-      int max = maxPassivatedCount_.get();
-      while (pc > max)
-      {
-         if (!maxPassivatedCount_ .compareAndSet(max, pc))
-         {
-            max = maxPassivatedCount_.get();
-         }
-      }
-   }
-   
-   // -----------------------------------------------  Lifecyle When Unembedded
-
-   /**
-    * Start this Manager when running in standalone Tomcat.
-    */
-   private void startUnembedded() throws LifecycleException
-   {
-      if (started_)
-      {
-         return;
-      }
-      
-      log_.info("Manager is about to start");      
-
-      // Notify our interested LifecycleListeners
-      lifecycle_.fireLifecycleEvent(BEFORE_START_EVENT, this);
-      
-      if (snapshotMode_ == null) 
-      {
-         // We were not instantiated by a JBossCacheCluster, so we need to
-         // find one and let it configure our cluster-wide properties
-         try 
-         {
-            JBossCacheCluster cluster = (JBossCacheCluster) container_.getCluster();
-            cluster.configureManager(this);
-         }
-         catch (ClassCastException e)
-         {
-            String msg = "Cluster is not an instance of JBossCacheCluster";
-            log_.error(msg, e);
-            throw new LifecycleException(msg, e);
-         }
-      }
-      
-      // Validate attributes
-      
-      // Create the JBossCacheService
-      try
-      {
-         initCacheProxy();
-         
-         // Confirm our replication granularity is compatible with the cache
-         // Throws ISE if not
-         validateFieldMarshalling();
-         
-         // We need to pass the classloader that is associated with this 
-         // web app so de-serialization will work correctly.
-         tcl_ = container_.getLoader().getClassLoader();
-         proxy_.start(tcl_, this);
-      }
-      catch (Throwable t)
-      {
-         String str = "Problem starting JBossCacheService for Tomcat clustering";
-         log_.error(str, t);
-         throw new LifecycleException(str, t);
-      }
-
-      tm = proxy_.getTransactionManager();
-      if(tm == null)
-      {
-         throw new LifecycleException("JBossCacheManager.start(): Obtain null tm");
-      }
-      
-      try
-      {
-         initializeUnloadedSessions();
-
-         // Add SnapshotValve and, if needed, JvmRouteValve and batch repl valve
-         installValves();
-
-         started_ = true;
-         
-         // Notify our interested LifecycleListeners
-         lifecycle_.fireLifecycleEvent(AFTER_START_EVENT, this);
-         
-         log_.debug("start(): JBossCacheService started");
-      } 
-      catch (Exception e)
-      {
-         log_.error("Unable to start manager.", e);
-         throw new LifecycleException(e);
-      }
-      
-      registerManagerMBean();
-   }
-
-   /**
-    * Accesses the underlying cache and creates the proxy
-    * 
-    * @throws ClusteringNotSupportedException
-    */
-   private void initCacheProxy() throws ClusteringNotSupportedException
-   {
-      // We are likely going to cause creation and start of a cache here;
-      // we don't want to leak the TCCL to cache/jgroups threads,
-      // so we switch it to our classloader
-      ContextClassLoaderSwitcher switcher = (ContextClassLoaderSwitcher) AccessController.doPrivileged(ContextClassLoaderSwitcher.INSTANTIATOR);
-      ContextClassLoaderSwitcher.SwitchContext switchContext = switcher.getSwitchContext(getClass().getClassLoader());
-      try
-      {
-         if (ReplicationGranularity.FIELD == replicationGranularity_)
-         {
-            PojoCache pojoC = getPojoCache();
-            if (pojoC == null)
-            {
-               pojoC = Util.findPojoCache(cacheConfigName_);
-               
-               cacheFromCacheManager_ = true;
-               
-               if (pojoC.getCache().getCacheStatus() != CacheStatus.STARTED)
-                  pojoC.getCache().start();
-            }
-            proxy_ = new FieldBasedJBossCacheService(pojoC);
-         }
-         else
-         {
-            Cache pc = getPlainCache();
-            if (pc == null)
-            {
-               pc = Util.findPlainCache(cacheConfigName_);
-               
-               cacheFromCacheManager_ = true;
-               
-               if (pc.getCacheStatus() != CacheStatus.STARTED)
-                  pc.start();
-            }
-            proxy_ = new JBossCacheService(pc);
-         }
-      }
-      finally
-      {
-         // Restore the TCCL
-         switchContext.reset();
-      }
-   }
-   
-   /**
-    * Gets the ids of all sessions in the distributed cache and adds
-    * them to the unloaded sessions map, along with their lastAccessedTime
-    * and their maxInactiveInterval. Passivates overage or excess sessions.
-    */
-   private void initializeUnloadedSessions() throws CacheException
-   {      
-      Map<String, String> sessions = proxy_.getSessionIds();
-      if (sessions != null)
-      {
-         boolean passivate = isPassivationEnabled();
-
-         long passivationMax = passivationMaxIdleTime_ * 1000L;
-         long passivationMin = passivationMinIdleTime_ * 1000L;
-         
-         for (Map.Entry<String, String> entry : sessions.entrySet())
-         {
-            String realId = entry.getKey();
-            String owner = entry.getValue();
-
-            SessionTimestamp ts = null;
-            SessionMetadata md = null;
-            try
-            {
-               Map sessionData = proxy_.getSessionData(realId, owner);
-               ts = (SessionTimestamp) sessionData.get(JBossCacheService.TIMESTAMP_KEY);
-               md = (SessionMetadata) sessionData.get(JBossCacheService.METADATA_KEY);
-            }
-            catch (Exception e)
-            {
-               // most likely a lock conflict if the session is being updated remotely; 
-               // ignore it and use default values for timstamp and maxInactive
-               log_.debug("Problem reading metadata for session " + realId + " -- " + e.toString());               
-            }
-            
-            long lastMod = ts == null ? System.currentTimeMillis() : ts.timestamp;
-            int maxLife = md == null ? getMaxInactiveInterval() : md.maxInactiveInterval;
-            
-            OwnedSessionUpdate osu = new OwnedSessionUpdate(owner, lastMod, maxLife, false);
-            unloadedSessions_.put(realId, osu);
-            if (passivate)
-            {
-               try
-               {
-                  long elapsed = System.currentTimeMillis() - lastMod;
-                  // if maxIdle time configured, means that we need to passivate sessions that have
-                  // exceeded the max allowed idle time
-                  if (passivationMax >= 0 
-                        && elapsed > passivationMax)
-                  {
-                     if (trace_)
-                     {
-                        log_.trace("Elapsed time of " + elapsed + " for session "+ 
-                              realId + " exceeds max of " + passivationMax + "; passivating");
-                     }
-                     processUnloadedSessionPassivation(realId, osu);
-                  }
-                  // If the session didn't exceed the passivationMaxIdleTime_, see   
-                  // if the number of sessions managed by this manager greater than the max allowed 
-                  // active sessions, passivate the session if it exceed passivationMinIdleTime_ 
-                  else if (maxActiveAllowed_ > 0 
-                              && passivationMin >= 0 
-                              && calcActiveSessions() > maxActiveAllowed_ 
-                              && elapsed >= passivationMin)
-                  {
-                     if (trace_)
-                     {
-                        log_.trace("Elapsed time of " + elapsed + " for session "+ 
-                              realId + " exceeds min of " + passivationMin + "; passivating");
-                     }
-                     processUnloadedSessionPassivation(realId, osu);
-                  }
-               }
-               catch (Exception e)
-               {
-                  // most likely a lock conflict if the session is being updated remotely; ignore it
-                  log_.debug("Problem passivating session " + realId + " -- " + e.toString());
-               }
-            }
-         }
-      }
-   }
-
-   /**
-    * Instantiate a SnapshotManager and ClusteredSessionValve and add 
-    * the valve to our parent Context's pipeline.  
-    * Add a JvmRouteValve and BatchReplicationClusteredSessionValve if needed.
-    */
-   private void installValves()
-   {      
-      // If JK usage wasn't explicitly configured, default to enabling
-      // it if jvmRoute is set on our containing Engine
-      if (useJK_ == null)
-      {
-         useJK_ = Boolean.valueOf(getJvmRoute() != null);
-      }
-      
-      if (getUseJK())
-      {
-         log_.debug("We are using JK for load-balancing. Adding JvmRouteValve.");         
-         installContextValve(new JvmRouteValve(this));         
-      }   
-      
-      // Handle batch replication if needed.
-      // TODO -- should we add this even if not FIELD in case a cross-context
-      // call traverses a field-based webapp?   
-      TransactionManager valveTM = null;
-      if (replicationGranularity_ == ReplicationGranularity.FIELD
-            && Boolean.TRUE.equals(replicationFieldBatchMode_))
-      {
-         valveTM = this.tm;
-         log_.debug("Including transaction manager in ClusteredSessionValve to support batch replication.");
-      }
-
-      // Add clustered session valve
-      ClusteredSessionValve valve = new ClusteredSessionValve(this, valveTM);
-      log_.debug("Adding ClusteredSessionValve");
-      installContextValve(valve);
-   }
-
-   /**
-    * Create and start a snapshot manager.
-    */
-   private void initSnapshotManager()
-   {
-      String ctxPath = ((Context) container_).getPath();
-      if (SnapshotMode.INSTANT == snapshotMode_)
-      {
-         snapshotManager_ = new InstantSnapshotManager(this, ctxPath);
-      }
-      else if (snapshotMode_ == null)
-      {
-         log_.warn("Snapshot mode must be 'instant' or 'interval' - " +
-                   "using 'instant'");
-         snapshotMode_ = SnapshotMode.INSTANT;
-         snapshotManager_ = new InstantSnapshotManager(this, ctxPath);
-      }
-      else if (ReplicationGranularity.FIELD == replicationGranularity_)
-      {
-         throw new IllegalStateException("Property snapshotMode must be " + 
-               SnapshotMode.INTERVAL + " when FIELD granularity is used");
-      }
-      else if (snapshotInterval_ < 1)
-      {
-         log_.warn("Snapshot mode set to 'interval' but snapshotInterval is < 1 " +
-                   "using 'instant'");
-         snapshotMode_ = SnapshotMode.INSTANT;
-         snapshotManager_ = new InstantSnapshotManager(this, ctxPath);         
-      }
-      else
-      {
-         snapshotManager_ = new IntervalSnapshotManager(this, ctxPath, snapshotInterval_);
-      }
-      
-      snapshotManager_.start();
-   }
-   
-   private void installContextValve(Valve valve)
-   {
-      boolean installed = false;
-      
-      // In embedded mode, install the valve via JMX to be consistent
-      // with the way the overall context is created in TomcatDeployer.
-      // We can't do this in unembedded mode because we are called
-      // before our Context is registered with the MBean server
-      if (embedded_ && getContextObjectName() != null) {
-         try
-         {
-            getMBeanServer().invoke(getContextObjectName(), "addValve",
-                                    new Object[]{valve},
-                                    new String[]{"org.apache.catalina.Valve"});
-            installed = true;
-         }
-         catch (Exception e)
-         {
-            // JBAS-2422.  If the context is restarted via JMX, the above
-            // JMX call will fail as the context will not be registered
-            // when it's made.  So we catch the exception and fall back
-            // to adding the valve directly.
-            // TODO consider skipping adding via JMX and just do it directly
-            log_.debug("Caught exception installing valve to Context", e);
-         }
-      }
-      
-      if (!installed)
-      {
-         // If possible install via the ContainerBase.addValve() API.
-         if (container_ instanceof ContainerBase)
-         {            
-            ((ContainerBase) container_).addValve(valve);
-         }
-         else
-         {
-            // No choice; have to add it to the context's pipeline
-            container_.getPipeline().addValve(valve);
-         }
-      }
-   }
-
-   /**
-    * If we are using FIELD granularity, checks that the TreeCache
-    * supports marshalling.
-    * 
-    * @throws IllegalStateException if not
-    */
-   private void validateFieldMarshalling()
-   {
-      if (replicationGranularity_ == ReplicationGranularity.FIELD
-            && !proxy_.isMarshallingAvailable())
-      {
-         // BES 16/8/2006 -- throw ISE, not ClusteringNotSupportedException, as a
-         // misconfig should be treated differently from the absence of clustering 
-         // services
-         throw new IllegalStateException("replication-granularity value is set to " +
-               "'FIELD' but is not supported by the cache service configuration. " +
-               "Must set 'UseRegionBasedMarshalling' to 'true' in the " +
-               "jboss-web-cluster.sar jboss-service.xml");
-      }
-   }
-   
-   private ObjectName getContextObjectName()
-   {
-      String oname = container_.getObjectName();
-      try
-      {         
-         return (oname == null) ? null : new ObjectName(oname);
-      }
-      catch (MalformedObjectNameException e)
-      {
-         log_.warn("Error creating object name from string " + oname, e);
-         return null;
-      }
-   }
-   
-   private void releaseCacheToManager()
-   {      
-      try
-      {
-         if (replicationGranularity_ == ReplicationGranularity.FIELD)
-         {
-            PojoCacheManager pcm = PojoCacheManagerLocator.getCacheManagerLocator().getCacheManager(null);
-            pcm.releaseCache(cacheConfigName_);
-         }
-         else
-         {
-            CacheManager cm = CacheManagerLocator.getCacheManagerLocator().getCacheManager(null);
-            cm.releaseCache(cacheConfigName_);
-         }
-      }
-      catch (Exception e)
-      {
-         log_.error("Problem releasing cache to CacheManager -- config is " + cacheConfigName_, e);
-      }
-   }
-
-   /**
-    * Clear the underlying cache store.
-    */
-   private void clearSessions()
-   {
-      boolean passivation = isPassivationEnabled();
-      // First, the sessions we have actively loaded
-      ClusteredSession[] sessions = findLocalSessions();
-      for(int i=0; i < sessions.length; i++)
-      {
-         ClusteredSession ses = sessions[i];
-         
-         if (trace_)
-         {
-             log_.trace("clearSessions(): clear session by expiring or passivating: " + ses);
-         }
-         try
-         {
-            // if session passivation is enabled, passivate sessions instead of expiring them which means
-            // they'll be available to the manager for activation after a restart. 
-            if(passivation && ses.isValid())
-            {               
-               processSessionPassivation(ses.getRealId());
-            }
-            else
-            {               
-               boolean notify = true;
-               boolean localCall = true;
-               boolean localOnly = true;
-               ses.expire(notify, localCall, localOnly);               
-            }
-         }
-         catch (Throwable t)
-         {
-            log_.warn("clearSessions(): Caught exception expiring or passivating session " +
-                     ses.getIdInternal(), t);
-         }
-         finally
-         {
-            // Guard against leaking memory if anything is holding a
-            // ref to the session by clearing its internal state
-            ses.recycle();
-         }
-      }      
-      
-      String action = passivation ? "evicting" : "removing";
-      Set<Map.Entry<String, OwnedSessionUpdate>> unloaded = 
-               unloadedSessions_.entrySet();
-      for (Iterator<Map.Entry<String, OwnedSessionUpdate>> it = unloaded.iterator(); it.hasNext();)
-      {
-         Map.Entry<String, OwnedSessionUpdate> entry = it.next();
-         String realId = entry.getKey();         
-         try
-         {
-            if (passivation)
-            {
-               OwnedSessionUpdate osu = entry.getValue();
-               // Ignore the marker entries for our passivated sessions
-               if (!osu.passivated)
-               {
-                  proxy_.evictSession(realId, osu.owner);
-               }
-            }
-            else
-            {
-               proxy_.removeSessionLocal(realId, false);           
-            }
-         }
-         catch (Exception e)
-         {
-            // Not as big a problem; we don't own the session
-            log_.debug("Problem " + action + " session " + realId + " -- " + e);
-         }
-         it.remove(); 
-      }
-   }
-   
-   // ------------------------------------------------------  Lifecyle Embedded
-   
-   /**
-    * Start this Manager when running embedded in JBoss AS.
-    *
-    * @throws org.apache.catalina.LifecycleException
-    */
-   private void startEmbedded() throws LifecycleException
-   {
-      super.start();
-      
-      // Start the JBossCacheService
-      // Will need to pass the classloader that is associated with this 
-      // web app so de-serialization will work correctly.
-      tcl_ = super.getContainer().getLoader().getClassLoader();
-      
-      try
-      {
-         if (proxy_ == null) // Could happen during a restart
-         {
-            initCacheProxy();
-         }
-         
-         proxy_.start(tcl_, this);
-
-         tm = proxy_.getTransactionManager();
-         if(tm == null)
-         {
-            throw new LifecycleException("JBossCacheManager.start(): Obtain null tm");
-         }
-         
-         initializeUnloadedSessions();
-         
-         // Setup our SnapshotManager
-         initSnapshotManager();
-         
-         // Add SnapshotValve and, if needed, JvmRouteValve and batch repl valve
-         installValves();
-
-         log_.debug("start(): JBossCacheService started");         
-      }
-      catch (LifecycleException le)
-      {
-         throw le;
-      }
-      catch (Exception e)
-      {
-         log_.error("Unable to start manager.", e);
-         throw new LifecycleException(e);
-      }
-   }
-
-   // -------------------------------------------------------------------- Misc
-   
-   private void endTransaction(String id)
-   {
-      if (tm == null)
-      {
-         log_.warn("JBossCacheManager.endTransaction(): tm is null for id: " +id);
-         return;
-      }
-
-      try
-      {
-         if(tm.getTransaction().getStatus() != Status.STATUS_MARKED_ROLLBACK)
-         {
-            tm.commit();
-         }
-         else
-         {
-            log_.info("JBossCacheManager.endTransaction(): rolling back tx for id: " +id);
-            tm.rollback();
-         }
-      }
-      catch (RollbackException re)
-      {
-         // Do nothing here since cache may rollback automatically.
-         log_.warn("JBossCacheManager.endTransaction(): rolling back transaction with exception: " +re);
-      }
-      catch (Exception e)
-      {
-         throw new RuntimeException("JBossCacheManager.endTransaction(): Exception for id: " +id, e);
-      }
-   }
-
-   /**
-    * Gets the session id with any jvmRoute removed.
-    * 
-    * @param id a session id with or without an appended jvmRoute.
-    *           Cannot be <code>null</code>.
-    */
-   private String getRealId(String id)
-   {
-      return (getUseJK() ? Util.getRealId(id) : id);
-   }
-   
-   private String reportSessionIds(Set ids)
-   {
-      StringBuffer sb = new StringBuffer();
-      boolean added = false;
-      for (Iterator it = ids.iterator(); it.hasNext(); )
-      {
-         if (added)
-         {
-            sb.append(',');
-         }
-         else
-         {
-            added = true;
-         }
-         
-         sb.append(it.next());
-      }
-      return sb.toString();
-   }   
-   
-   // ------------------------------------------------------------ Inner Classes
-   
-   private class OwnedSessionUpdate
-   {
-      String owner;
-      long updateTime;
-      int maxInactive;
-      boolean passivated;
-      
-      OwnedSessionUpdate(String owner, long updateTime, int maxInactive, boolean passivated)
-      {
-         this.owner = owner;
-         this.updateTime = updateTime;
-         this.maxInactive = maxInactive;
-         this.passivated = passivated;
-      }
-   }
-}

Added: trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/JBossCacheManager.java
===================================================================
--- trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/JBossCacheManager.java	                        (rev 0)
+++ trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/JBossCacheManager.java	2008-08-26 16:41:17 UTC (rev 77496)
@@ -0,0 +1,2154 @@
+/*
+* JBoss, Home of Professional Open Source
+* Copyright 2005, JBoss Inc., and individual contributors as indicated
+* by the @authors tag. See the copyright.txt 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.web.tomcat.service.session;
+
+import java.util.Date;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import javax.management.MalformedObjectNameException;
+import javax.management.ObjectName;
+
+import org.apache.catalina.Context;
+import org.apache.catalina.LifecycleException;
+import org.apache.catalina.Session;
+import org.apache.catalina.Valve;
+import org.apache.catalina.core.ContainerBase;
+import org.jboss.cache.CacheException;
+import org.jboss.metadata.web.jboss.JBossWebMetaData;
+import org.jboss.metadata.web.jboss.ReplicationConfig;
+import org.jboss.metadata.web.jboss.ReplicationGranularity;
+import org.jboss.metadata.web.jboss.ReplicationTrigger;
+import org.jboss.metadata.web.jboss.SnapshotMode;
+import org.jboss.web.tomcat.service.session.distributedcache.impl.jbc2.JBossCacheService;
+import org.jboss.web.tomcat.service.session.distributedcache.spi.BatchingManager;
+import org.jboss.web.tomcat.service.session.distributedcache.spi.ClusteringNotSupportedException;
+import org.jboss.web.tomcat.service.session.distributedcache.spi.DistributableSessionMetadata;
+import org.jboss.web.tomcat.service.session.distributedcache.spi.DistributableSessionTimestamp;
+import org.jboss.web.tomcat.service.session.distributedcache.spi.DistributedCacheManager;
+import org.jboss.web.tomcat.service.session.distributedcache.spi.DistributedCacheManagerFactory;
+import org.jboss.web.tomcat.service.session.distributedcache.spi.DistributedCacheManagerFactoryFactory;
+import org.jboss.web.tomcat.service.session.distributedcache.spi.FieldBasedDistributedCacheManager;
+import org.jboss.web.tomcat.service.session.distributedcache.spi.LocalDistributableSessionManager;
+
+/**
+ * Implementation of a clustered session manager for
+ * catalina using JBossCache replication.
+ *
+ * @author Ben Wang
+ * @author Brian Stansberry
+ * @author Hany Mesha
+ * @version $Revision: 58587 $
+ */
+public class JBossCacheManager
+   extends JBossManager
+   implements JBossCacheManagerMBean, LocalDistributableSessionManager
+{
+   // --------------------------------------------------------------- Constants
+   
+   /**
+    * Informational name for this Catalina component
+    */
+   private static final String info_ = "JBossCacheManager/1.0";
+
+   // ------------------------------------------------------------------ Fields
+
+   /** The transaction manager. */
+   private BatchingManager batchingManager;
+
+   /** Proxy-object for the JBossCache */
+   private DistributedCacheManager proxy_;
+
+   /** The factory for our distributed cache manager */
+   private final DistributedCacheManagerFactory distributedCacheManagerFactory;
+   
+   /** Id/timestamp of sessions in distributedcache that we haven't loaded locally*/
+   private Map<String, OwnedSessionUpdate> unloadedSessions_ = 
+         new ConcurrentHashMap<String, OwnedSessionUpdate>();
+   
+   /** Number of passivated sessions */
+   private AtomicInteger passivatedCount_ = new AtomicInteger();
+   
+   /** Maximum number of concurrently passivated sessions */
+   private AtomicInteger maxPassivatedCount_ = new AtomicInteger();
+   
+   /** If set to true, will add a JvmRouteFilter to the request. */
+   private Boolean useJK_;
+
+   /** Are we running embedded in JBoss? */
+   private boolean embedded_ = false;
+
+   /** Our ClusteredSessionValve's snapshot mode. */
+   private SnapshotMode snapshotMode_ = null;
+
+   /** Our ClusteredSessionValve's snapshot interval. */
+   private int snapshotInterval_ = 0;
+   
+   /** Replication granularity. */
+   private ReplicationGranularity replicationGranularity_;
+
+   /** Policy to determine if a session is dirty */
+   private ReplicationTrigger replicationTrigger_;
+
+   /**
+    * Whether we use batch mode replication for field level granularity.
+    * We store this in a Boolean rather than a primitive so JBossCacheCluster
+    * can determine if this was set via a <Manager> element.
+    */
+   private Boolean replicationFieldBatchMode_;
+   
+   /** Class loader for this web app. */
+   private ClassLoader tcl_;
+   
+   /** The snapshot manager we are using. */
+   private SnapshotManager snapshotManager_;
+   
+   /** The name of our cache's configuration */
+   private String cacheConfigName_;
+   
+   private int maxUnreplicatedInterval_ = -1;
+
+   //  ----------------------------------------------------------  Constructors
+
+   public JBossCacheManager() throws ClusteringNotSupportedException
+   {
+      this(DistributedCacheManagerFactoryFactory.getInstance().getDistributedCacheManagerFactory());
+   }
+   
+   public JBossCacheManager(DistributedCacheManagerFactory distributedManagerFactory)
+   {
+      assert distributedManagerFactory != null : "distributedManagerFactory is null";
+      
+      this.distributedCacheManagerFactory = distributedManagerFactory;
+   }
+
+   // ---------------------------------------------------- AbstractJBossManager
+
+   /**
+    * {@inheritDoc}
+    * <p>
+    * <strong>NOTE:</strong> This method should not be called when
+    * running unembedded.
+    * </p>
+    */
+   @Override
+   public void init(String name, JBossWebMetaData webMetaData)
+      throws ClusteringNotSupportedException
+   {
+      super.init(name, webMetaData);
+      
+      ReplicationConfig repCfg = webMetaData.getReplicationConfig();
+      if (repCfg != null)
+      {         
+         replicationTrigger_ = repCfg.getReplicationTrigger();
+         replicationGranularity_ = repCfg.getReplicationGranularity();
+      }       
+      
+      // Only configure JK usage if it was explicitly set; otherwise
+      // wait until we're starting when we can check for a jvmRoute
+      // in our containing Engine
+      Boolean jk = repCfg.getUseJK();
+      if (jk != null)
+      {
+         this.useJK_ = jk;
+      }
+      
+      Boolean batch = repCfg.getReplicationFieldBatchMode();
+      this.replicationFieldBatchMode_ = (batch == null ? Boolean.TRUE : batch);
+      setSnapshotMode(repCfg.getSnapshotMode());
+      Integer snapshotInt = repCfg.getSnapshotInterval();
+      setSnapshotInterval(snapshotInt == null ? 0 : snapshotInt.intValue());
+      
+      Integer maxUnrep = repCfg.getMaxUnreplicatedInterval();
+      if (maxUnrep != null)
+      {
+         this.maxUnreplicatedInterval_ = maxUnrep.intValue();
+      }
+      
+      log_.debug("init(): replicationGranularity_ is " + replicationGranularity_ +
+         " and replicationTrigger is " + replicationTrigger_ +
+         " and replicationFieldBatchMode is " + replicationFieldBatchMode_ +
+         " and useJK is " + useJK_ +
+         " and snapshotMode is " + snapshotMode_ +
+         " and snapshotInterval is " + snapshotInterval_);     
+      
+      this.cacheConfigName_ = repCfg.getCacheName();
+      
+      // Initing the proxy would be better in start, but we do it here so we
+      // can detect ClusteringNotSupportedException at this deploy stage
+      initCacheProxy();
+
+      embedded_ = true;
+   }
+
+   /**
+    * {@inheritDoc}
+    * <p>
+    * Removes the session from this Manager's collection of actively managed
+    * sessions.  Also removes the session from this server's copy of the
+    * distributed cache (but does not remove it from other servers'
+    * distributed cache).
+    * </p>
+    */
+   public void removeLocal(Session session)
+   {
+      ClusteredSession clusterSess = (ClusteredSession) session;
+      synchronized (clusterSess)
+      {
+         String realId = clusterSess.getRealId();
+         if (realId == null) return;
+
+         if (trace_)
+         {
+            log_.trace("Removing session from local store with id: " + realId);
+         }
+
+         try {
+            // Ignore any cache notifications that our own work generates
+            SessionReplicationContext.startCacheActivity();
+            clusterSess.removeMyselfLocal();
+         }
+         finally
+         {
+            SessionReplicationContext.finishCacheActivity();
+            
+            // We don't want to replicate this session at the end
+            // of the request; the removal process took care of that
+            SessionReplicationContext.sessionExpired(clusterSess, realId, snapshotManager_);
+            
+            sessions_.remove(realId);
+            stats_.removeStats(realId);
+
+            // Compute how long this session has been alive, and update
+            // our statistics accordingly
+            int timeAlive = (int) ((System.currentTimeMillis() - clusterSess.getCreationTimeInternal())/1000);
+            sessionExpired(timeAlive);
+         }
+      }
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   public boolean storeSession(Session baseSession)
+   {
+      boolean stored = false;
+      if(baseSession != null && started_)
+      {
+         ClusteredSession session = (ClusteredSession) baseSession;
+
+         synchronized (session)
+         {
+            if (trace_)
+            {
+               log_.trace("check to see if needs to store and replicate " +
+                          "session with id " + session.getIdInternal());
+            }
+
+            if (session.isValid() &&
+                  (session.isSessionDirty() || session.getMustReplicateTimestamp()))
+            {
+               String realId = session.getRealId();
+
+               // Notify all session attributes that they get serialized (SRV 7.7.2)
+               long begin = System.currentTimeMillis();
+               session.passivate();
+               long elapsed = System.currentTimeMillis() - begin;
+               stats_.updatePassivationStats(realId, elapsed);
+
+               // Do the actual replication
+               begin = System.currentTimeMillis();
+               processSessionRepl(session);
+               elapsed = System.currentTimeMillis() - begin;
+               stored = true;
+               stats_.updateReplicationStats(realId, elapsed);
+            }
+            else if (trace_)
+            {
+               log_.trace("Session " + session.getIdInternal() + 
+                          " did not require replication.");
+            }
+         }
+      }
+
+      return stored;
+   }
+
+   // ----------------------------------------------------------------- Manager
+
+   /**
+    * {@inheritDoc}
+    */
+   public void add(Session session)
+   {
+      if (session == null)
+         return;
+
+      if (!(session instanceof ClusteredSession))
+      {
+         throw new IllegalArgumentException("You can only add instances of " +
+               "type ClusteredSession to this Manager. Session class name: " +
+               session.getClass().getName());
+      }
+
+//      add((ClusteredSession) session, true);
+      add((ClusteredSession) session, false); // wait to replicate until req end
+   }
+
+   /**
+    * Adds the given session to the collection of those being managed by this
+    * Manager.
+    *
+    * @param session   the session. Cannot be <code>null</code>.
+    * @param replicate whether the session should be replicated
+    *
+    * @throws NullPointerException if <code>session</code> is <code>null</code>.
+    */
+   private void add(ClusteredSession session, boolean replicate)
+   {
+      // TODO -- why are we doing this check? The request checks session 
+      // validity and will expire the session; this seems redundant
+      if (!session.isValid())
+      {
+         // Not an error; this can happen if a failover request pulls in an
+         // outdated session from the distributed cache (see TODO above)
+         log_.debug("Cannot add session with id=" + session.getIdInternal() +
+                    " because it is invalid");
+         return;
+      }
+
+      String realId = session.getRealId();
+      Object existing = sessions_.put(realId, session);
+      unloadedSessions_.remove(realId);
+
+      if (!session.equals(existing))
+      {
+         if (replicate)
+         {
+            storeSession(session);
+         }
+
+         // Update counters
+         calcActiveSessions();
+         
+         if (trace_)
+         {
+            log_.trace("Session with id=" + session.getIdInternal() + " added. " +
+                       "Current active sessions " + localActiveCounter_.get());
+         }
+      }
+   }
+
+   // Satisfy the Manager interface.  Internally we use
+   // createEmptyClusteredSession to avoid a cast
+   public Session createEmptySession()
+   {
+      if (trace_)
+      {
+         log_.trace("Creating an empty ClusteredSession");
+      }
+      
+      return createEmptyClusteredSession();
+   }
+   
+   /**
+    * {@inheritDoc}
+    */
+   public Session createSession()
+   {
+      return createSession(null);
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   public Session createSession(String sessionId)
+   {      
+      // First check if we've reached the max allowed sessions, 
+      // then try to expire/passivate sessions to free memory
+      // maxActiveAllowed_ -1 is unlimited
+      // We check here for maxActive instead of in add().  add() gets called
+      // when we load an already existing session from the distributed cache
+      // (e.g. in a failover) and we don't want to fail in that situation.
+
+      if(maxActiveAllowed_ != -1 && calcActiveSessions() >= maxActiveAllowed_)
+      {
+         if (trace_)
+         {
+            log_.trace("createSession(): active sessions = " + calcActiveSessions() +
+                       " and max allowed sessions = " + maxActiveAllowed_);
+         }
+         
+         processExpirationPassivation();
+         
+         if (calcActiveSessions() >= maxActiveAllowed_)
+         {
+            // Exceeds limit. We need to reject it.
+            rejectedCounter_.incrementAndGet();
+            // Catalina api does not specify what happens
+            // but we will throw a runtime exception for now.
+            String msgEnd = (sessionId == null) ? "" : " id " + sessionId;
+            throw new IllegalStateException("createSession(): number of " +
+                   "active sessions exceeds the maximum limit: " +
+                   maxActiveAllowed_ + " when trying to create session" + msgEnd);
+         }
+      }
+
+      ClusteredSession session = createEmptyClusteredSession();
+
+      session.setNew(true);
+      session.setCreationTime(System.currentTimeMillis());
+      session.setMaxInactiveInterval(this.maxInactiveInterval_);
+      session.setValid(true);
+
+      if (sessionId == null)
+      {
+          sessionId = this.getNextId();
+
+          // We are using mod_jk for load balancing. Append the JvmRoute.
+          if (getUseJK())
+          {
+              if (trace_)
+              {
+                  log_.trace("createSession(): useJK is true. Will append JvmRoute: " + this.getJvmRoute());
+              }
+              sessionId += "." + this.getJvmRoute();
+          }
+      }
+
+      session.setId(sessionId); // Setting the id leads to a call to add()
+
+      if (trace_)
+      {
+         log_.trace("Created a ClusteredSession with id: " + sessionId);
+      }
+
+      createdCounter_.incrementAndGet(); // the call to add() handles the other counters 
+      
+      // Add this session to the set of those potentially needing replication
+      SessionReplicationContext.bindSession(session, snapshotManager_);
+      
+      return session;
+   }
+
+   /**
+    * Attempts to find the session in the collection of those being managed
+    * locally, and if not found there, in the distributed cache of sessions.
+    * <p>
+    * If a session is found in the distributed cache, it is added to the
+    * collection of those being managed locally.
+    * </p>
+    *
+    * @param id the session id, which may include an appended jvmRoute
+    *
+    * @return the session, or <code>null</code> if no such session could
+    *         be found
+    */
+   public Session findSession(String id)
+   {
+      String realId = getRealId(id);
+      // Find it from the local store first
+      ClusteredSession session = findLocalSession(realId);
+      
+      // If we didn't find it locally, only check the distributed cache
+      // if we haven't previously handled this session id on this request.
+      // If we handled it previously but it's no longer local, that means
+      // it's been invalidated. If we request an invalidated session from
+      // the distributed cache, it will be missing from the local cache but
+      // may still exist on other nodes (i.e. if the invalidation hasn't 
+      // replicated yet because we are running in a tx). With buddy replication,
+      // asking the local cache for the session will cause the out-of-date
+      // session from the other nodes to be gravitated, thus resuscitating
+      // the session.
+      if (session == null 
+            && !SessionReplicationContext.isSessionBoundAndExpired(realId, snapshotManager_))
+      {
+         if (trace_)
+            log_.trace("Checking for session " + realId + " in the distributed cache");
+         
+         session = loadSession(realId);
+         if (session != null)
+         {
+            add(session);
+            // TODO should we advise of a new session?
+            //tellNew();
+         }
+      }
+      else if (session != null && session.isOutdated())
+      {
+         if (trace_)
+            log_.trace("Updating session " + realId + " from the distributed cache");
+         
+         // Need to update it from the cache
+         loadSession(realId);
+      }
+
+      if (session != null)
+      {
+         // Add this session to the set of those potentially needing replication
+         SessionReplicationContext.bindSession(session, snapshotManager_);
+      }
+
+      return session;
+   }
+
+   /**
+    * Return the sessions. Note that this will return not only the local
+    * in-memory sessions, but also any sessions that are in the distributed
+    * cache but have not previously been accessed on this server.  Invoking
+    * this method will bring all such sessions into local memory and can
+    * potentially be quite expensive.
+    *
+    * <p>
+    * Note also that when sessions are loaded from the distributed cache, no
+    * check is made as to whether the number of local sessions will thereafter
+    * exceed the maximum number allowed on this server.
+    * </p>
+    *
+    * @return an array of all the sessions
+    */
+   public Session[] findSessions()
+   {
+      // Need to load all the unloaded sessions
+      if(unloadedSessions_.size() > 0)
+      {
+         // Make a thread-safe copy of the new id list to work with
+         Set ids = new HashSet(unloadedSessions_.keySet());
+
+         if(trace_) {
+            log_.trace("findSessions: loading sessions from distributed cache: " + ids);
+         }
+
+         for(Iterator it = ids.iterator(); it.hasNext();) {
+            loadSession((String) it.next());
+         }
+      }
+
+      // All sessions are now "local" so just return the local sessions
+      return findLocalSessions();
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   public String getInfo()
+   {
+      return info_;
+   }
+
+   /**
+    * Removes the session from this Manager's collection of actively managed
+    * sessions.  Also removes the session from the distributed cache, both
+    * on this server and on all other server to which this one replicates.
+    */
+   public void remove(Session session)
+   {
+      ClusteredSession clusterSess = (ClusteredSession) session;
+      synchronized (clusterSess)
+      {
+         String realId = clusterSess.getRealId();
+         if (realId == null)
+            return;
+
+         if (trace_)
+         {
+            log_.trace("Removing session from store with id: " + realId);
+         }
+
+         try {
+            // Ignore any cache notifications that our own work generates
+            SessionReplicationContext.startCacheActivity();
+            clusterSess.removeMyself();
+         }
+         finally {
+            SessionReplicationContext.finishCacheActivity();
+            
+            // We don't want to replicate this session at the end
+            // of the request; the removal process took care of that
+            SessionReplicationContext.sessionExpired(clusterSess, realId, snapshotManager_);
+            
+            sessions_.remove(realId);
+            stats_.removeStats(realId);
+
+            // Compute how long this session has been alive, and update
+            // our statistics accordingly
+            int timeAlive = (int) ((System.currentTimeMillis() - clusterSess.getCreationTimeInternal())/1000);
+            sessionExpired(timeAlive);
+         }
+      }
+   }
+
+   // --------------------------------------------------------------- Lifecycle
+   
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   public void start() throws LifecycleException
+   {
+      if (embedded_)
+      {
+         startEmbedded();
+      }
+      else
+      {
+         startUnembedded();
+      }
+      
+      log_.debug("JBossCacheManager for " + getContainer().getName() + " started");
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   public void stop() throws LifecycleException
+   {
+      if (!started_)
+      {
+         throw new IllegalStateException("Manager not started");
+      }
+      
+      log_.debug("Stopping");
+      
+      // Block for any ongoing backgroundProcess, then disable
+      synchronized (backgroundProcessAllowed)
+      {
+         backgroundProcessAllowed.set(false);
+      }
+      
+      resetStats();
+      
+      // Notify our interested LifecycleListeners
+      lifecycle_.fireLifecycleEvent(BEFORE_STOP_EVENT, this);
+      
+      clearSessions();
+      
+      // Don't leak the classloader
+      tcl_ = null;
+      
+      proxy_.stop();
+      proxy_ = null;
+      
+      batchingManager = null;
+      
+      snapshotManager_.stop();
+      
+      // Clean up maps
+      sessions_.clear();
+      unloadedSessions_.clear();
+      
+      passivatedCount_.set(0);
+      
+      started_ = false;
+      
+      // Notify our interested LifecycleListeners
+      lifecycle_.fireLifecycleEvent(AFTER_STOP_EVENT, this);
+      
+      unregisterManagerMBean();
+   }
+   
+
+   // -------------------------------------------------- JBossCacheManagerMBean
+
+   /**
+    * {@inheritDoc}
+    */
+   public void expireSession(String sessionId)
+   {
+      Session session = findSession(sessionId);
+      if (session != null)
+         session.expire();
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   public String getCacheConfigName()
+   {
+      return cacheConfigName_;
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   public String getCreationTime(String sessionId)
+   {
+      Session session = findSession(sessionId);
+      if(session == null) 
+      {
+         log_.info("getCreationTime(): Session " + sessionId + 
+                       " not found");
+         return "";
+      }
+     return new Date(session.getCreationTime()).toString();
+   }
+   
+   /**
+    * {@inheritDoc}
+    */
+   public int getDuplicates()
+   {
+      return duplicates_.get();
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   public String getLastAccessedTime(String sessionId)
+   {
+      Session session = findSession(sessionId);
+      if(session == null) {
+         log_.info("getLastAccessedTime(): Session " + sessionId + 
+                   " not found");
+         return "";
+      }
+     return new Date(session.getLastAccessedTime()).toString();
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   public String getSessionAttribute(String sessionId, String key)
+   {
+      Object attr = null;
+      ClusteredSession session = (ClusteredSession) findSession(sessionId);
+      if (session != null)
+      {
+         attr = session.getAttribute(key);
+      }
+      
+      return attr == null ? null : attr.toString();
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   public long getMaxPassivatedSessionCount()
+   {
+      return maxPassivatedCount_.get();
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   public int getMaxUnreplicatedInterval()
+   {
+      return maxUnreplicatedInterval_;
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   public void setMaxUnreplicatedInterval(int maxUnreplicatedInterval)
+   {
+      this.maxUnreplicatedInterval_ = maxUnreplicatedInterval;
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   public long getPassivatedSessionCount()
+   {
+      return passivatedCount_.get();
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   public long getPassivationMaxIdleTime()
+   {
+      return passivationMaxIdleTime_;
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   public long getPassivationMinIdleTime()
+   {
+      return passivationMinIdleTime_;
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   public ReplicationGranularity getReplicationGranularity()
+   {
+      return replicationGranularity_;
+   }    
+   
+   /**
+    * {@inheritDoc}
+    */
+   public ReplicationTrigger getReplicationTrigger()
+   {
+      return this.replicationTrigger_;
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   public int getSnapshotInterval()
+   {
+      return snapshotInterval_;
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   public SnapshotMode getSnapshotMode()
+   {
+      return snapshotMode_;
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   public boolean getUseJK()
+   {
+      return useJK_ == null ? false : useJK_.booleanValue();
+   }
+   
+   /**
+    * {@inheritDoc}
+    */
+   public boolean isPassivationEnabled()
+   {
+      return (passivationMode_ && proxy_.isPassivationEnabled());
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   public Boolean isReplicationFieldBatchMode()
+   {
+      return replicationFieldBatchMode_;
+   }
+   
+   /**
+    * {@inheritDoc}
+    */
+   public String listLocalSessionIds()
+   {
+      return reportSessionIds(sessions_.keySet());
+   }
+   
+   /**
+    * {@inheritDoc}
+    */
+   public String listSessionIds()
+   {
+      Set ids = new HashSet(sessions_.keySet());
+      ids.addAll(unloadedSessions_.keySet());
+      return reportSessionIds(ids);
+   }
+   
+   // -------------------------------- Callbacks from Distributed Caching Layer
+
+   public String getContextName()
+   {
+      return getContainer().getName();
+   }
+   
+   public String getHostName()
+   {
+      return getContainer().getParent().getName();
+   }
+   
+   /**
+    * Notifies the manager that a session in the distributed cache has
+    * been invalidated
+    * 
+    * @param realId the session id excluding any jvmRoute
+    */
+   public void notifyRemoteInvalidation(String realId)
+   {
+      // Remove the session from our local map
+      ClusteredSession session = (ClusteredSession) sessions_.remove(realId);
+      if (session == null)
+      {
+         // We weren't managing the session anyway.  But remove it
+         // from the list of cached sessions we haven't loaded
+         if (unloadedSessions_.remove(realId) != null)
+         {
+            if (trace_)
+               log_.trace("Removed entry for session " + realId + " from unloaded session map");
+         }
+      }
+      else
+      {
+         // Expire the session
+         // DON'T SYNCHRONIZE ON SESSION HERE -- isValid() and
+         // expire() are meant to be multi-threaded and synchronize
+         // properly internally; synchronizing externally can lead
+         // to deadlocks!!
+         boolean notify = false; // Don't notify listeners. SRV.10.7
+                                 // allows this, and sending notifications
+                                 // leads to all sorts of issues; e.g.
+                                 // circular calls with ClusteredSSO
+         boolean localCall = false; // this call originated from the cache;
+                                    // we have already removed session
+         boolean localOnly = true; // Don't pass attr removals to cache
+         
+         // Ensure the correct TCL is in place
+         ClassLoader prevTcl = Thread.currentThread().getContextClassLoader();
+         try
+         {
+            Thread.currentThread().setContextClassLoader(tcl_);
+            session.expire(notify, localCall, localOnly);
+         }
+         finally
+         {
+            Thread.currentThread().setContextClassLoader(prevTcl);
+         }
+
+         // Remove any stats for this session
+         stats_.removeStats(realId);
+      }
+   }
+   
+   /**
+    * Callback from the distributed cache notifying of a local modification
+    * to a session's attributes.  Meant for use with FIELD granularity,
+    * where the session may not be aware of modifications.
+    * 
+    * @param realId the session id excluding any jvmRoute
+    */
+   public void notifyLocalAttributeModification(String realId)
+   {
+      ClusteredSession session = (ClusteredSession) sessions_.get(realId);
+      if (session != null)
+      {
+         session.sessionAttributesDirty();
+      }
+      else
+      {
+         log_.warn("");
+      }
+   }
+   
+   public void sessionActivated()
+   {
+      int pc = passivatedCount_.decrementAndGet();
+      // Correct for drift since we don't know the true passivation
+      // count when we started.  We can get activations of sessions
+      // we didn't know were passivated.
+      // FIXME -- is the above statement still correct? Is this needed?
+      if (pc < 0) 
+      {
+         // Just reverse our decrement.
+         passivatedCount_.incrementAndGet();
+      }
+   }
+   
+   /**
+    * Callback from the distributed cache to notify us that a session
+    * has been modified remotely.
+    * 
+    * @param realId the session id, without any trailing jvmRoute
+    * @param dataOwner  the owner of the session.  Can be <code>null</code> if
+    *                   the owner is unknown.
+    * @param distributedVersion the session's version per the distributed cache
+    * @param timestamp the session's timestamp per the distributed cache
+    * @param metadata the session's metadata per the distributed cache
+    */
+   public boolean sessionChangedInDistributedCache(String realId, 
+                                         String dataOwner,
+                                         int distributedVersion,
+                                         DistributableSessionTimestamp timestamp, 
+                                         DistributableSessionMetadata metadata)
+   {
+      boolean updated = true;
+      
+      ClusteredSession session = findLocalSession(realId);
+      if (session != null)
+      {
+         // Need to invalidate the loaded session. We get back whether
+         // this an actual version increment
+         updated = session.setVersionFromDistributedCache(distributedVersion);
+         if (updated && trace_)      
+         {            
+            log_.trace("session in-memory data is invalidated for id: " + realId + 
+                       " new version: " + distributedVersion);
+         }         
+      }
+      else
+      {
+         long lastMod = timestamp == null ? System.currentTimeMillis() : timestamp.getTimestamp();
+         int maxLife = metadata == null ? getMaxInactiveInterval() : metadata.getMaxInactiveInterval();
+         
+         Object existing = unloadedSessions_.put(realId, new OwnedSessionUpdate(dataOwner, lastMod, maxLife, false));
+         if (existing == null)
+         {
+            calcActiveSessions();
+            if (trace_)
+            {
+               log_.trace("New session " + realId + " added to unloaded session map");
+            }
+         }
+         else if (trace_)
+         {
+            log_.trace("Updated timestamp for unloaded session " + realId);
+         }
+      }
+      
+      return updated;
+   }
+   
+   // ----------------------------------------------- JBossCacheCluster Support
+
+   /**
+    * Sets how often session changes should be replicated to other nodes.
+    *
+    * @param snapshotInterval the number of milliseconds between
+    *                         session replications.
+    */
+   public void setSnapshotInterval(int snapshotInterval)
+   {
+      this.snapshotInterval_ = snapshotInterval;
+   }
+
+   /**
+    * Sets when sessions are replicated to the other nodes. Valid values are:
+    * <ul>
+    * <li>instant</li>
+    * <li>interval</li>
+    * </ul>
+    */
+   public void setSnapshotMode(SnapshotMode snapshotMode)
+   {
+      this.snapshotMode_ = snapshotMode;
+   }
+
+   /**
+    * Sets whether the <code>Engine</code> in which we are running
+    * uses <code>mod_jk</code>.
+    */
+   public void setUseJK(boolean useJK)
+   {
+      this.useJK_ = Boolean.valueOf(useJK);
+   }
+
+   /**
+    * Sets the granularity of session data replicated across the cluster.
+    * Valid values are:
+    * <ul>
+    * <li>SESSION</li>
+    * <li>ATTRIBUTE</li>
+    * <li>FIELD</li>
+    * </ul>
+    */
+   public void setReplicationGranularity(ReplicationGranularity granularity)
+   {
+      this.replicationGranularity_ = granularity;
+   }
+
+   /**
+    * Returns the replication granularity.
+    *
+    *  @see JBossWebMetaData#REPLICATION_GRANULARITY_ATTRIBUTE
+    *  @see JBossWebMetaData#REPLICATION_GRANULARITY_FIELD
+    *  @see JBossWebMetaData#REPLICATION_GRANULARITY_SESSION
+    */
+   public String getReplicationGranularityString()
+   {
+      return replicationGranularity_ == null ? null : replicationGranularity_.toString();
+   }
+
+   /**
+    * Sets the granularity of session data replicated across the cluster.
+    * Valid values are:
+    * <ul>
+    * <li>SESSION</li>
+    * <li>ATTRIBUTE</li>
+    * <li>FIELD</li>
+    * </ul>
+    */
+   public void setReplicationGranularityString(String granularity)
+   {
+      setReplicationGranularity(granularity == null ? null : 
+                  ReplicationGranularity.fromString(granularity.toUpperCase()));
+   } 
+   
+   /**
+    * Sets the type of operations on a <code>HttpSession</code> that
+    * trigger replication.  Valid values are:
+    * <ul>
+    * <li>SET_AND_GET</li>
+    * <li>SET_AND_NON_PRIMITIVE_GET</li>
+    * <li>SET</li>
+    * </ul>
+    */
+   public void setReplicationTrigger(ReplicationTrigger trigger)
+   {
+      this.replicationTrigger_ = trigger;
+   }
+
+   public String getReplicationTriggerString()
+   {      
+      return replicationTrigger_ == null ? null : replicationTrigger_.toString();
+   }
+
+   public void setReplicationTriggerString(String trigger)
+   {      
+      setReplicationTrigger(trigger == null ? null : ReplicationTrigger.fromString(trigger.toUpperCase()));
+   }
+
+   /**
+    * Sets whether, if replication granularity is set to <code>FIELD</code>,
+    * replication should be done in batch mode.  Ignored if field-level
+    * granularity is not used.
+    */
+   public void setReplicationFieldBatchMode(boolean replicationFieldBatchMode)
+   {
+      this.replicationFieldBatchMode_ = Boolean.valueOf(replicationFieldBatchMode);
+   }
+
+   // -------------------------------------------------------------  Properties
+   
+   /**
+    * Gets the <code>FieldBasedJBossCacheService</code> through which we interact
+    * with the PojoCache.
+    * 
+    * @throws IllegalStateException if we are not using field based replication
+    */
+   public FieldBasedDistributedCacheManager getFieldBasedDistributedCacheManager()
+   {
+      if (proxy_ != null && !(proxy_ instanceof FieldBasedDistributedCacheManager))
+         throw new IllegalStateException("PojoCache not being used");
+      return (FieldBasedDistributedCacheManager) proxy_;
+   }
+
+   /**
+    * Gets the <code>JBossCacheService</code> through which we interact
+    * with the <code>Cache</code>.
+    */
+   public DistributedCacheManager getDistributedCacheManager()
+   {
+      return proxy_;
+   }
+
+   // --------------------------------------------------------------- Overrides
+
+   /**
+    * {@inheritDoc}
+    * <p>
+    * Overrides the superclass version to ensure that the generated id
+    * does not duplicate the id of any other session this manager is aware of.
+    * </p>
+    */
+   @Override
+   protected String getNextId()
+   {
+      while (true)
+      {
+         String id = super.getNextId();
+         if (sessions_.containsKey(id) || unloadedSessions_.containsKey(id))
+         {
+            duplicates_.incrementAndGet();
+         }
+         else
+         {
+            return id;
+         }
+      }
+   }
+
+   protected int getTotalActiveSessions()
+   {
+      return localActiveCounter_.get() + unloadedSessions_.size() - passivatedCount_.get();
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   protected void processExpirationPassivation()
+   {      
+      boolean expire = maxInactiveInterval_ >= 0;
+      boolean passivate = isPassivationEnabled();
+      
+      long passivationMax = passivationMaxIdleTime_ * 1000L;
+      long passivationMin = passivationMinIdleTime_ * 1000L;
+
+      if (trace_)
+      { 
+         log_.trace("processExpirationPassivation(): Looking for sessions that have expired ...");
+         log_.trace("processExpirationPassivation(): active sessions = " + calcActiveSessions());
+         log_.trace("processExpirationPassivation(): expired sessions = " + expiredCounter_);
+         if (passivate)
+         {
+            log_.trace("processExpirationPassivation(): passivated count = " + getPassivatedSessionCount());
+         }
+      }
+      
+      try
+      {
+         // First, handle the sessions we are actively managing
+         Session sessions[] = findLocalSessions();
+         for (int i = 0; i < sessions.length; ++i)
+         {
+            try
+            {
+               ClusteredSession session = (ClusteredSession) sessions[i];
+               if(session == null)
+               {
+                  log_.warn("processExpirationPassivation(): processing null session at index " +i);
+                  continue;
+               }
+
+               if (expire)
+               {
+                  // JBAS-2403. Check for outdated sessions where we think
+                  // the local copy has timed out.  If found, refresh the
+                  // session from the cache in case that might change the timeout
+                  if (session.isOutdated() && !(session.isValid(false)))
+                  {
+                     // JBAS-2792 don't assign the result of loadSession to session
+                     // just update the object from the cache or fall through if
+                     // the session has been removed from the cache
+                     loadSession(session.getRealId());
+                  }
+   
+                  // Do a normal invalidation check that will expire the
+                  // session if it has timed out
+                  // DON'T SYNCHRONIZE on session here -- isValid() and
+                  // expire() are meant to be multi-threaded and synchronize
+                  // properly internally; synchronizing externally can lead
+                  // to deadlocks!!
+                  if (!session.isValid()) continue;
+               }
+                
+               // we now have a valid session; see if we need to passivate it
+               if (passivate)
+               {
+                  long timeNow = System.currentTimeMillis();
+                  long timeIdle = timeNow - session.getLastAccessedTimeInternal();
+                  // if maxIdle time configured, means that we need to passivate sessions that have
+                  // exceeded the max allowed idle time
+                  if (passivationMax >= 0 
+                        && timeIdle > passivationMax)
+                  {
+                     processSessionPassivation(session.getRealId());
+                  }
+                  // If the session didn't exceed the passivationMaxIdleTime_, see   
+                  // if the number of sessions managed by this manager greater than the max allowed 
+                  // active sessions, passivate the session if it exceed passivationMinIdleTime_ 
+                  else if (maxActiveAllowed_ > 0 
+                              && passivationMin > 0 
+                              && calcActiveSessions() >= maxActiveAllowed_ 
+                              && timeIdle > passivationMin)
+                  {
+                     processSessionPassivation(session.getRealId());
+                  }                  
+               }
+               
+            }
+            catch (Exception ex)
+            {
+               log_.error("processExpirationPassivation(): failed handling " + 
+                          sessions[i].getIdInternal() + " with exception: " + 
+                          ex, ex);
+            }
+         }
+
+         // Next, handle any unloaded sessions
+
+         
+         // We may have not gotten replication of a timestamp for requests 
+         // that occurred w/in maxUnreplicatedInterval_ of the previous
+         // request. So we add a grace period to avoid flushing a session early
+         // and permanently losing part of its node structure in JBoss Cache.
+         long maxUnrep = maxUnreplicatedInterval_ < 0 ? 60 : maxUnreplicatedInterval_;
+         
+         Map unloaded = new HashMap(unloadedSessions_);
+         for (Iterator<Map.Entry<String, OwnedSessionUpdate>> it = unloaded.entrySet().iterator(); it.hasNext(); )
+         {
+            Map.Entry<String, OwnedSessionUpdate> entry = it.next();
+            String realId = entry.getKey();
+            OwnedSessionUpdate osu = entry.getValue();
+            
+            // Ignore marker entries for our own passivated sessions
+            // Also skip if the session isn't configured to expire
+            if (osu.passivated || osu.maxInactive < 1)
+               continue;
+            
+            long now = System.currentTimeMillis();
+            long elapsed = (now - osu.updateTime);
+            try
+            {
+               if (expire && elapsed >= (osu.maxInactive + maxUnrep) * 1000L)
+               {
+                  proxy_.removeSessionLocal(realId, osu.owner);
+                  unloadedSessions_.remove(realId);
+               }
+               else if (passivate)
+               {
+                  // if maxIdle time configured, means that we need to passivate sessions that have
+                  // exceeded the max allowed idle time
+                  if (passivationMax >= 0 
+                        && elapsed > passivationMax)
+                  {
+                     processUnloadedSessionPassivation(realId, osu);
+                  }
+                  // If the session didn't exceed the passivationMaxIdleTime_, see   
+                  // if the number of sessions managed by this manager is greater than the max allowed 
+                  // active sessions; passivate the session if it exceed passivationMinIdleTime_ 
+                  else if (maxActiveAllowed_ > 0 
+                              && passivationMin >= 0 
+                              && elapsed >= passivationMin
+                              && calcActiveSessions() >= maxActiveAllowed_)
+                  {
+                     processUnloadedSessionPassivation(realId, osu);
+                  }               
+               }
+            } 
+            catch (Exception ex)
+            {
+               log_.error("processExpirationPassivation(): failed handling unloaded session " + 
+                       realId, ex);
+            }
+         }
+      }
+      catch (Exception ex)
+      {
+         log_.error("processExpirationPassivation(): failed with exception: " + ex, ex);
+      }
+      
+      if (trace_)
+      { 
+         log_.trace("processExpirationPassivation(): Completed ...");
+         log_.trace("processExpirationPassivation(): active sessions = " + calcActiveSessions());
+         log_.trace("processExpirationPassivation(): expired sessions = " + expiredCounter_);
+         if (passivate)
+         {
+            log_.trace("processExpirationPassivation(): passivated count = " + getPassivatedSessionCount());
+         }
+      }
+   }
+   
+   @Override
+   public void resetStats()
+   {
+      super.resetStats();
+      
+      this.maxPassivatedCount_.set(this.passivatedCount_.get());
+   }
+   
+   // ------------------------------------------------------ Session Management
+
+   private JBossCacheClusteredSession createEmptyClusteredSession()
+   {     
+
+      JBossCacheClusteredSession session = null;
+      switch (replicationGranularity_)
+      {
+         case ATTRIBUTE:
+         session = new AttributeBasedClusteredSession(this);
+            break;
+         case FIELD:
+            session = new FieldBasedClusteredSession(this);
+            break;
+         default:
+            session = new SessionBasedClusteredSession(this);
+            break;
+      }
+      return session;
+   }
+   
+   /**
+    * Loads a session from the distributed store.  If an existing session with
+    * the id is already under local management, that session's internal state
+    * will be updated from the distributed store.  Otherwise a new session
+    * will be created and added to the collection of those sessions under
+    * local management.
+    *
+    * @param realId  id of the session-id with any jvmRoute removed
+    *
+    * @return the session or <code>null</code> if the session cannot be found
+    *         in the distributed store
+    */
+   private ClusteredSession loadSession(String realId)
+   {
+      if (realId == null)
+      {
+         return null;
+      }
+
+      long begin = System.currentTimeMillis();
+      boolean mustAdd = false;
+      JBossCacheClusteredSession session = (JBossCacheClusteredSession) sessions_.get(realId);
+      
+      if (session == null)
+      {                 
+         // This is either the first time we've seen this session on this
+         // server, or we previously expired it and have since gotten
+         // a replication message from another server
+         mustAdd = true;
+         session = createEmptyClusteredSession();
+      }
+
+      synchronized (session)
+      {
+         boolean doTx = false; 
+         try
+         {
+            // We need transaction so any data gravitation replication 
+            // is sent in batch.
+            // Don't do anything if there is already transaction context
+            // associated with this thread.
+            if (batchingManager.isBatchInProgress() == false)
+            {
+               batchingManager.startBatch();
+               doTx = true;
+            }
+            
+            // Ignore cache notifications we may generate for this 
+            // session if data gravitation occurs. 
+            SessionReplicationContext.startCacheActivity();
+            
+            session = proxy_.loadSession(realId, session);
+            
+            if (session != null)
+            {
+               session.initAfterLoad(this);
+            }
+         }
+         catch (Exception ex)
+         {
+            try
+            {
+//                  if(doTx)
+               // Let's set it no matter what.
+               batchingManager.setBatchRollbackOnly();
+            }
+            catch (Exception exn)
+            {
+               log_.error("Caught exception rolling back transaction", exn);
+            }
+            // We will need to alert Tomcat of this exception.
+            if (ex instanceof RuntimeException)
+               throw (RuntimeException) ex;
+            
+            throw new RuntimeException("loadSession(): failed to load session " +
+                                       realId, ex);
+         }
+         finally
+         {
+            try {
+               if(doTx)
+               {
+                  batchingManager.endBatch();
+               }
+            }
+            finally {
+               SessionReplicationContext.finishCacheActivity();
+            }
+         }
+
+         if (session != null)
+         {            
+            if (mustAdd)
+               add(session, false); // don't replicate
+            long elapsed = System.currentTimeMillis() - begin;
+            stats_.updateLoadStats(realId, elapsed);
+
+            if (trace_)
+            {
+               log_.trace("loadSession(): id= " + realId + ", session=" + session);
+            }
+         }
+         else if (trace_)
+         {
+            log_.trace("loadSession(): session " + realId +
+                       " not found in distributed cache");
+         }
+      }
+
+      return session;
+   }
+
+   /**
+    * Places the current session contents in the distributed cache and
+    * replicates them to the cluster
+    *
+    * @param session  the session.  Cannot be <code>null</code>.
+    */
+   private void processSessionRepl(ClusteredSession session)
+   {
+      // If we are using SESSION granularity, we don't want to initiate a TX
+      // for a single put
+      boolean notSession = (replicationGranularity_ != ReplicationGranularity.SESSION);
+      boolean doTx = false;
+      try
+      {
+         // We need transaction so all the replication are sent in batch.
+         // Don't do anything if there is already transaction context
+         // associated with this thread.
+         if(notSession && batchingManager.isBatchInProgress() == false)
+         {
+            batchingManager.startBatch();
+            doTx = true;
+         }
+
+         // Tell the proxy to ignore cache notifications we are about
+         // to generate for this session. We have to do this
+         // at this level because we don't want to resume handling
+         // notifications until any compensating changes resulting
+         // from a tx rollback are done.
+         SessionReplicationContext.startCacheActivity();
+
+         session.processSessionRepl();
+      }
+      catch (Exception ex)
+      {
+         log_.debug("processSessionRepl(): failed with exception", ex);
+         
+         try
+         {
+            //if(doTx)
+            // Let's setRollbackOnly no matter what.
+            // (except if there's no tx due to SESSION (JBAS-3840))
+            if (notSession)
+               batchingManager.setBatchRollbackOnly();
+         }
+         catch (Exception exn)
+         {
+            log_.error("Caught exception rolling back transaction", exn);
+         }
+         
+         // We will need to alert Tomcat of this exception.
+         if (ex instanceof RuntimeException)
+            throw (RuntimeException) ex;
+         
+         throw new RuntimeException("JBossCacheManager.processSessionRepl(): " +
+                                    "failed to replicate session.", ex);
+      }
+      finally
+      {
+         try {
+            if(doTx)
+            {
+               batchingManager.endBatch();
+            }
+         }
+         finally {
+            SessionReplicationContext.finishCacheActivity();
+         }
+      }
+   }
+   
+   /**
+    * Session passivation logic for an actively managed session.
+    * 
+    * @param realId the session id, minus any jvmRoute
+    */
+   private void processSessionPassivation(String realId)
+   {
+      // get the session from the local map
+      ClusteredSession session = findLocalSession(realId);
+      // Remove actively managed session and add to the unloaded sessions
+      // if it's already unloaded session (session == null) don't do anything, 
+      if (session != null)
+      {
+         synchronized (session)
+         {
+            if (trace_)
+            {
+               log_.trace("Passivating session with id: " + realId);
+            }
+
+            try {
+               // Tell the proxy to ignore cache notifications we are about
+               // to generate for this session.
+               SessionReplicationContext.startCacheActivity();
+               session.passivate();
+               proxy_.evictSession(realId);
+               sessionPassivated();
+            }
+            finally {
+               SessionReplicationContext.finishCacheActivity();
+            }
+            
+            // Put the session in the unloadedSessions map. This will
+            // expose the session to regular invalidation.
+            Object obj = unloadedSessions_.put(realId, 
+                  new OwnedSessionUpdate(null, session.getLastAccessedTime(), session.getMaxInactiveInterval(), true));
+            if (trace_)
+            {
+               if (obj == null)
+               {
+                  log_.trace("New session " + realId + " added to unloaded session map");
+               }
+               else
+               {
+                  log_.trace("Updated timestamp for unloaded session " + realId);
+               }
+            }
+            sessions_.remove(realId);
+         }
+      }
+      else if (trace_)
+      {
+         log_.trace("processSessionPassivation():  could not find session " + realId);
+      }
+   }
+   
+   /**
+    * Session passivation logic for sessions only in the distributed store.
+    * 
+    * @param realId the session id, minus any jvmRoute
+    */
+   private void processUnloadedSessionPassivation(String realId, OwnedSessionUpdate osu)
+   {
+      if (trace_)
+      {
+         log_.trace("Passivating session with id: " + realId);
+      }
+
+      try {
+         // Tell the proxy to ignore cache notifications we are about
+         // to generate for this session.
+         SessionReplicationContext.startCacheActivity();
+         proxy_.evictSession(realId, osu.owner);
+         osu.passivated = true;
+         sessionPassivated();
+      }
+      finally {
+         SessionReplicationContext.finishCacheActivity();
+      }
+      
+   }
+   
+   private void sessionPassivated()
+   {
+      int pc = passivatedCount_.incrementAndGet();
+      int max = maxPassivatedCount_.get();
+      while (pc > max)
+      {
+         if (!maxPassivatedCount_ .compareAndSet(max, pc))
+         {
+            max = maxPassivatedCount_.get();
+         }
+      }
+   }
+   
+   // -----------------------------------------------  Lifecyle When Unembedded
+
+   /**
+    * Start this Manager when running in standalone Tomcat.
+    */
+   private void startUnembedded() throws LifecycleException
+   {
+      if (started_)
+      {
+         return;
+      }
+      
+      log_.info("Manager is about to start");      
+
+      // Notify our interested LifecycleListeners
+      lifecycle_.fireLifecycleEvent(BEFORE_START_EVENT, this);
+      
+      if (snapshotMode_ == null) 
+      {
+         // We were not instantiated by a JBossCacheCluster, so we need to
+         // find one and let it configure our cluster-wide properties
+         try 
+         {
+            JBossCacheCluster cluster = (JBossCacheCluster) container_.getCluster();
+            cluster.configureManager(this);
+         }
+         catch (ClassCastException e)
+         {
+            String msg = "Cluster is not an instance of JBossCacheCluster";
+            log_.error(msg, e);
+            throw new LifecycleException(msg, e);
+         }
+      }
+      
+      // Validate attributes
+      
+      // Create the JBossCacheService
+      try
+      {
+         if (proxy_ == null)
+         {
+            initCacheProxy();
+         }
+         
+         // We need to pass the classloader that is associated with this 
+         // web app so de-serialization will work correctly.
+         tcl_ = container_.getLoader().getClassLoader();
+         proxy_.start(tcl_, this);
+      }
+      catch (Throwable t)
+      {
+         String str = "Problem starting JBossCacheService for Tomcat clustering";
+         log_.error(str, t);
+         throw new LifecycleException(str, t);
+      }
+
+      batchingManager = proxy_.getBatchingManager();
+      if(batchingManager == null)
+      {
+         throw new LifecycleException("JBossCacheManager.start(): Obtain null batchingManager");
+      }
+      
+      try
+      {
+         initializeUnloadedSessions();
+
+         // Add SnapshotValve and, if needed, JvmRouteValve and batch repl valve
+         installValves();
+
+         started_ = true;
+         
+         // Notify our interested LifecycleListeners
+         lifecycle_.fireLifecycleEvent(AFTER_START_EVENT, this);
+         
+         log_.debug("start(): JBossCacheService started");
+      } 
+      catch (Exception e)
+      {
+         log_.error("Unable to start manager.", e);
+         throw new LifecycleException(e);
+      }
+      
+      registerManagerMBean();
+   }
+
+   /**
+    * Accesses the underlying cache and creates the proxy
+    * 
+    * @throws ClusteringNotSupportedException
+    */
+   private void initCacheProxy() throws ClusteringNotSupportedException
+   {
+      if (ReplicationGranularity.FIELD == replicationGranularity_)
+      {
+         proxy_ = distributedCacheManagerFactory.getFieldBasedDistributedCacheManager(cacheConfigName_);
+      }
+      else
+      {
+         proxy_ = distributedCacheManagerFactory.getDistributedCacheManager(cacheConfigName_);
+      }
+   }
+   
+   /**
+    * Gets the ids of all sessions in the distributed cache and adds
+    * them to the unloaded sessions map, along with their lastAccessedTime
+    * and their maxInactiveInterval. Passivates overage or excess sessions.
+    */
+   private void initializeUnloadedSessions() throws CacheException
+   {      
+      Map<String, String> sessions = proxy_.getSessionIds();
+      if (sessions != null)
+      {
+         boolean passivate = isPassivationEnabled();
+
+         long passivationMax = passivationMaxIdleTime_ * 1000L;
+         long passivationMin = passivationMinIdleTime_ * 1000L;
+         
+         for (Map.Entry<String, String> entry : sessions.entrySet())
+         {
+            String realId = entry.getKey();
+            String owner = entry.getValue();
+
+            DistributableSessionTimestamp ts = null;
+            DistributableSessionMetadata md = null;
+            try
+            {
+               Map sessionData = proxy_.getSessionData(realId, owner);
+               ts = (DistributableSessionTimestamp) sessionData.get(JBossCacheService.TIMESTAMP_KEY);
+               md = (DistributableSessionMetadata) sessionData.get(JBossCacheService.METADATA_KEY);
+            }
+            catch (Exception e)
+            {
+               // most likely a lock conflict if the session is being updated remotely; 
+               // ignore it and use default values for timstamp and maxInactive
+               log_.debug("Problem reading metadata for session " + realId + " -- " + e.toString());               
+            }
+            
+            long lastMod = ts == null ? System.currentTimeMillis() : ts.getTimestamp();
+            int maxLife = md == null ? getMaxInactiveInterval() : md.getMaxInactiveInterval();
+            
+            OwnedSessionUpdate osu = new OwnedSessionUpdate(owner, lastMod, maxLife, false);
+            unloadedSessions_.put(realId, osu);
+            if (passivate)
+            {
+               try
+               {
+                  long elapsed = System.currentTimeMillis() - lastMod;
+                  // if maxIdle time configured, means that we need to passivate sessions that have
+                  // exceeded the max allowed idle time
+                  if (passivationMax >= 0 
+                        && elapsed > passivationMax)
+                  {
+                     if (trace_)
+                     {
+                        log_.trace("Elapsed time of " + elapsed + " for session "+ 
+                              realId + " exceeds max of " + passivationMax + "; passivating");
+                     }
+                     processUnloadedSessionPassivation(realId, osu);
+                  }
+                  // If the session didn't exceed the passivationMaxIdleTime_, see   
+                  // if the number of sessions managed by this manager greater than the max allowed 
+                  // active sessions, passivate the session if it exceed passivationMinIdleTime_ 
+                  else if (maxActiveAllowed_ > 0 
+                              && passivationMin >= 0 
+                              && calcActiveSessions() > maxActiveAllowed_ 
+                              && elapsed >= passivationMin)
+                  {
+                     if (trace_)
+                     {
+                        log_.trace("Elapsed time of " + elapsed + " for session "+ 
+                              realId + " exceeds min of " + passivationMin + "; passivating");
+                     }
+                     processUnloadedSessionPassivation(realId, osu);
+                  }
+               }
+               catch (Exception e)
+               {
+                  // most likely a lock conflict if the session is being updated remotely; ignore it
+                  log_.debug("Problem passivating session " + realId + " -- " + e.toString());
+               }
+            }
+         }
+      }
+   }
+
+   /**
+    * Instantiate a SnapshotManager and ClusteredSessionValve and add 
+    * the valve to our parent Context's pipeline.  
+    * Add a JvmRouteValve and BatchReplicationClusteredSessionValve if needed.
+    */
+   private void installValves()
+   {      
+      // If JK usage wasn't explicitly configured, default to enabling
+      // it if jvmRoute is set on our containing Engine
+      if (useJK_ == null)
+      {
+         useJK_ = Boolean.valueOf(getJvmRoute() != null);
+      }
+      
+      if (getUseJK())
+      {
+         log_.debug("We are using JK for load-balancing. Adding JvmRouteValve.");         
+         installContextValve(new JvmRouteValve(this));         
+      }   
+      
+      // Handle batch replication if needed.
+      // TODO -- should we add this even if not FIELD in case a cross-context
+      // call traverses a field-based webapp?   
+      BatchingManager valveBM = null;
+      if (replicationGranularity_ == ReplicationGranularity.FIELD
+            && Boolean.TRUE.equals(replicationFieldBatchMode_))
+      {
+         valveBM = this.batchingManager;
+         log_.debug("Including transaction manager in ClusteredSessionValve to support batch replication.");
+      }
+
+      // Add clustered session valve
+      ClusteredSessionValve valve = new ClusteredSessionValve(this, valveBM);
+      log_.debug("Adding ClusteredSessionValve");
+      installContextValve(valve);
+   }
+
+   /**
+    * Create and start a snapshot manager.
+    */
+   private void initSnapshotManager()
+   {
+      String ctxPath = ((Context) container_).getPath();
+      if (SnapshotMode.INSTANT == snapshotMode_)
+      {
+         snapshotManager_ = new InstantSnapshotManager(this, ctxPath);
+      }
+      else if (snapshotMode_ == null)
+      {
+         log_.warn("Snapshot mode must be 'instant' or 'interval' - " +
+                   "using 'instant'");
+         snapshotMode_ = SnapshotMode.INSTANT;
+         snapshotManager_ = new InstantSnapshotManager(this, ctxPath);
+      }
+      else if (ReplicationGranularity.FIELD == replicationGranularity_)
+      {
+         throw new IllegalStateException("Property snapshotMode must be " + 
+               SnapshotMode.INTERVAL + " when FIELD granularity is used");
+      }
+      else if (snapshotInterval_ < 1)
+      {
+         log_.warn("Snapshot mode set to 'interval' but snapshotInterval is < 1 " +
+                   "using 'instant'");
+         snapshotMode_ = SnapshotMode.INSTANT;
+         snapshotManager_ = new InstantSnapshotManager(this, ctxPath);         
+      }
+      else
+      {
+         snapshotManager_ = new IntervalSnapshotManager(this, ctxPath, snapshotInterval_);
+      }
+      
+      snapshotManager_.start();
+   }
+   
+   private void installContextValve(Valve valve)
+   {
+      boolean installed = false;
+      
+      // In embedded mode, install the valve via JMX to be consistent
+      // with the way the overall context is created in TomcatDeployer.
+      // We can't do this in unembedded mode because we are called
+      // before our Context is registered with the MBean server
+      if (embedded_ && getContextObjectName() != null) {
+         try
+         {
+            getMBeanServer().invoke(getContextObjectName(), "addValve",
+                                    new Object[]{valve},
+                                    new String[]{"org.apache.catalina.Valve"});
+            installed = true;
+         }
+         catch (Exception e)
+         {
+            // JBAS-2422.  If the context is restarted via JMX, the above
+            // JMX call will fail as the context will not be registered
+            // when it's made.  So we catch the exception and fall back
+            // to adding the valve directly.
+            // TODO consider skipping adding via JMX and just do it directly
+            log_.debug("Caught exception installing valve to Context", e);
+         }
+      }
+      
+      if (!installed)
+      {
+         // If possible install via the ContainerBase.addValve() API.
+         if (container_ instanceof ContainerBase)
+         {            
+            ((ContainerBase) container_).addValve(valve);
+         }
+         else
+         {
+            // No choice; have to add it to the context's pipeline
+            container_.getPipeline().addValve(valve);
+         }
+      }
+   }
+   
+   private ObjectName getContextObjectName()
+   {
+      String oname = container_.getObjectName();
+      try
+      {         
+         return (oname == null) ? null : new ObjectName(oname);
+      }
+      catch (MalformedObjectNameException e)
+      {
+         log_.warn("Error creating object name from string " + oname, e);
+         return null;
+      }
+   }
+
+   /**
+    * Clear the underlying cache store.
+    */
+   private void clearSessions()
+   {
+      boolean passivation = isPassivationEnabled();
+      // First, the sessions we have actively loaded
+      ClusteredSession[] sessions = findLocalSessions();
+      for(int i=0; i < sessions.length; i++)
+      {
+         ClusteredSession ses = sessions[i];
+         
+         if (trace_)
+         {
+             log_.trace("clearSessions(): clear session by expiring or passivating: " + ses);
+         }
+         try
+         {
+            // if session passivation is enabled, passivate sessions instead of expiring them which means
+            // they'll be available to the manager for activation after a restart. 
+            if(passivation && ses.isValid())
+            {               
+               processSessionPassivation(ses.getRealId());
+            }
+            else
+            {               
+               boolean notify = true;
+               boolean localCall = true;
+               boolean localOnly = true;
+               ses.expire(notify, localCall, localOnly);               
+            }
+         }
+         catch (Throwable t)
+         {
+            log_.warn("clearSessions(): Caught exception expiring or passivating session " +
+                     ses.getIdInternal(), t);
+         }
+         finally
+         {
+            // Guard against leaking memory if anything is holding a
+            // ref to the session by clearing its internal state
+            ses.recycle();
+         }
+      }      
+      
+      String action = passivation ? "evicting" : "removing";
+      Set<Map.Entry<String, OwnedSessionUpdate>> unloaded = 
+               unloadedSessions_.entrySet();
+      for (Iterator<Map.Entry<String, OwnedSessionUpdate>> it = unloaded.iterator(); it.hasNext();)
+      {
+         Map.Entry<String, OwnedSessionUpdate> entry = it.next();
+         String realId = entry.getKey();         
+         try
+         {
+            if (passivation)
+            {
+               OwnedSessionUpdate osu = entry.getValue();
+               // Ignore the marker entries for our passivated sessions
+               if (!osu.passivated)
+               {
+                  proxy_.evictSession(realId, osu.owner);
+               }
+            }
+            else
+            {
+               proxy_.removeSessionLocal(realId, false);           
+            }
+         }
+         catch (Exception e)
+         {
+            // Not as big a problem; we don't own the session
+            log_.debug("Problem " + action + " session " + realId + " -- " + e);
+         }
+         it.remove(); 
+      }
+   }
+   
+   // ------------------------------------------------------  Lifecyle Embedded
+   
+   /**
+    * Start this Manager when running embedded in JBoss AS.
+    *
+    * @throws org.apache.catalina.LifecycleException
+    */
+   private void startEmbedded() throws LifecycleException
+   {
+      super.start();
+      
+      // Start the JBossCacheService
+      // Will need to pass the classloader that is associated with this 
+      // web app so de-serialization will work correctly.
+      tcl_ = super.getContainer().getLoader().getClassLoader();
+      
+      try
+      {
+         if (proxy_ == null)
+         {
+            initCacheProxy();
+         }
+         
+         proxy_.start(tcl_, this);
+
+         batchingManager = proxy_.getBatchingManager();
+         if(batchingManager == null)
+         {
+            throw new LifecycleException("JBossCacheManager.start(): Obtain null batchingManager");
+         }
+         
+         initializeUnloadedSessions();
+         
+         // Setup our SnapshotManager
+         initSnapshotManager();
+         
+         // Add SnapshotValve and, if needed, JvmRouteValve and batch repl valve
+         installValves();
+
+         log_.debug("start(): JBossCacheService started");         
+      }
+      catch (LifecycleException le)
+      {
+         throw le;
+      }
+      catch (Exception e)
+      {
+         log_.error("Unable to start manager.", e);
+         throw new LifecycleException(e);
+      }
+   }
+
+   // -------------------------------------------------------------------- Misc
+   
+   /**
+    * Gets the session id with any jvmRoute removed.
+    * 
+    * @param id a session id with or without an appended jvmRoute.
+    *           Cannot be <code>null</code>.
+    */
+   private String getRealId(String id)
+   {
+      return (getUseJK() ? Util.getRealId(id) : id);
+   }
+   
+   private String reportSessionIds(Set ids)
+   {
+      StringBuffer sb = new StringBuffer();
+      boolean added = false;
+      for (Iterator it = ids.iterator(); it.hasNext(); )
+      {
+         if (added)
+         {
+            sb.append(',');
+         }
+         else
+         {
+            added = true;
+         }
+         
+         sb.append(it.next());
+      }
+      return sb.toString();
+   }   
+   
+   // ------------------------------------------------------------ Inner Classes
+   
+   private class OwnedSessionUpdate
+   {
+      String owner;
+      long updateTime;
+      int maxInactive;
+      boolean passivated;
+      
+      OwnedSessionUpdate(String owner, long updateTime, int maxInactive, boolean passivated)
+      {
+         this.owner = owner;
+         this.updateTime = updateTime;
+         this.maxInactive = maxInactive;
+         this.passivated = passivated;
+      }
+   }
+}

Deleted: trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/JBossCacheManagerMBean.java
===================================================================
--- trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/JBossCacheManagerMBean.java	2008-08-26 16:10:33 UTC (rev 77495)
+++ trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/JBossCacheManagerMBean.java	2008-08-26 16:41:17 UTC (rev 77496)
@@ -1,227 +0,0 @@
-/*
-* JBoss, Home of Professional Open Source
-* Copyright 2005, JBoss Inc., and individual contributors as indicated
-* by the @authors tag. See the copyright.txt 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.web.tomcat.service.session;
-
-import org.jboss.metadata.web.jboss.ReplicationGranularity;
-import org.jboss.metadata.web.jboss.ReplicationTrigger;
-import org.jboss.metadata.web.jboss.SnapshotMode;
-
-public interface JBossCacheManagerMBean extends JBossManagerMBean
-{
-   /**
-    * Gets the value of the attribute with the given key from the given
-    * session.  If the session is in the distributed store but hasn't been
-    * loaded on this node, invoking this method will cause it to be loaded.
-    * 
-    * @param sessionId the id of the session
-    * @param key       the attribute key
-    * @return          the value, converted to a String via toString(), 
-    *                  or <code>null</code> if the session or key does not exist.
-    */
-   String getSessionAttribute(String sessionId, String key);
-   
-   /**
-    * Expires the given session. If the session is in the distributed store 
-    * but hasn't been loaded on this node, invoking this method will cause it 
-    * to be loaded.
-    * 
-    * @param sessionId the id of the session
-    */
-   void expireSession(String sessionId);
-   
-   /**
-    * Gets the last time the given session was accessed.  If the session is in 
-    * the distributed store but hasn't been loaded on this node, invoking this 
-    * method will cause it to be loaded.
-    * 
-    * @param sessionId
-    * @return the last accessed time, or the empty string if the session
-    *         doesn't exist.
-    */
-   String getLastAccessedTime(String sessionId);
-   
-   /**
-    * Gets the creation time of the given session.  If the session is in 
-    * the distributed store but hasn't been loaded on this node, invoking this 
-    * method will cause it to be loaded.
-    * 
-    * @param sessionId
-    * @return the creation time, or or the empty string if the session
-    *         doesn't exist.
-    */
-   String getCreationTime(String sessionId);
-   
-   /**
-    * Gets the cache config name used to get the underlying cache
-    * from a cache manager.
-    * 
-    * @return the config name, or <code>null</code> if this has not yet been
-    *         configured or the cache was directly injected.
-    */
-   String getCacheConfigName();
-   
-   /**
-    * Gets the replication granularity.
-    * 
-    * @return SESSION, ATTRIBUTE or FIELD, or <code>null</code> if this
-    *         has not yet been configured.
-    */
-   ReplicationGranularity getReplicationGranularity();
-
-   /**
-    * Gets the replication trigger.
-    * 
-    * @return SET, SET_AND_GET, SET_AND_NON_PRIMITIVE_GET or <code>null</code> 
-    *         if this has not yet been configured.
-    */
-   ReplicationTrigger getReplicationTrigger();
-   
-   /**
-    * Gets whether batching of field granularity changes will be done.  Only
-    * relevant with replication granularity FIELD.
-    * 
-    * @return <code>true</code> if per-request batching will be done, 
-    *         <code>false</code> if not, <code>null</code> if not configured
-    */
-   Boolean isReplicationFieldBatchMode();
-   
-   /**
-    * Gets whether JK is being used and special handling of a jvmRoute
-    * portion of session ids is needed.
-    */
-   boolean getUseJK();
-   
-   /**
-    * Gets the snapshot mode.
-    * 
-    * @return "instant" or "interval"
-    */
-   SnapshotMode getSnapshotMode();
-   
-   /**
-    * Gets the number of milliseconds between replications if "interval" mode
-    * is used.
-    */
-   int getSnapshotInterval();
-
-   /**
-    * Get the maximum interval between requests, in seconds, after which a
-    * request will trigger replication of the session's metadata regardless
-    * of whether the request has otherwise made the session dirty. Such 
-    * replication ensures that other nodes in the cluster are aware of a 
-    * relatively recent value for the session's timestamp and won't incorrectly
-    * expire an unreplicated session upon failover.
-    * <p/>
-    * Default value is <code>-1</code>.
-    * <p/>
-    * The cost of the metadata replication depends on the configured
-    * {@link #setReplicationGranularityString(String) replication granularity}.
-    * With <code>SESSION</code>, the sesssion's attribute map is replicated 
-    * along with the metadata, so it can be fairly costly.  With other 
-    * granularities, the metadata object is replicated separately from the
-    * attributes and only contains a String, and a few longs, ints and booleans.
-    * 
-    * @return the maximum interval since last replication after which a request
-    *         will trigger session metadata replication. A value of 
-    *         <code>0</code> means replicate metadata on every request; a value 
-    *         of <code>-1</code> means never replicate metadata unless the 
-    *         session is otherwise dirty.
-    */
-   int getMaxUnreplicatedInterval();
-
-   /**
-    * Sets the maximum interval between requests, in seconds, after which a
-    * request will trigger replication of the session's metadata regardless
-    * of whether the request has otherwise made the session dirty.
-    * 
-    * @param  maxUnreplicatedInterval  
-    *         the maximum interval since last replication after which a request
-    *         will trigger session metadata replication. A value of 
-    *         <code>0</code> means replicate metadata on every request; a value 
-    *         of <code>-1</code> means never replicate metadata unless the 
-    *         session is otherwise dirty.
-    */
-   void setMaxUnreplicatedInterval(int maxUnreplicatedInterval);
-   
-   /**
-    * Lists all session ids known to this manager, including those in the 
-    * distributed store that have not been accessed on this node.
-    * 
-    * @return a comma-separated list of session ids
-    */
-   String listSessionIds();
-   
-   /**
-    * Lists all session ids known to this manager, excluding those in the 
-    * distributed store that have not been accessed on this node.
-    * 
-    * @return a comma-separated list of session ids
-    */
-   String listLocalSessionIds();
-   
-   /**
-    * Gets whether passivation was enabled in jboss-web.xml and in the
-    * underlying cache.
-    * 
-    * @return <code>true</code> if passivation is enabled in both
-    *         jboss-web.xml and in the cache; <code>false</code> otherwise
-    */
-   boolean isPassivationEnabled();
-   
-   /**
-    * Gets the number of passivated sessions
-    * 
-    * @return
-    */
-   long getPassivatedSessionCount();
-   
-   /**
-    * Gets the highest number of passivated sessions seen.
-    * 
-    * @return
-    */
-   long getMaxPassivatedSessionCount();
-   
-   /**
-    * Elapsed time after which an inactive session will be passivated 
-    * to persistent storage if {@link #isPassivationEnabled() passivation is
-    * enabled}.
-    * 
-    * @return
-    */
-   long getPassivationMaxIdleTime();
-   
-   /**
-    * Elapsed time after which an inactive session will be passivated 
-    * to persistent storage if {@link #isPassivationEnabled() passivation is
-    * enabled} and the manager needs to passivate sessions early in order to
-    * comply with a {@link JBossManagerMBean#getMaxActiveAllowed()} setting.
-    * 
-    * @return
-    */
-   long getPassivationMinIdleTime();
-   
-   /**
-    * Gets the number of duplicated session ids generated.
-    */
-   int getDuplicates();
-}

Added: trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/JBossCacheManagerMBean.java
===================================================================
--- trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/JBossCacheManagerMBean.java	                        (rev 0)
+++ trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/JBossCacheManagerMBean.java	2008-08-26 16:41:17 UTC (rev 77496)
@@ -0,0 +1,227 @@
+/*
+* JBoss, Home of Professional Open Source
+* Copyright 2005, JBoss Inc., and individual contributors as indicated
+* by the @authors tag. See the copyright.txt 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.web.tomcat.service.session;
+
+import org.jboss.metadata.web.jboss.ReplicationGranularity;
+import org.jboss.metadata.web.jboss.ReplicationTrigger;
+import org.jboss.metadata.web.jboss.SnapshotMode;
+
+public interface JBossCacheManagerMBean extends JBossManagerMBean
+{
+   /**
+    * Gets the value of the attribute with the given key from the given
+    * session.  If the session is in the distributed store but hasn't been
+    * loaded on this node, invoking this method will cause it to be loaded.
+    * 
+    * @param sessionId the id of the session
+    * @param key       the attribute key
+    * @return          the value, converted to a String via toString(), 
+    *                  or <code>null</code> if the session or key does not exist.
+    */
+   String getSessionAttribute(String sessionId, String key);
+   
+   /**
+    * Expires the given session. If the session is in the distributed store 
+    * but hasn't been loaded on this node, invoking this method will cause it 
+    * to be loaded.
+    * 
+    * @param sessionId the id of the session
+    */
+   void expireSession(String sessionId);
+   
+   /**
+    * Gets the last time the given session was accessed.  If the session is in 
+    * the distributed store but hasn't been loaded on this node, invoking this 
+    * method will cause it to be loaded.
+    * 
+    * @param sessionId
+    * @return the last accessed time, or the empty string if the session
+    *         doesn't exist.
+    */
+   String getLastAccessedTime(String sessionId);
+   
+   /**
+    * Gets the creation time of the given session.  If the session is in 
+    * the distributed store but hasn't been loaded on this node, invoking this 
+    * method will cause it to be loaded.
+    * 
+    * @param sessionId
+    * @return the creation time, or or the empty string if the session
+    *         doesn't exist.
+    */
+   String getCreationTime(String sessionId);
+   
+   /**
+    * Gets the cache config name used to get the underlying cache
+    * from a cache manager.
+    * 
+    * @return the config name, or <code>null</code> if this has not yet been
+    *         configured or the cache was directly injected.
+    */
+   String getCacheConfigName();
+   
+   /**
+    * Gets the replication granularity.
+    * 
+    * @return SESSION, ATTRIBUTE or FIELD, or <code>null</code> if this
+    *         has not yet been configured.
+    */
+   ReplicationGranularity getReplicationGranularity();
+
+   /**
+    * Gets the replication trigger.
+    * 
+    * @return SET, SET_AND_GET, SET_AND_NON_PRIMITIVE_GET or <code>null</code> 
+    *         if this has not yet been configured.
+    */
+   ReplicationTrigger getReplicationTrigger();
+   
+   /**
+    * Gets whether batching of field granularity changes will be done.  Only
+    * relevant with replication granularity FIELD.
+    * 
+    * @return <code>true</code> if per-request batching will be done, 
+    *         <code>false</code> if not, <code>null</code> if not configured
+    */
+   Boolean isReplicationFieldBatchMode();
+   
+   /**
+    * Gets whether JK is being used and special handling of a jvmRoute
+    * portion of session ids is needed.
+    */
+   boolean getUseJK();
+   
+   /**
+    * Gets the snapshot mode.
+    * 
+    * @return "instant" or "interval"
+    */
+   SnapshotMode getSnapshotMode();
+   
+   /**
+    * Gets the number of milliseconds between replications if "interval" mode
+    * is used.
+    */
+   int getSnapshotInterval();
+
+   /**
+    * Get the maximum interval between requests, in seconds, after which a
+    * request will trigger replication of the session's metadata regardless
+    * of whether the request has otherwise made the session dirty. Such 
+    * replication ensures that other nodes in the cluster are aware of a 
+    * relatively recent value for the session's timestamp and won't incorrectly
+    * expire an unreplicated session upon failover.
+    * <p/>
+    * Default value is <code>-1</code>.
+    * <p/>
+    * The cost of the metadata replication depends on the configured
+    * {@link #setReplicationGranularityString(String) replication granularity}.
+    * With <code>SESSION</code>, the sesssion's attribute map is replicated 
+    * along with the metadata, so it can be fairly costly.  With other 
+    * granularities, the metadata object is replicated separately from the
+    * attributes and only contains a String, and a few longs, ints and booleans.
+    * 
+    * @return the maximum interval since last replication after which a request
+    *         will trigger session metadata replication. A value of 
+    *         <code>0</code> means replicate metadata on every request; a value 
+    *         of <code>-1</code> means never replicate metadata unless the 
+    *         session is otherwise dirty.
+    */
+   int getMaxUnreplicatedInterval();
+
+   /**
+    * Sets the maximum interval between requests, in seconds, after which a
+    * request will trigger replication of the session's metadata regardless
+    * of whether the request has otherwise made the session dirty.
+    * 
+    * @param  maxUnreplicatedInterval  
+    *         the maximum interval since last replication after which a request
+    *         will trigger session metadata replication. A value of 
+    *         <code>0</code> means replicate metadata on every request; a value 
+    *         of <code>-1</code> means never replicate metadata unless the 
+    *         session is otherwise dirty.
+    */
+   void setMaxUnreplicatedInterval(int maxUnreplicatedInterval);
+   
+   /**
+    * Lists all session ids known to this manager, including those in the 
+    * distributed store that have not been accessed on this node.
+    * 
+    * @return a comma-separated list of session ids
+    */
+   String listSessionIds();
+   
+   /**
+    * Lists all session ids known to this manager, excluding those in the 
+    * distributed store that have not been accessed on this node.
+    * 
+    * @return a comma-separated list of session ids
+    */
+   String listLocalSessionIds();
+   
+   /**
+    * Gets whether passivation was enabled in jboss-web.xml and in the
+    * underlying cache.
+    * 
+    * @return <code>true</code> if passivation is enabled in both
+    *         jboss-web.xml and in the cache; <code>false</code> otherwise
+    */
+   boolean isPassivationEnabled();
+   
+   /**
+    * Gets the number of passivated sessions
+    * 
+    * @return
+    */
+   long getPassivatedSessionCount();
+   
+   /**
+    * Gets the highest number of passivated sessions seen.
+    * 
+    * @return
+    */
+   long getMaxPassivatedSessionCount();
+   
+   /**
+    * Elapsed time after which an inactive session will be passivated 
+    * to persistent storage if {@link #isPassivationEnabled() passivation is
+    * enabled}.
+    * 
+    * @return
+    */
+   long getPassivationMaxIdleTime();
+   
+   /**
+    * Elapsed time after which an inactive session will be passivated 
+    * to persistent storage if {@link #isPassivationEnabled() passivation is
+    * enabled} and the manager needs to passivate sessions early in order to
+    * comply with a {@link JBossManagerMBean#getMaxActiveAllowed()} setting.
+    * 
+    * @return
+    */
+   long getPassivationMinIdleTime();
+   
+   /**
+    * Gets the number of duplicated session ids generated.
+    */
+   int getDuplicates();
+}

Deleted: trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/JBossCacheService.java
===================================================================
--- trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/JBossCacheService.java	2008-08-26 16:10:33 UTC (rev 77495)
+++ trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/JBossCacheService.java	2008-08-26 16:41:17 UTC (rev 77496)
@@ -1,749 +0,0 @@
-/*
-* JBoss, Home of Professional Open Source
-* Copyright 2005, JBoss Inc., and individual contributors as indicated
-* by the @authors tag. See the copyright.txt 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.web.tomcat.service.session;
-
-import java.io.IOException;
-import java.io.Serializable;
-import java.util.HashMap;
-import java.util.Iterator;
-import java.util.Map;
-import java.util.Set;
-import java.util.Map.Entry;
-
-import javax.transaction.TransactionManager;
-
-import org.apache.catalina.Container;
-import org.jboss.cache.Cache;
-import org.jboss.cache.CacheException;
-import org.jboss.cache.Fqn;
-import org.jboss.cache.Node;
-import org.jboss.cache.Region;
-import org.jboss.cache.buddyreplication.BuddyManager;
-import org.jboss.cache.config.BuddyReplicationConfig;
-import org.jboss.cache.config.CacheLoaderConfig;
-import org.jboss.cache.pojo.impl.InternalConstant;
-import org.jboss.cache.transaction.BatchModeTransactionManager;
-import org.jboss.ha.framework.interfaces.CachableMarshalledValue;
-import org.jboss.ha.framework.server.MarshalledValueHelper;
-import org.jboss.ha.framework.server.SimpleCachableMarshalledValue;
-import org.jboss.logging.Logger;
-import org.jboss.web.tomcat.service.session.ClusteredSession.SessionMetadata;
-import org.jboss.web.tomcat.service.session.ClusteredSession.SessionTimestamp;
-
-/**
- * A wrapper class to JBossCache. This is currently needed to handle various operations such as
- * <ul>
- * <li>Using MarshalledValue to replace Serializable used inside different web app class loader context.</li>
- * <li>Stripping out any id string after ".". This is to handle the JK failover properly with
- * Tomcat JvmRoute.</li>
- * <li>Cache exception retry.</li>
- * <li>Helper APIS.</li>
- * </ul>
- */
-public class JBossCacheService
-{   
-   protected static Logger log_ = Logger.getLogger(JBossCacheService.class);
-   public static final String BUDDY_BACKUP = BuddyManager.BUDDY_BACKUP_SUBTREE;
-   public static final Fqn BUDDY_BACKUP_FQN = BuddyManager.BUDDY_BACKUP_SUBTREE_FQN;
-   public static final String SESSION = "JSESSION";
-   public static final String ATTRIBUTE = "ATTRIBUTE";
-   public static final String VERSION_KEY = "V";
-   public static final String TIMESTAMP_KEY = "T";
-   public static final String METADATA_KEY = "M";
-   public static final String ATTRIBUTE_KEY = "A";
-      
-   public static final String FQN_DELIMITER = "/";
-   
-   private Cache plainCache_;
-   
-   /** name of webapp's virtual host; hostName + webAppPath + session id is a unique combo. */
-   protected String hostName_;
-   /** Context path for webapp; hostName + webAppPath + session id is a unique combo. */
-   protected String webAppPath_;
-   protected TransactionManager tm;
-
-   private JBossCacheManager manager_;
-   private CacheListener cacheListener_;
-   protected JBossCacheWrapper cacheWrapper_;
-   
-   /** Do we have to marshall attributes ourself or can we let JBC do it? */
-   private boolean useTreeCacheMarshalling_ = false;
-   
-   /** Are we configured for passivation? */
-   private boolean usePassivation_ = false;
-   private PassivationListener passivationListener_;
-   
-   /** Is cache configured for buddy replication? */
-   private boolean useBuddyReplication_ = false;
-   
-   
-   public JBossCacheService(Cache cache)
-   {
-      plainCache_ = cache;
-      
-      cacheWrapper_ = new JBossCacheWrapper(plainCache_);
-      
-      useTreeCacheMarshalling_ = plainCache_.getConfiguration().isUseRegionBasedMarshalling();
-      CacheLoaderConfig clc = plainCache_.getConfiguration().getCacheLoaderConfig();
-      if(clc != null)
-      {
-         usePassivation_ = (clc.isPassivation() && !clc.isShared());
-      }
-   }
-   
-   protected JBossCacheManager getManager()
-   {
-      return manager_;
-   }
-   
-   protected Cache getCache()
-   {
-      return plainCache_;
-   }
-   
-   protected void setCache(Cache cache)
-   {
-      this.plainCache_ = cache;
-   }
-
-   public void start(ClassLoader tcl, JBossCacheManager manager)
-   {
-      manager_ = manager;
-      
-      Container webapp = manager_.getContainer();
-      String path = webapp.getName();
-      if( path.length() == 0 || path.equals("/")) {
-         // If this is root.
-         webAppPath_ = "ROOT";
-      } else if ( path.startsWith("/") ) {
-         webAppPath_ = path.substring(1);
-      } else {
-         webAppPath_ = path;
-      }
-      // JBAS-3941 -- context path can be multi-level, but we don't
-      // want that turning into a multilevel Fqn, so escape it
-      // Use '?' which is illegal in a context path
-      webAppPath_ = webAppPath_.replace('/', '?');
-      log_.debug("Old and new web app path are: " +path + ", " +webAppPath_);
-      
-      String host = webapp.getParent().getName();
-      if( host == null || host.length() == 0) {
-         hostName_ = "localhost";
-      }else {
-         hostName_ = host;
-      }
-      log_.debug("Old and new virtual host name are: " + host + ", " + hostName_);
-
-
-      // We require the cache tm to be BatchModeTransactionManager now.
-      tm = plainCache_.getConfiguration().getRuntimeConfig().getTransactionManager();
-      if( ! (tm instanceof BatchModeTransactionManager) )
-      {
-         throw new RuntimeException("start(): JBoss Cache transaction manager " +
-                                    "is not of type BatchModeTransactionManager. " +
-                                    "It is " + (tm == null ? "null" : tm.getClass().getName()));
-      }
-      
-      Object[] objs = new Object[]{SESSION, hostName_, webAppPath_};
-      Fqn pathFqn = new Fqn( objs );
-      
-      BuddyReplicationConfig brc = plainCache_.getConfiguration().getBuddyReplicationConfig();
-      this.useBuddyReplication_ = brc != null && brc.isEnabled();
-      if (useTreeCacheMarshalling_ || this.useBuddyReplication_)
-      {
-         // JBAS-5628/JBAS-5629 -- clean out persistent store
-         cleanWebappRegion(pathFqn);
-      }
-
-      // Listen for cache changes
-      cacheListener_ = new CacheListener(cacheWrapper_, manager_, hostName_, webAppPath_);
-      plainCache_.addCacheListener(cacheListener_);
-      
-      if(useTreeCacheMarshalling_)
-      {
-         // register the tcl and bring over the state for the webapp
-         try
-         {
-            
-            log_.debug("UseMarshalling is true. We will register the fqn: " +
-                        pathFqn + " with class loader" +tcl +
-                        " and activate the webapp's Region");
-            Node root = plainCache_.getRoot();
-            if (root.hasChild(pathFqn) == false)
-            {
-               plainCache_.getInvocationContext().getOptionOverrides().setCacheModeLocal(true);
-               root.addChild(pathFqn);
-            }
-            Region region = plainCache_.getRegion(pathFqn, true);
-            region.registerContextClassLoader(tcl);
-            region.activate(); 
-         }
-         catch (Exception ex)
-         {
-            throw new RuntimeException("Can't register class loader", ex);
-         }
-      }
-      
-      if(manager_.isPassivationEnabled())
-      {
-         log_.debug("Passivation is enabled");
-         passivationListener_ = new PassivationListener(manager_, hostName_, webAppPath_);
-         plainCache_.addCacheListener(passivationListener_);
-      }
-      else
-      {
-         log_.debug("Passivation is disabled");
-      }
-   }
-
-   private void cleanWebappRegion(Fqn regionFqn)
-   {
-      try {
-         // Remove locally.
-         plainCache_.getInvocationContext().getOptionOverrides().setCacheModeLocal(true);
-         plainCache_.removeNode(regionFqn);
-      }
-      catch (CacheException e)
-      {
-         log_.error("can't clean content from the underlying distributed cache");
-      }
-   }
-
-   public void stop()
-   {
-      plainCache_.removeCacheListener(cacheListener_);      
-      if (passivationListener_ != null)
-         plainCache_.removeCacheListener(passivationListener_);
-      
-      // Construct the fqn
-      Object[] objs = new Object[]{SESSION, hostName_, webAppPath_};
-      Fqn pathFqn = new Fqn( objs );
-
-      if(useTreeCacheMarshalling_)
-      {
-         log_.debug("UseMarshalling is true. We will inactivate the fqn: " +
-                    pathFqn + " and un-register its classloader");
-            
-         try {
-            Region region = plainCache_.getRegion(pathFqn, false);
-            if (region != null)
-            {
-               region.deactivate();
-               region.unregisterContextClassLoader();
-            }
-         } 
-         catch (Exception e) 
-         {
-            log_.error("Exception during inactivation of webapp region " + pathFqn + 
-                       " or un-registration of its class loader", e);
-         }
-      } 
-      BuddyReplicationConfig brc = plainCache_.getConfiguration().getBuddyReplicationConfig();
-      this.useBuddyReplication_ = brc != null && brc.isEnabled();
-      if (useTreeCacheMarshalling_ || this.useBuddyReplication_)
-      {
-         // JBAS-5628/JBAS-5629 -- clean out persistent store
-         cleanWebappRegion(pathFqn);
-      }
-      // remove session data
-      // BES 2007/08/18 Can't do this as it will 
-      // 1) blow away passivated sessions
-      // 2) leave the cache in an inconsistent state if the war
-      //    is restarted
-//      cacheWrapper_.removeLocalSubtree(pathFqn);
-   }
-
-   /**
-    * Get specfically the BatchModeTransactionManager.
-    */
-   public TransactionManager getTransactionManager()
-   {
-      return tm;
-   }
-   
-   /**
-    * Gets whether TreeCache-based marshalling is available
-    */
-   public boolean isMarshallingAvailable()
-   {
-      return useTreeCacheMarshalling_;
-   }
-
-   /**
-    * Loads any serialized data in the cache into the given session
-    * using its <code>readExternal</code> method.
-    *
-    * @return the session passed as <code>toLoad</code>, or
-    *         <code>null</code> if the cache had no data stored
-    *         under the given session id.
-    */
-   public JBossCacheClusteredSession loadSession(String realId, JBossCacheClusteredSession toLoad)
-   {
-      Fqn fqn = getSessionFqn(realId);
-      Map sessionData =  cacheWrapper_.getData(fqn, true);
-      
-      if (sessionData == null) {
-         // Requested session is no longer in the cache; return null
-         return null;
-      }
-      
-      setupSessionRegion(toLoad, fqn);
-      
-      Integer version = (Integer) sessionData.get(VERSION_KEY);
-      SessionTimestamp timestamp = (SessionTimestamp) sessionData.get(TIMESTAMP_KEY);
-      SessionMetadata metadata = (SessionMetadata) sessionData.get(METADATA_KEY);
-      Map attrs = (Map) getUnMarshalledValue(sessionData.get(ATTRIBUTE_KEY));
-      toLoad.update(version, timestamp, metadata, attrs);
-      
-      return toLoad;
-   }
-
-   public void putSession(String realId, JBossCacheClusteredSession session)
-   { 
-      if (log_.isTraceEnabled())
-      {
-         log_.trace("putSession(): putting session " + session.getIdInternal());
-      }     
-      
-      Fqn fqn = getSessionFqn(realId);
-      
-      setupSessionRegion(session, fqn);
-      
-      Map map = new HashMap();
-      map.put(VERSION_KEY, new Integer(session.getVersion()));
-      
-      boolean replicateTimestamp = false;
-      
-      if (session.isSessionMetadataDirty())
-      {   
-         map.put(METADATA_KEY, session.getSessionMetadata()); 
-         replicateTimestamp = true;
-      }
-     
-      if (session.isSessionAttributeMapDirty())
-      {
-         Map attrs = session.getSessionAttributeMap();
-         // May be null if the session type doesn't use this mechanism to store
-         // attributes (i.e. isn't SessionBasedClusteredSession)
-         if (attrs != null)
-         {
-//            if (useTreeCacheMarshalling_)
-//               map.put(ATTRIBUTE_KEY, attrs);
-//            else
-            map.put(ATTRIBUTE_KEY, getMarshalledValue(attrs));
-         } 
-         
-         replicateTimestamp = true;
-      }
-      
-      if (replicateTimestamp || session.getMustReplicateTimestamp())
-      {
-         map.put(TIMESTAMP_KEY, session.getSessionTimestamp());
-      }
-      
-      cacheWrapper_.put(fqn, map);
-   }
-
-   /**
-    * If the session requires a region in the cache, establishes one.
-    * 
-    * @param session the session
-    * @param fqn the fqn for the session
-    */
-   private void setupSessionRegion(JBossCacheClusteredSession session, Fqn fqn)
-   {
-      if (session.needRegionForSession())
-      {
-         plainCache_.getRegion(fqn, true);
-         session.createdRegionForSession();
-         if (log_.isTraceEnabled())
-         {
-            log_.trace("Created region for session at " + fqn);
-         }
-      }
-   }
-
-   public void removeSession(String realId, boolean removeRegion)
-   {
-      Fqn fqn = getSessionFqn(realId);
-      if (log_.isTraceEnabled())
-      {
-         log_.trace("Remove session from distributed store. Fqn: " + fqn);
-      }
-      
-      if (removeRegion)
-      {
-         plainCache_.removeRegion(fqn);
-      }
-
-      cacheWrapper_.remove(fqn);
-   }
-
-   public void removeSessionLocal(String realId, boolean removeRegion)
-   {
-      Fqn fqn = getSessionFqn(realId);
-      if (log_.isTraceEnabled())
-      {
-         log_.trace("Remove session from my own distributed store only. Fqn: " + fqn);
-      }
-      
-      if (removeRegion)
-      {
-         plainCache_.removeRegion(fqn);
-      }
-      
-      cacheWrapper_.removeLocal(fqn);
-   }
-
-   public void removeSessionLocal(String realId, String dataOwner)
-   {
-      if (dataOwner == null)
-      {
-         removeSessionLocal(realId, false);
-      }
-      else
-      {         
-         Fqn fqn = getSessionFqn(realId, dataOwner);
-         if (log_.isTraceEnabled())
-         {
-            log_.trace("Remove session from my own distributed store only. Fqn: " + fqn);
-         }
-         cacheWrapper_.removeLocal(fqn);
-      }
-   }   
-      
-   public void evictSession(String realId)
-   {
-      evictSession(realId, null);      
-   }   
-      
-   public void evictSession(String realId, String dataOwner)
-   {    
-      Fqn fqn = dataOwner == null ? getSessionFqn(realId) : getSessionFqn(realId, dataOwner);
-      if(log_.isTraceEnabled())
-      {
-         log_.trace("evictSession(): evicting session from my distributed store. Fqn: " + fqn);
-      }
-      cacheWrapper_.evictSubtree(fqn);      
-   }
-
-   public boolean exists(String realId)
-   {
-      Fqn fqn = getSessionFqn(realId);
-      return plainCache_.getRoot().hasChild(fqn);
-   }
-   
-   public Map getSessionData(String realId, String dataOwner)
-   {
-      Fqn fqn = dataOwner == null ? getSessionFqn(realId) : getSessionFqn(realId, dataOwner);
-      return cacheWrapper_.getData(fqn, false);
-   }
-
-   public Object getAttribute(String realId, String key)
-   {
-      Fqn fqn = getAttributeFqn(realId);
-      return getUnMarshalledValue(cacheWrapper_.get(fqn, key));
-   }
-
-   public void putAttribute(String realId, String key, Object value)
-   {
-      Fqn fqn = getAttributeFqn(realId);
-      cacheWrapper_.put(fqn, key, getMarshalledValue(value));
-   }
-
-   public void putAttribute(String realId, Map map)
-   {
-      // Duplicate the map with marshalled values
-      Map marshalled = new HashMap(map.size());
-      Set entries = map.entrySet();
-      for (Iterator it = entries.iterator(); it.hasNext(); )
-      {
-         Map.Entry entry = (Map.Entry) it.next();
-         marshalled.put(entry.getKey(), getMarshalledValue(entry.getValue()));
-      }
-      
-      Fqn fqn = getAttributeFqn(realId);
-      cacheWrapper_.put(fqn, marshalled);
-      
-   }
-
-   public void removeAttributes(String realId)
-   {
-      Fqn fqn = getAttributeFqn(realId);
-      cacheWrapper_.remove(fqn);
-   }
-
-   public Object removeAttribute(String realId, String key)
-   {
-      Fqn fqn = getAttributeFqn(realId);
-      if (log_.isTraceEnabled())
-      {
-         log_.trace("Remove attribute from distributed store. Fqn: " + fqn + " key: " + key);
-      }
-      return getUnMarshalledValue(cacheWrapper_.remove(fqn, key));
-   }
-
-   public void removeAttributesLocal(String realId)
-   {
-      Fqn fqn = getAttributeFqn(realId);
-      if (log_.isTraceEnabled())
-      {
-         log_.trace("Remove attributes from my own distributed store only. Fqn: " + fqn);
-      }
-      cacheWrapper_.removeLocal(fqn);
-   }
-
-   /**
-    * Obtain the keys associated with this fqn. Note that it is not the fqn children.
-    *
-    */
-   public Set getAttributeKeys(String realId)
-   {
-      Set keys = null;
-      Fqn fqn = getAttributeFqn(realId);
-      try
-      {
-         Node node = plainCache_.getRoot().getChild(fqn);
-         if (node != null)
-            keys = node.getKeys();
-      }
-      catch (CacheException e)
-      {
-         log_.error("getAttributeKeys(): Exception getting keys for session " + realId, e);
-      }
-      
-      return keys;
-   }
-
-   /**
-    * Return all attributes associated with this session id.
-    * 
-    * @param realId the session id with any jvmRoute removed
-    * @return the attributes, or any empty Map if none are found.
-    */
-   public Map getAttributes(String realId)
-   {
-      if (realId == null || realId.length() == 0) return new HashMap();
-      
-      Map attrs = new HashMap();
-      Fqn fqn = getAttributeFqn(realId);
-      
-      Node node = plainCache_.getRoot().getChild(fqn);
-      Map rawData = node.getData();
-      
-      for (Iterator it = rawData.entrySet().iterator(); it.hasNext();)
-      {
-         Entry entry = (Entry) it.next();
-         attrs.put(entry.getKey(), getUnMarshalledValue(entry.getValue()));
-      }
-      
-      return attrs;
-   }
-
-   /**
-    * Gets the ids of all sessions in the underlying cache.
-    *
-    * @return Map<String, String> containing all of the session ids of sessions in the cache
-    *         (with any jvmRoute removed) as keys, and the identifier of the data owner for
-    *         the session as value (or a <code>null</code>  value if buddy
-    *         replication is not enabled.) Will not return <code>null</code>.
-    */
-   public Map<String, String> getSessionIds() throws CacheException
-   {
-      Map<String, String> result = new HashMap<String, String>();
-      
-      Node bbRoot = plainCache_.getRoot().getChild(BUDDY_BACKUP_FQN);
-      if (bbRoot != null)
-      {
-         Set owners = bbRoot.getChildren();
-         if (owners != null)
-         {
-            for (Iterator it = owners.iterator(); it.hasNext();)
-            {
-               Node owner = (Node) it.next();
-               Node webRoot = owner.getChild(getWebappFqn());
-               if (webRoot != null)
-               {
-                  Set<String> ids = webRoot.getChildrenNames();
-                  storeSessionOwners(ids, (String) owner.getFqn().getLastElement(), result);
-               }
-            }
-         }
-      }
-      
-      storeSessionOwners(getChildrenNames(getWebappFqn()), null, result);
-
-      return result;
-   }
-   
-   protected Set getChildrenNames(Fqn fqn)
-   {
-      Node node = plainCache_.getRoot().getChild(fqn);
-      return (node == null ? null : node.getChildrenNames());
-   }
-
-   private void storeSessionOwners(Set<String> ids, String owner, Map<String, String> map)
-   {
-      if (ids != null)
-      {
-         for (String id : ids)
-         {            
-            if (!InternalConstant.JBOSS_INTERNAL_STRING.equals(id))
-            {
-               map.put(id, owner);
-            }
-         }
-      }
-   }
-   
-   public boolean isBuddyReplicationEnabled()
-   {
-      BuddyReplicationConfig brc = plainCache_.getConfiguration().getBuddyReplicationConfig();
-      return brc != null && brc.isEnabled();
-   }
- 
-   public boolean isCachePassivationEnabled()
-   {
-      return usePassivation_;      
-   }
-
-   protected Fqn getWebappFqn()
-   {
-      // /SESSION/hostname/webAppPath
-      Object[] objs = new Object[]{SESSION, hostName_, webAppPath_};
-      return new Fqn(objs);
-   }
-   
-   protected Fqn getSessionFqn(String id)
-   {
-      return getSessionFqn(hostName_, webAppPath_, id);
-   }
-   
-   public static Fqn getSessionFqn(String hostname, String contextPath, String sessionId)
-   {
-      Object[] objs = new Object[]{SESSION, hostname, contextPath, sessionId};
-      return new Fqn(objs);
-   }
-
-   private Fqn getSessionFqn(String id, String dataOwner)
-   {
-      return getSessionFqn(dataOwner, hostName_, webAppPath_, id);
-   }
-   
-   public static Fqn getSessionFqn(String dataOwner, String hostname, String contextPath, String sessionId)
-   {
-      Object[] objs = new Object[]{BUDDY_BACKUP, dataOwner, SESSION, hostname, contextPath, sessionId};
-      return new Fqn(objs);
-   }
-
-   protected Fqn getAttributeFqn(String id)
-   {
-      return getAttributeFqn(hostName_, webAppPath_, id);
-   }
-
-   public static Fqn getAttributeFqn(String hostname, String contextPath, String sessionId)
-   {
-      Object[] objs = new Object[]{SESSION, hostname, contextPath, sessionId, ATTRIBUTE};
-      return new Fqn(objs);
-   }
-
-   private Object getMarshalledValue(Object value)
-   {
-      // JBAS-2920.  For now, continue using MarshalledValue, as 
-      // it allows lazy deserialization of the attribute on remote nodes
-      // For Branch_4_0 this is what we have to do anyway for backwards
-      // compatibility. For HEAD we'll follow suit for now.
-      // TODO consider only using MV for complex objects (i.e. not primitives)
-      // and Strings longer than X.
-      
-//      if (useTreeCacheMarshalling_)
-//      {
-//         return value;
-//      }
-//      else
-//      {
-         
-         // JBAS-2921 - replaced MarshalledValue calls with SessionSerializationFactory calls
-         // to allow for switching between JBossSerialization and JavaSerialization using
-         // system property -D=session.serialization.jboss=true / false
-         // MarshalledValue mv = new MarshalledValue(value);
-         if  (MarshalledValueHelper.isTypeExcluded(value.getClass()))
-         {
-            return value;               
-         }
-         else 
-         {
-            try
-            {
-               CachableMarshalledValue mv = SessionSerializationFactory.createMarshalledValue((Serializable) value);
-               return mv;
-            }
-            catch (ClassCastException e)
-            {
-               throw new IllegalArgumentException(value + " does not implement java.io.Serializable");
-            }
-         }
-//      }
-   }
-
-   private Object getUnMarshalledValue(Object obj)
-   {
-      // JBAS-2920.  For now, continue using MarshalledValue, as 
-      // it allows lazy deserialization of the attribute on remote nodes
-      // For Branch_4_0 this is what we have to do anyway for backwards
-      // compatibility. For HEAD we'll follow suit for now.
-//      if (useTreeCacheMarshalling_)
-//      {
-//         return mv;
-//      }
-//      else
-//      {
-         if (!(obj instanceof SimpleCachableMarshalledValue))
-            return obj;
-         
-         // Swap in/out the tcl for this web app. Needed only for un marshalling.
-         ClassLoader prevTCL = Thread.currentThread().getContextClassLoader();
-         Thread.currentThread().setContextClassLoader(manager_.getWebappClassLoader());
-         try
-         {
-            SimpleCachableMarshalledValue mv = (SimpleCachableMarshalledValue) obj;
-            mv.setObjectStreamSource(SessionSerializationFactory.getObjectStreamSource());
-            return mv.get();
-         }
-         catch (IOException e)
-         {
-            log_.error("IOException occurred unmarshalling value ", e);
-            return null;
-         }
-         catch (ClassNotFoundException e)
-         {
-            log_.error("ClassNotFoundException occurred unmarshalling value ", e);
-            return null;
-         }
-         finally
-         {
-            Thread.currentThread().setContextClassLoader(prevTCL);
-         }
-//      }
-   }
-
-}

Deleted: trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/JBossCacheWrapper.java
===================================================================
--- trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/JBossCacheWrapper.java	2008-08-26 16:10:33 UTC (rev 77495)
+++ trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/JBossCacheWrapper.java	2008-08-26 16:41:17 UTC (rev 77496)
@@ -1,255 +0,0 @@
-/*
-* JBoss, Home of Professional Open Source
-* Copyright 2005, JBoss Inc., and individual contributors as indicated
-* by the @authors tag. See the copyright.txt 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.web.tomcat.service.session;
-
-import java.util.Map;
-
-import org.jboss.cache.Cache;
-import org.jboss.cache.Fqn;
-import org.jboss.cache.lock.TimeoutException;
-
-public class JBossCacheWrapper 
-{
-   private static final int RETRY = 3;
-   private static final String RETRY_FAIL_MSG = 
-      "Continued to catch TimeoutException during " + 
-       RETRY + " retry attempts. Giving up.";
-   private Cache plainCache_;
-   
-   JBossCacheWrapper(Cache cache)
-   {
-      plainCache_ = cache;
-   }
-   
-   Map getData(Fqn fqn, boolean gravitate)
-   {
-      TimeoutException ex = null;
-      for (int i = 0; i < RETRY; i++)
-      {
-         try
-         {            
-            if (gravitate)
-            {            
-               plainCache_.getInvocationContext().getOptionOverrides()
-                                                 .setForceDataGravitation(true);
-            }
-            return plainCache_.getData(fqn);
-         }
-         catch (TimeoutException e)
-         {
-            ex = e;
-         }
-      }      
-      throw new RuntimeException(RETRY_FAIL_MSG, ex);      
-   }
-
-   /**
-    * Wrapper to embed retry logic.
-    *
-    * @param fqn
-    * @param id
-    * @return
-    */
-   Object get(Fqn fqn, String id)
-   {
-      return get(fqn, id, false);
-   }
-
-   /**
-    * Wrapper to embed retry logic.
-    *
-    * @param fqn
-    * @param id
-    * @return
-    */
-   Object get(Fqn fqn, String id, boolean gravitate)
-   {
-      TimeoutException ex = null;
-      for (int i = 0; i < RETRY; i++)
-      {
-         try
-         {            
-            if (gravitate)
-            {            
-               plainCache_.getInvocationContext().getOptionOverrides()
-                                                 .setForceDataGravitation(true);
-            }
-            return plainCache_.get(fqn, id);
-         }
-         catch (TimeoutException e)
-         {
-            ex = e;
-         }
-      }
-      
-      throw new RuntimeException(RETRY_FAIL_MSG, ex);
-   }
-
-   /**
-    * Wrapper to embed retry logic.
-    *
-    * @param fqn
-    * @param id
-    * @param value
-    * @return
-    */
-   void put(Fqn fqn, String id, Object value)
-   {
-      TimeoutException ex = null;
-      for (int i = 0; i < RETRY; i++)
-      {
-         try
-         {
-            plainCache_.put(fqn, id, value);
-            return;
-         }
-         catch (TimeoutException e)
-         {
-            ex = e;
-         }
-      }
-      
-      throw new RuntimeException(RETRY_FAIL_MSG, ex);
-   }
-
-
-   /**
-    * Wrapper to embed retry logic.
-    *
-    * @param fqn
-    * @param map
-    */
-   void put(Fqn fqn, Map map)
-   {
-      TimeoutException ex = null;
-      for (int i = 0; i < RETRY; i++)
-      {
-         try
-         {
-            plainCache_.put(fqn, map);
-            return;
-         }
-         catch (TimeoutException e)
-         {
-            ex = e;
-         }
-      }
-      
-      throw new RuntimeException(RETRY_FAIL_MSG, ex);
-   }
-
-   /**
-    * Wrapper to embed retry logic.
-    *
-    * @param fqn
-    * @param id
-    * @return
-    */
-   Object remove(Fqn fqn, String id)
-   {
-      TimeoutException ex = null;
-      for (int i = 0; i < RETRY; i++)
-      {
-         try
-         {
-            return plainCache_.remove(fqn, id);
-         }
-         catch (TimeoutException e)
-         {
-            ex = e;
-         }
-      }
-      
-      throw new RuntimeException(RETRY_FAIL_MSG, ex);
-   }
-
-   /**
-    * Wrapper to embed retry logic.
-    *
-    * @param fqn
-    */
-   void remove(Fqn fqn)
-   {
-      TimeoutException ex = null;
-      for (int i = 0; i < RETRY; i++)
-      {
-         try
-         {
-            plainCache_.removeNode(fqn);
-            return;
-         }
-         catch (TimeoutException e)
-         {
-            ex = e;
-         }
-      }
-      
-      throw new RuntimeException(RETRY_FAIL_MSG, ex);
-   }
-   
-   /**
-    * Wrapper to embed retry logic.
-    *
-    * @param fqn
-    */
-   void removeLocal(Fqn fqn)
-   {
-      TimeoutException ex = null;
-      for (int i = 0; i < RETRY; i++)
-      {
-         try
-         {
-            plainCache_.getInvocationContext().getOptionOverrides()
-                                              .setCacheModeLocal(true);
-            plainCache_.removeNode(fqn);
-            return;
-         }
-         catch (TimeoutException e)
-         {
-            ex = e;
-         }
-      }
-      
-      throw new RuntimeException(RETRY_FAIL_MSG, ex);
-   }
-   
-   void evictSubtree(Fqn fqn)
-   {      
-      TimeoutException ex = null;
-      for (int i = 0; i < RETRY; i++)
-      {
-         try
-         {
-            plainCache_.evict(fqn, true);
-            return;
-            
-         }
-         catch (TimeoutException e)
-         {
-            ex = e;
-         }
-      }
-      
-      throw new RuntimeException(RETRY_FAIL_MSG, ex);
-   }
-
-}

Modified: trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/JBossManager.java
===================================================================
--- trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/JBossManager.java	2008-08-26 16:10:33 UTC (rev 77495)
+++ trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/JBossManager.java	2008-08-26 16:41:17 UTC (rev 77496)
@@ -55,6 +55,7 @@
 import org.jboss.logging.Logger;
 import org.jboss.metadata.web.jboss.JBossWebMetaData;
 import org.jboss.metadata.web.jboss.PassivationConfig;
+import org.jboss.web.tomcat.service.session.distributedcache.spi.ClusteringNotSupportedException;
 import org.jboss.web.tomcat.statistics.ReplicationStatistics;
 
 

Deleted: trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/JBossSerializationObjectStreamSource.java
===================================================================
--- trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/JBossSerializationObjectStreamSource.java	2008-08-26 16:10:33 UTC (rev 77495)
+++ trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/JBossSerializationObjectStreamSource.java	2008-08-26 16:41:17 UTC (rev 77496)
@@ -1,54 +0,0 @@
-/*
- * JBoss, Home of Professional Open Source.
- * Copyright 2008, Red Hat Middleware LLC, 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.web.tomcat.service.session;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.ObjectInput;
-import java.io.ObjectOutput;
-import java.io.OutputStream;
-
-import org.jboss.ha.framework.interfaces.ObjectStreamSource;
-import org.jboss.serial.io.JBossObjectInputStreamSharedTree;
-import org.jboss.serial.io.JBossObjectOutputStreamSharedTree;
-
-/**
- * {@link ObjectStreamSource} implementation that provides 
- * {@link JBossObjectInputStreamSharedTree} and {@link JBossObjectOutputStreamSharedTree}.
- * 
- * @author Brian Stansberry
- */
-public class JBossSerializationObjectStreamSource implements ObjectStreamSource
-{
-
-   public ObjectInput getObjectInput(InputStream input) throws IOException
-   {
-      return new JBossObjectInputStreamSharedTree(input);
-   }
-
-   public ObjectOutput getObjectOutput(OutputStream output) throws IOException
-   {
-      return new JBossObjectOutputStreamSharedTree(output);
-   }
-
-}

Deleted: trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/PassivationListener.java
===================================================================
--- trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/PassivationListener.java	2008-08-26 16:10:33 UTC (rev 77495)
+++ trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/PassivationListener.java	2008-08-26 16:41:17 UTC (rev 77496)
@@ -1,75 +0,0 @@
-/*
- * JBoss, Home of Professional Open Source.
- * Copyright 2006, Red Hat Middleware LLC, 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.web.tomcat.service.session;
-
-import org.jboss.cache.Fqn;
-import org.jboss.cache.notifications.annotation.NodeActivated;
-import org.jboss.cache.notifications.event.NodeActivatedEvent;
-
-/**
- * Listener for JBoss Cache activation events.  Triggers updates of
- * the passivation counter.
- * 
- * @author <a href="brian.stansberry at jboss.com">Brian Stansberry</a>
- * @version $Revision$
- */
- at org.jboss.cache.notifications.annotation.CacheListener
-public class PassivationListener extends CacheListenerBase
-{
-
-   PassivationListener(JBossCacheManager manager, String hostname, String webapp)
-   {      
-      super(manager, hostname, webapp);
-   }
-   
-   // NOTE: Don't track passivation from here -- we know in JBossCacheManager
-   // when we trigger a passivation. Avoid spurious listener callbacks to
-   // webapps that aren't interested.
-   
-//   @NodePassivated
-//   public void nodePassivated(NodePassivatedEvent event)
-//   {
-//      Fqn fqn = event.getFqn();
-//      if (isFqnForOurWebapp(fqn, isBuddyFqn(fqn)))
-//      {
-//         manager_.sessionPassivated();
-//      }
-//   }
-   
-   // We do want activation callbacks, as JBossCacheManager can't readily
-   // track whether a cache read is going to result in an activation
-   
-   @NodeActivated
-   public void nodeActivated(NodeActivatedEvent event)
-   {
-      Fqn fqn = event.getFqn();
-      boolean isBuddy = isBuddyFqn(fqn);      
-      if (isFqnSessionRootSized(fqn, isBuddy) 
-            && isFqnForOurWebapp(fqn, isBuddy))
-      {
-         manager_.sessionActivated();
-      }
-      
-   }
-
-}

Modified: trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/SessionBasedClusteredSession.java
===================================================================
--- trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/SessionBasedClusteredSession.java	2008-08-26 16:10:33 UTC (rev 77495)
+++ trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/SessionBasedClusteredSession.java	2008-08-26 16:41:17 UTC (rev 77496)
@@ -24,7 +24,10 @@
 import java.util.HashMap;
 import java.util.Map;
 
+import org.jboss.web.tomcat.service.session.distributedcache.spi.DistributableSessionMetadata;
+import org.jboss.web.tomcat.service.session.distributedcache.spi.DistributableSessionTimestamp;
 
+
 /**
  * Implementation of a clustered session for the JBossCacheManager. The replication granularity
  * level is session based; that is, we replicate per whole session object.
@@ -139,8 +142,8 @@
       return attrs;
    }
 
-   protected void update(Integer version, SessionTimestamp timestamp, 
-         SessionMetadata metadata, Map updatedAttrs)
+   public void update(Integer version, DistributableSessionTimestamp timestamp, 
+         DistributableSessionMetadata metadata, Map updatedAttrs)
    {
       // A new session may not have sent over any attributes
       if (updatedAttrs == null)

Deleted: trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/SessionSerializationFactory.java
===================================================================
--- trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/SessionSerializationFactory.java	2008-08-26 16:10:33 UTC (rev 77495)
+++ trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/SessionSerializationFactory.java	2008-08-26 16:41:17 UTC (rev 77496)
@@ -1,87 +0,0 @@
-/*
-* JBoss, Home of Professional Open Source
-* Copyright 2005, JBoss Inc., and individual contributors as indicated
-* by the @authors tag. See the copyright.txt 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.web.tomcat.service.session;
-
-import java.io.Serializable;
-
-import org.jboss.ha.framework.interfaces.ObjectStreamSource;
-import org.jboss.ha.framework.server.MarshalledValueObjectStreamSource;
-import org.jboss.ha.framework.server.SimpleCachableMarshalledValue;
-import org.jboss.logging.Logger;
-
-/**
- * Factory class for creating object output and input streams, 
- * switching between JDK Serialization and JBoss Serialization classes. 
- * Using MarshalledValue to replace Serializable used inside different 
- * web app class loader context. Assuming that the caller classes are already
- * handling the switch between web app class loader context. 
- *  
- * 
- * @author <a href="hmesha at novell.com">Hany Mesha</a>
- * @author Brian Stansberry
- */
-public class SessionSerializationFactory
-{
-   /** 
-    * System property that, if set to <code>true</code>, indicates that 
-    * JBoss Serialization should be used for session serialization.
-    */
-   public static final String JBOSS_SERIALIZATION_SYS_PROP = "session.serialization.jboss";
-   private static Logger log_ = Logger.getLogger(SessionSerializationFactory.class);
-   private static boolean useJBossSerialization = false;
-   private static ObjectStreamSource objectStreamSource; 
-
-   static
-   {
-       String useJBossSerializationStr = System.getProperty(JBOSS_SERIALIZATION_SYS_PROP, "false");
-       useJBossSerialization = Boolean.valueOf(useJBossSerializationStr).booleanValue();
-       objectStreamSource = useJBossSerialization ? new JBossSerializationObjectStreamSource()
-                                                  : new MarshalledValueObjectStreamSource(); 
-       try
-       {
-          if (useJBossSerialization)
-          {
-             log_.debug("Using JBossSerialization for web session replication");
-          }
-       }
-       catch (Throwable ignored) 
-       {
-          // we don't fail a static initializer due to a debug log stmt
-       }
-   }
-   
-   public static SimpleCachableMarshalledValue createMarshalledValue(Serializable o)
-   {
-      return new SimpleCachableMarshalledValue(o, objectStreamSource);
-   }
-   
-   public static ObjectStreamSource getObjectStreamSource()
-   {
-      return objectStreamSource;
-   }
-
-   public static boolean useJBossSerialization()
-   {
-       return useJBossSerialization;
-   }
-
-}

Modified: trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/Util.java
===================================================================
--- trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/Util.java	2008-08-26 16:10:33 UTC (rev 77495)
+++ trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/Util.java	2008-08-26 16:41:17 UTC (rev 77496)
@@ -30,12 +30,6 @@
 import java.util.Set;
 
 import org.jboss.aop.Advised;
-import org.jboss.cache.Cache;
-import org.jboss.cache.CacheManager;
-import org.jboss.cache.pojo.PojoCache;
-import org.jboss.ha.framework.server.CacheManagerLocator;
-import org.jboss.ha.framework.server.PojoCacheManager;
-import org.jboss.ha.framework.server.PojoCacheManagerLocator;
 
 /**
  * Utility methods related to JBoss distributed sessions.
@@ -66,80 +60,6 @@
          Class.class}));
 
    
-   public static PojoCache findPojoCache(String cacheConfigName) throws ClusteringNotSupportedException
-   {
-      PojoCacheManager pcm = getManagerForPojoCache(cacheConfigName);
-      
-      try
-      {
-         return pcm.getPojoCache(cacheConfigName, true);
-      }
-      catch (RuntimeException re)
-      {
-         throw re;
-      }
-      catch (Exception e)
-      {
-         throw new RuntimeException("Problem accessing cache " + cacheConfigName, e);
-      }
-   }
-   
-   public static Cache findPlainCache(String cacheConfigName) throws ClusteringNotSupportedException
-   {
-      CacheManager pcm = getManagerForCache(cacheConfigName);
-      
-      try
-      {
-         return pcm.getCache(cacheConfigName, true);
-      }
-      catch (RuntimeException re)
-      {
-         throw re;
-      }
-      catch (Exception e)
-      {
-         throw new RuntimeException("Problem accessing cache " + cacheConfigName, e);
-      }
-   }
-   
-   private static PojoCacheManager getManagerForPojoCache(String cacheConfigName) 
-      throws ClusteringNotSupportedException
-   {
-      PojoCacheManager pcm = null;
-      try
-      {
-         pcm = PojoCacheManagerLocator.getCacheManagerLocator().getCacheManager(null);
-      }
-      catch (Throwable t)
-      {
-         throw new ClusteringNotSupportedException("Could not access PojoCacheManager for JBossWeb clustering", t);
-      }
-      
-      if (!pcm.getConfigurationNames().contains(cacheConfigName))
-         throw new IllegalStateException("PojoCacheManager does not recognize config " + cacheConfigName);
-      
-      return pcm;
-   }
-   
-   private static CacheManager getManagerForCache(String cacheConfigName) 
-      throws ClusteringNotSupportedException
-   {
-      CacheManager cm = null;
-      try
-      {
-         cm = CacheManagerLocator.getCacheManagerLocator().getCacheManager(null);
-      }
-      catch (Throwable t)
-      {
-         throw new ClusteringNotSupportedException("Could not access CacheManager for JBossWeb clustering", t);
-      }
-      
-      if (!cm.getConfigurationNames().contains(cacheConfigName))
-         throw new IllegalStateException("CacheManager does not recognize config " + cacheConfigName);
-      
-      return cm;
-   }
-   
    /**
     * Returns a session id with any trailing jvmRoute removed.
     * 

Added: trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/distributedcache/impl/DistributedCacheManagerFactoryImpl.java
===================================================================
--- trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/distributedcache/impl/DistributedCacheManagerFactoryImpl.java	                        (rev 0)
+++ trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/distributedcache/impl/DistributedCacheManagerFactoryImpl.java	2008-08-26 16:41:17 UTC (rev 77496)
@@ -0,0 +1,245 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2008, Red Hat Middleware LLC, 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.web.tomcat.service.session.distributedcache.impl;
+
+import java.io.File;
+
+import javax.management.MBeanServer;
+import javax.management.ObjectName;
+
+import org.apache.tomcat.util.modeler.Registry;
+import org.jboss.cache.Cache;
+import org.jboss.cache.config.Configuration;
+import org.jboss.cache.pojo.PojoCache;
+import org.jboss.cache.pojo.PojoCacheFactory;
+import org.jboss.cache.pojo.jmx.PojoCacheJmxWrapper;
+import org.jboss.cache.pojo.jmx.PojoCacheJmxWrapperMBean;
+import org.jboss.cache.transaction.BatchModeTransactionManagerLookup;
+import org.jboss.mx.util.MBeanProxyExt;
+import org.jboss.web.tomcat.service.session.distributedcache.impl.jbc2.FieldBasedJBossCacheService;
+import org.jboss.web.tomcat.service.session.distributedcache.impl.jbc2.JBossCacheService;
+import org.jboss.web.tomcat.service.session.distributedcache.spi.ClusteringNotSupportedException;
+import org.jboss.web.tomcat.service.session.distributedcache.spi.DistributedCacheManager;
+import org.jboss.web.tomcat.service.session.distributedcache.spi.FieldBasedDistributedCacheManager;
+import org.jboss.web.tomcat.service.session.distributedcache.spi.TomcatClusterConfig;
+import org.jboss.web.tomcat.service.session.distributedcache.spi.TomcatClusterDistributedCacheManagerFactory;
+
+/**
+ * @author Brian Stansberry
+ *
+ */
+public class DistributedCacheManagerFactoryImpl implements TomcatClusterDistributedCacheManagerFactory
+{
+
+   public static final String DEFAULT_CLUSTER_NAME = "Tomcat-Cluster";
+
+   /** TreeCache's isolation level */
+   public static final String DEFAULT_ISOLATION_LEVEL = "REPEATABLE_READ";
+   
+   /** TreeCache's cache mode */
+   public static final String DEFAULT_CACHE_MODE = "REPL_ASYNC";
+   
+   /** TreeCache's lock aquisition timeout */
+   public static final long DEFAULT_LOCK_TIMEOUT = 15000;
+   
+   /** TransactionManagerLookup implementation that the TreeCache should use. */
+   public static final String DEFAULT_TM_LOOKUP = 
+      BatchModeTransactionManagerLookup.class.getName();
+   
+   private TomcatClusterConfig tomcatConfig;
+   private MBeanServer mserver;
+   private boolean pojoCacheLocal = false;
+   
+   private PojoCache pojoCache;
+   private Cache plainCache;
+   
+   public DistributedCacheManager getDistributedCacheManager(String cacheConfigName)
+         throws ClusteringNotSupportedException
+   {
+      return plainCache == null? new JBossCacheService(cacheConfigName) : new JBossCacheService(plainCache);
+   }
+
+   public FieldBasedDistributedCacheManager getFieldBasedDistributedCacheManager(String cacheConfigName)
+         throws ClusteringNotSupportedException
+   {
+      return pojoCache == null? new FieldBasedJBossCacheService(cacheConfigName) : new FieldBasedJBossCacheService(pojoCache);
+   }
+
+   public TomcatClusterConfig getTomcatClusterConfig()
+   {
+      return tomcatConfig;
+   }
+
+   public void setTomcatClusterConfig(TomcatClusterConfig clusterConfig)
+   {
+      this.tomcatConfig = clusterConfig;
+   }
+
+   public void start() throws Exception
+   {
+      if (tomcatConfig != null)
+      {
+         initializePojoCache();
+      }      
+   }
+
+   public void stop() throws Exception
+   {
+      if (pojoCache != null)
+      {
+         pojoCache.stop();
+         pojoCache.destroy();
+         
+         if (pojoCacheLocal && mserver != null && tomcatConfig.getCacheObjectName() != null)
+         {
+            mserver.unregisterMBean(new ObjectName(tomcatConfig.getCacheObjectName()));
+         }
+      }      
+   }
+
+   public PojoCache getPojoCache()
+   {
+      return pojoCache;
+   }
+
+   /** 
+    * Hook for test fixtures to inject a PojoCache, which if present will
+    * be used to create the DistributedCacheManager in preference to any
+    * passed <code>cacheConfigName</code>.
+    */
+   public void setPojoCache(PojoCache pojoCache)
+   {
+      this.pojoCache = pojoCache;
+      this.plainCache = pojoCache.getCache();
+   }
+
+   public Cache getPlainCache()
+   {
+      return plainCache;
+   }
+
+   /** 
+    * Hook for test fixtures to inject a Cache, which if present will
+    * be used to create the DistributedCacheManager in preference to any
+    * passed <code>cacheConfigName</code>.
+    */
+   public void setPlainCache(Cache plainCache)
+   {
+      this.plainCache = plainCache;
+      this.pojoCache = null;
+   }
+   
+   /**
+    * Convenience method for test fixtures to clear any injected cache.
+    */
+   public void clearCaches()
+   {
+      this.plainCache = null;
+      this.pojoCache = null;
+   }
+   
+   
+   /**
+    * Gets our TreeCache, either from a local reference or the JMX
+    * server.  If one is not found, creates and configures it.
+    */
+   private void initializePojoCache() throws Exception
+   {
+      if (pojoCache == null) {
+         
+         PojoCacheJmxWrapperMBean pcWrapper = null;
+         MBeanServer server = getMBeanServer();
+         String cfgName = tomcatConfig.getCacheObjectName();
+         ObjectName objName = cfgName == null ? null : new ObjectName(tomcatConfig.getCacheObjectName());
+         if (server != null && objName != null && server.isRegistered(objName))
+         {
+            // Get a proxy to the existing TreeCache
+            pcWrapper = ((PojoCacheJmxWrapperMBean) 
+                           MBeanProxyExt.create(PojoCacheJmxWrapperMBean.class, objName));
+         }
+         else
+         {
+            // See if there is an XML descriptor file to configure the cache
+            File configFile = tomcatConfig.getCacheConfigFile();
+            String clusterName = tomcatConfig.getClusterName();
+            
+            if (configFile != null)
+            {
+               pcWrapper = new PojoCacheJmxWrapper(PojoCacheFactory.createCache(configFile.getAbsolutePath(), false));
+               Configuration config = pojoCache.getCache().getConfiguration();
+                              
+               if (clusterName != null)
+               {
+                  // Override the XML config with the name provided in
+                  // server.xml.  Method setClusterName is specified in the
+                  // Cluster interface, otherwise we would not do this
+                  config.setClusterName(clusterName);
+               }
+            }
+            else
+            {
+               // User did not try to configure the cache.
+               // Configure it using defaults.  Only exception
+               // is the clusterName, which user can specify in server.xml.
+               Configuration config = new Configuration();
+               String channelName = (clusterName == null) ? DEFAULT_CLUSTER_NAME
+                                                          : clusterName;
+               config.setClusterName(channelName);
+               config.setIsolationLevel(DEFAULT_ISOLATION_LEVEL);
+               config.setCacheMode(DEFAULT_CACHE_MODE);
+               config.setLockAcquisitionTimeout(DEFAULT_LOCK_TIMEOUT);
+               config.setTransactionManagerLookupClass(DEFAULT_TM_LOOKUP);
+               
+               pcWrapper = new PojoCacheJmxWrapper(PojoCacheFactory.createCache(config, false));
+            }
+            
+            if (server != null && objName != null)
+            {
+               server.registerMBean(pcWrapper, objName);
+            }
+            
+            pojoCacheLocal = true;
+         }
+         
+         setPojoCache(pcWrapper.getPojoCache());
+      }
+   }
+   
+
+
+   /**
+    * Get the current Catalina MBean Server.
+    * 
+    * @return
+    * @throws Exception
+    */
+   private MBeanServer getMBeanServer() throws Exception
+   {
+      if (mserver == null)
+      {
+         mserver = Registry.getRegistry(null, null).getMBeanServer();
+      }
+      return mserver;
+   }
+   
+}

Added: trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/distributedcache/impl/jbc2/BatchingManagerImpl.java
===================================================================
--- trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/distributedcache/impl/jbc2/BatchingManagerImpl.java	                        (rev 0)
+++ trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/distributedcache/impl/jbc2/BatchingManagerImpl.java	2008-08-26 16:41:17 UTC (rev 77496)
@@ -0,0 +1,96 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2008, Red Hat Middleware LLC, 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.web.tomcat.service.session.distributedcache.impl.jbc2;
+
+import javax.transaction.RollbackException;
+import javax.transaction.Status;
+import javax.transaction.TransactionManager;
+
+import org.jboss.logging.Logger;
+import org.jboss.web.tomcat.service.session.distributedcache.spi.BatchingManager;
+
+/**
+ * @author Brian Stansberry
+ *
+ */
+class BatchingManagerImpl implements BatchingManager
+{
+   private static final Logger log = Logger.getLogger(BatchingManagerImpl.class);
+   
+   private final TransactionManager tm;
+   
+   BatchingManagerImpl(TransactionManager tm)
+   {
+      assert tm != null : " batchingManager is null";
+      
+      this.tm = tm;
+   }
+
+   public boolean isBatchInProgress() throws Exception
+   {
+      return (tm.getTransaction() != null);
+   }
+
+   public void startBatch() throws Exception
+   {
+      tm.begin();
+   }
+
+   public void setBatchRollbackOnly() throws Exception
+   {
+      tm.setRollbackOnly();
+   }
+
+   public void endBatch()
+   {
+      try
+      {
+         if(tm.getTransaction().getStatus() != Status.STATUS_MARKED_ROLLBACK)
+         {
+            tm.commit();
+         }
+         else
+         {
+            log.debug("endBatch(): rolling back batch");
+            tm.rollback();
+         }
+      }
+      catch (RollbackException re)
+      {
+         // Do nothing here since cache may rollback automatically.
+         log.warn("endBatch(): rolling back transaction with exception: " +re);
+      }
+      catch (RuntimeException re)
+      {
+         throw re;
+      }
+      catch (Exception e)
+      {
+         throw new RuntimeException("endTransaction(): Caught Exception ending batch: ", e);
+      }
+      
+   }
+   
+   
+   
+}

Added: trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/distributedcache/impl/jbc2/CacheListener.java
===================================================================
--- trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/distributedcache/impl/jbc2/CacheListener.java	                        (rev 0)
+++ trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/distributedcache/impl/jbc2/CacheListener.java	2008-08-26 16:41:17 UTC (rev 77496)
@@ -0,0 +1,188 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2008, Red Hat Middleware LLC, 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.web.tomcat.service.session.distributedcache.impl.jbc2;
+
+import java.util.Map;
+
+import org.jboss.cache.Fqn;
+import org.jboss.cache.notifications.annotation.NodeModified;
+import org.jboss.cache.notifications.annotation.NodeRemoved;
+import org.jboss.cache.notifications.event.NodeModifiedEvent;
+import org.jboss.cache.notifications.event.NodeRemovedEvent;
+import org.jboss.logging.Logger;
+import org.jboss.metadata.web.jboss.ReplicationGranularity;
+import org.jboss.web.tomcat.service.session.distributedcache.spi.DistributableSessionMetadata;
+import org.jboss.web.tomcat.service.session.distributedcache.spi.DistributableSessionTimestamp;
+import org.jboss.web.tomcat.service.session.distributedcache.spi.LocalDistributableSessionManager;
+
+/**
+ * Listens for removals and modifications in the cache, notifying the 
+ * session manager of significant events.
+ * 
+ * @author Brian Stansberry
+ */
+ at org.jboss.cache.notifications.annotation.CacheListener
+public class CacheListener extends CacheListenerBase
+{
+   // Element within an FQN that is the root of a Pojo attribute map
+   private static final int POJO_ATTRIBUTE_FQN_INDEX = SESSION_ID_FQN_INDEX + 1;
+   // Element within an FQN that is the root of an individual Pojo attribute
+   private static final int POJO_KEY_FQN_INDEX = POJO_ATTRIBUTE_FQN_INDEX + 1;
+   // Element within an FQN that is the root of a session's internal pojo storage area
+   private static final int POJO_INTERNAL_FQN_INDEX = SESSION_ID_FQN_INDEX + 1;
+   // Minimum size of an FQN that is below the root of a session's internal pojo storage area
+   private static final int POJO_INTERNAL_FQN_SIZE = POJO_INTERNAL_FQN_INDEX + 1;
+   private static Logger log_ = Logger.getLogger(CacheListener.class);
+   private boolean fieldBased_;
+
+   CacheListener(JBossCacheWrapper wrapper, LocalDistributableSessionManager manager, String hostname, String webapp)
+   {      
+      super(manager, hostname, webapp);
+      fieldBased_ = (manager_.getReplicationGranularity() == ReplicationGranularity.FIELD);
+   }
+
+   // --------------- CacheListener methods ------------------------------------
+
+   @NodeRemoved
+   public void nodeRemoved(NodeRemovedEvent event)
+   {      
+      if (event.isPre())
+         return;
+      
+      boolean local = event.isOriginLocal();
+      if (!fieldBased_ && local)
+         return;
+      
+      Fqn fqn = event.getFqn();
+      boolean isBuddy = isBuddyFqn(fqn);
+      
+      if (!local 
+            && isFqnSessionRootSized(fqn, isBuddy) 
+            && isFqnForOurWebapp(fqn, isBuddy))
+      {
+         // A session has been invalidated from another node;
+         // need to inform manager
+         String sessId = getIdFromFqn(fqn, isBuddy);
+         manager_.notifyRemoteInvalidation(sessId);
+      }
+      else if (local && !isBuddy
+                  && isPossibleInternalPojoFqn(fqn) 
+                  && isFqnForOurWebapp(fqn, isBuddy))
+      {
+         // One of our sessions' pojos is modified; need to inform
+         // the manager so it can mark the session dirty
+         String sessId = getIdFromFqn(fqn, isBuddy);
+         manager_.notifyLocalAttributeModification(sessId);
+      }
+   }
+   
+   @NodeModified
+   public void nodeModified(NodeModifiedEvent event)
+   {      
+      if (event.isPre())
+         return;
+      
+      boolean local = event.isOriginLocal();
+      if (!fieldBased_ && local)
+         return;
+      
+      Fqn fqn = event.getFqn();
+      boolean isBuddy = isBuddyFqn(fqn);      
+      
+      if (isFqnSessionRootSized(fqn, isBuddy)
+             &&isFqnForOurWebapp(fqn, isBuddy))
+      {
+         // Query if we have version value in the distributed cache. 
+         // If we have a version value, compare the version and invalidate if necessary.
+         Map data = event.getData();
+         Integer version = (Integer) data.get(JBossCacheService.VERSION_KEY);
+         if(version != null)
+         {
+            String realId = getIdFromFqn(fqn, isBuddy);
+            String owner = isBuddy ? getBuddyOwner(fqn) : null;
+            // Notify the manager that a session has been updated
+            boolean updated = manager_.sessionChangedInDistributedCache(realId, owner, 
+                                               version.intValue(), 
+                                               (DistributableSessionTimestamp) data.get(JBossCacheService.TIMESTAMP_KEY), 
+                                               (DistributableSessionMetadata) data.get(JBossCacheService.METADATA_KEY));
+            if (!updated && !isBuddy)
+            {
+               log_.warn("Possible concurrency problem: Replicated version id " + 
+                         version + " matches in-memory version for session " + realId); 
+            }
+            /*else 
+            {
+               We have a local session but got a modification for the buddy tree.
+               This means another node is in the process of taking over the session;
+               we don't worry about it
+            }
+             */
+         }
+         else
+         {
+            log_.warn("No VERSION_KEY attribute found in " + fqn);
+         }
+      }
+      else if (local && !isBuddy
+            && isPossibleInternalPojoFqn(fqn) 
+            && isFqnForOurWebapp(fqn, isBuddy))
+      {
+         // One of our sessions' pojos is modified; need to inform
+         // the manager so it can mark the session dirty
+         String sessId = getIdFromFqn(fqn, isBuddy);
+         manager_.notifyLocalAttributeModification(sessId);
+      }
+   }
+   
+   public static String getPojoKeyFromFqn(Fqn fqn, boolean isBuddy)
+   {
+      return (String) fqn.get(isBuddy ? BUDDY_BACKUP_ROOT_OWNER_SIZE + POJO_KEY_FQN_INDEX: POJO_KEY_FQN_INDEX);
+   }
+   
+   /**
+    * Check if the fqn is big enough to be in the internal pojo area but
+    * isn't in the regular attribute area.
+    * 
+    * Structure in the cache is:
+    * 
+    * /JSESSION
+    * ++ /hostname
+    * ++++ /contextpath
+    * ++++++ /sessionid
+    * ++++++++ /ATTRIBUTE
+    * ++++++++ /_JBossInternal_
+    * ++++++++++ etc etc
+    * 
+    * If the Fqn size is big enough to be "etc etc" or lower, but the 4th
+    * level is "ATTRIBUTE", it must be under _JBossInternal_. We discriminate
+    * based on != ATTRIBUTE to avoid having to code to the internal PojoCache
+    * _JBossInternal_ name.
+    * 
+    * @param fqn
+    * @return
+    */
+   public static boolean isPossibleInternalPojoFqn(Fqn fqn)
+   {      
+      return (fqn.size() > POJO_INTERNAL_FQN_SIZE 
+            && JBossCacheService.ATTRIBUTE.equals(fqn.get(POJO_INTERNAL_FQN_INDEX)) == false);
+   }
+}

Added: trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/distributedcache/impl/jbc2/CacheListenerBase.java
===================================================================
--- trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/distributedcache/impl/jbc2/CacheListenerBase.java	                        (rev 0)
+++ trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/distributedcache/impl/jbc2/CacheListenerBase.java	2008-08-26 16:41:17 UTC (rev 77496)
@@ -0,0 +1,105 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2008, Red Hat Middleware LLC, 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.web.tomcat.service.session.distributedcache.impl.jbc2;
+
+import org.jboss.cache.Fqn;
+import org.jboss.cache.buddyreplication.BuddyManager;
+import org.jboss.web.tomcat.service.session.distributedcache.spi.LocalDistributableSessionManager;
+
+/**
+ * Base class for JBC cache listener impls.
+ * 
+ * @author Brian Stansberry
+ */
+public class CacheListenerBase
+{
+
+   private static final int JSESSION_FQN_INDEX = 0;
+   private static final int HOSTNAME_FQN_INDEX = 1;
+   private static final int WEBAPP_FQN_INDEX = 2;
+   protected static final int SESSION_ID_FQN_INDEX = 3;
+   private static final int SESSION_FQN_SIZE = SESSION_ID_FQN_INDEX + 1;
+   private static final int BUDDY_BACKUP_ROOT_OWNER_INDEX = BuddyManager.BUDDY_BACKUP_SUBTREE_FQN.size();
+   protected static final int BUDDY_BACKUP_ROOT_OWNER_SIZE = BUDDY_BACKUP_ROOT_OWNER_INDEX + 1;
+   protected LocalDistributableSessionManager manager_;
+   private String webapp_;
+   private String hostname_;
+
+   CacheListenerBase(LocalDistributableSessionManager manager, String hostname, String webapp)
+   {      
+      manager_ = manager;
+      hostname_ = hostname;
+      webapp_ =  webapp;
+   }
+
+   protected boolean isFqnForOurWebapp(Fqn fqn, boolean isBuddy)
+   {
+      try
+      {
+         if (webapp_.equals(fqn.get(isBuddy ? BUDDY_BACKUP_ROOT_OWNER_SIZE + WEBAPP_FQN_INDEX : WEBAPP_FQN_INDEX))
+               && hostname_.equals(fqn.get(isBuddy ? BUDDY_BACKUP_ROOT_OWNER_SIZE + HOSTNAME_FQN_INDEX : HOSTNAME_FQN_INDEX))
+               && JBossCacheService.SESSION.equals(fqn.get(isBuddy ? BUDDY_BACKUP_ROOT_OWNER_SIZE + JSESSION_FQN_INDEX : JSESSION_FQN_INDEX)))
+            return true;
+      }
+      catch (IndexOutOfBoundsException e)
+      {
+         // can't be ours; too small; just fall through
+      }
+   
+      return false;
+   }
+
+   public static boolean isFqnSessionRootSized(Fqn fqn, boolean isBuddy)
+   {
+      return fqn.size() == (isBuddy ? BUDDY_BACKUP_ROOT_OWNER_SIZE + SESSION_FQN_SIZE : SESSION_FQN_SIZE);
+   }
+
+   public static String getIdFromFqn(Fqn fqn, boolean isBuddy)
+   {
+      return (String)fqn.get(isBuddy ? BUDDY_BACKUP_ROOT_OWNER_SIZE + SESSION_ID_FQN_INDEX : SESSION_ID_FQN_INDEX);
+   }
+
+   public static boolean isBuddyFqn(Fqn fqn)
+   {
+      try
+      {
+         return BuddyManager.BUDDY_BACKUP_SUBTREE.equals(fqn.get(0));
+      }
+      catch (IndexOutOfBoundsException e)
+      {
+         // Can only happen if fqn is ROOT, and we shouldn't get
+         // notifications for ROOT.
+         // If it does, just means it's not a buddy
+         return false;
+      }      
+   }
+
+   /**
+    * Extracts the owner portion of an buddy subtree Fqn.
+    * 
+    * @param fqn An Fqn that is a child of the buddy backup root node.
+    */
+   public static String getBuddyOwner(Fqn fqn)
+   {
+      return (String) fqn.get(BUDDY_BACKUP_ROOT_OWNER_INDEX);     
+   }
+}
\ No newline at end of file

Added: trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/distributedcache/impl/jbc2/FieldBasedJBossCacheService.java
===================================================================
--- trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/distributedcache/impl/jbc2/FieldBasedJBossCacheService.java	                        (rev 0)
+++ trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/distributedcache/impl/jbc2/FieldBasedJBossCacheService.java	2008-08-26 16:41:17 UTC (rev 77496)
@@ -0,0 +1,273 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2008, Red Hat Middleware LLC, 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.web.tomcat.service.session.distributedcache.impl.jbc2;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Set;
+import java.util.StringTokenizer;
+
+import org.jboss.cache.CacheException;
+import org.jboss.cache.Fqn;
+import org.jboss.cache.pojo.PojoCache;
+import org.jboss.ha.framework.server.PojoCacheManager;
+import org.jboss.ha.framework.server.PojoCacheManagerLocator;
+import org.jboss.web.tomcat.service.session.distributedcache.spi.ClusteringNotSupportedException;
+import org.jboss.web.tomcat.service.session.distributedcache.spi.DistributableSession;
+import org.jboss.web.tomcat.service.session.distributedcache.spi.FieldBasedDistributedCacheManager;
+
+/**
+ * Adds PojoCache-specific capabilities to JBossCacheService.
+ * 
+ * @author Brian Stansberry
+ *
+ */
+public class FieldBasedJBossCacheService extends JBossCacheService implements FieldBasedDistributedCacheManager
+{
+   private final PojoCache pojoCache_;
+   
+   public FieldBasedJBossCacheService(String cacheConfigName) throws ClusteringNotSupportedException
+   {
+      this(Util.findPojoCache(cacheConfigName));
+   }
+   
+   /**
+    * Create a new FieldBasedJBossCacheService.
+    * 
+    * @param cache
+    */
+   public FieldBasedJBossCacheService(PojoCache cache)
+   {
+      super(cache.getCache());
+      this.pojoCache_ = cache;
+//      setCache(pojoCache_.getCache());
+      if (!isMarshallingAvailable())
+      {
+         // BES 16/8/2006 -- throw ISE, not ClusteringNotSupportedException, as a
+         // misconfig should be treated differently from the absence of clustering 
+         // services
+         throw new IllegalStateException("replication-granularity value is set to " +
+               "'FIELD' but is not supported by the cache service configuration. " +
+               "Must set 'useRegionBasedMarshalling' to 'true' in the cache configuration");
+      }
+   }
+   
+   private Fqn getFieldFqn(String id, String key)
+   {
+      return getFieldFqn(hostName_, webAppPath_, id, key);
+   }
+
+
+   private static void breakKeys(String key, List list)
+   {
+      StringTokenizer token = new StringTokenizer(key, FQN_DELIMITER);
+      while(token.hasMoreTokens())
+      {
+         list.add(token.nextToken());
+      }
+   }
+   public static Fqn getFieldFqn(String hostName, String contextPath, String sessionId, String attributeKey)
+   {
+      List list = new ArrayList(6);
+      list.add(SESSION);
+      list.add(hostName);
+      list.add(contextPath);
+      list.add(sessionId);
+      list.add(ATTRIBUTE);
+      // Guard against string with delimiter.
+      breakKeys(attributeKey, list);
+      return new Fqn(list);      
+   }
+
+   /**
+    * store the pojo instance in the cache. Note that this is for the aop cache.
+    * THe pojo needs to be "aspectized".
+    * 
+    * @param realId the session id with any jvmRoute removed
+    * @param key    the attribute key
+    * @param pojo
+    * @param createRegion TODO
+    */
+   public Object setPojo(String realId, String key, Object pojo, DistributableSession session)
+   {
+      if(log_.isTraceEnabled())
+      {
+         log_.trace("setPojo(): session id: " + realId + " key: " + key + 
+                    " object: " + pojo.toString());
+      }      
+      
+      if (session.needRegionForSession())
+      {
+         Fqn sessionRegion = getSessionFqn(realId);
+         getCache().getRegion(sessionRegion, true);
+         session.createdRegionForSession();
+         if (log_.isTraceEnabled())
+         {
+            log_.trace("Created region for session at " + sessionRegion);
+         }         
+      }
+      
+      // Construct the fqn.
+      Fqn fqn = getFieldFqn(realId, key);
+      try {
+         // Ignore any cache notifications that our own work generates 
+//         SessionReplicationContext.startCacheActivity();            
+         return pojoCache_.attach(fqn.toString(), pojo);
+      } catch (CacheException e) {
+         throw new RuntimeException("JBossCacheService: exception occurred in cache setPojo ... ", e);
+      }
+      finally {
+//         SessionReplicationContext.finishCacheActivity();
+      }
+   }
+
+   /**
+    * Remove pojo from the underlying cache store.
+    * @param realId the session id with any jvmRoute removed
+    * @param key    the attribute key
+    * @return pojo that just removed. Null if there none.
+    */
+   public Object removePojo(String realId, String key)
+   {
+      if(log_.isTraceEnabled())
+      {
+         log_.trace("removePojo(): session id: " +realId + " key: " +key);
+      }
+      // Construct the fqn.
+      Fqn fqn = getFieldFqn(realId, key);
+      try {
+         // Ignore any cache notifications that our own work generates 
+//         SessionReplicationContext.startCacheActivity();
+         return pojoCache_.detach(fqn.toString());
+      } catch (CacheException e) {
+         throw new RuntimeException("JBossCacheService: exception occurred in cache removePojo ... ", e);
+      }
+      finally {
+//         SessionReplicationContext.finishCacheActivity();
+      }
+   }
+
+   /**
+    * Remove all the pojos from the underlying cache store locally 
+    * without replication.
+    * 
+    * @param realId the session id with any jvmRoute removed
+    */
+   public void removePojosLocal(String realId)
+   {
+      if(log_.isTraceEnabled())
+      {
+         log_.trace("removePojoLocal(): session id: " +realId);
+      }
+      // Construct the fqn.
+      Fqn fqn = getAttributeFqn(realId);
+      try {
+         // Ignore any cache notifications that our own work generates 
+//         SessionReplicationContext.startCacheActivity();
+         cacheWrapper_.removeLocal(fqn);
+      }
+      finally {
+//         SessionReplicationContext.finishCacheActivity();
+      }
+   }
+
+   /**
+    * Remove all the pojos from the underlying cache store locally 
+    * without replication.
+    * 
+    * @param realId the session id with any jvmRoute removed
+    */
+   public void removePojoLocal(String realId, String key)
+   {
+      if(log_.isTraceEnabled())
+      {
+         log_.trace("removePojoLocal(): session id: " + realId + " key: " +key);
+      }
+      // Construct the fqn.
+      Fqn fqn = getFieldFqn(realId, key);
+      try {
+         // Ignore any cache notifications that our own work generates 
+//         SessionReplicationContext.startCacheActivity();
+         cacheWrapper_.removeLocal(fqn);
+      }
+      finally {
+//         SessionReplicationContext.finishCacheActivity();
+      }
+   }
+   
+   public Set getPojoKeys(String realId)
+   {
+      Set keys = null;
+      Fqn fqn = getAttributeFqn(realId);
+      try
+      {
+         keys = getChildrenNames(fqn);
+      }
+      catch (CacheException e)
+      {
+         log_.error("getPojoKeys(): Exception getting keys for session " + realId, e);
+      }
+      
+      return keys;      
+   }
+   
+
+   /**
+    *
+    * @param realId the session id with any jvmRoute removed
+    * @param key    the attribute key
+    * @return Pojo that is associated with the attribute
+    */
+   public Object getPojo(String realId, String key)
+   {
+      Fqn fqn = getFieldFqn(realId, key);
+      if(log_.isTraceEnabled())
+      {
+         log_.trace("getPojo(): session id: " +realId + " key: " + key +
+                    " fqn: " + fqn);
+      }
+      
+      try 
+      {
+         return pojoCache_.find(fqn);
+      }
+      catch (CacheException e) 
+      {
+         throw new RuntimeException("JBossCacheService: exception occurred in cache find ... ", e);
+      }
+   }
+   
+   protected void releaseCacheToManager(String cacheConfigName)
+   {      
+      try
+      {
+         PojoCacheManager pcm = PojoCacheManagerLocator.getCacheManagerLocator().getCacheManager(null);
+         pcm.releaseCache(cacheConfigName);
+      }
+      catch (Exception e)
+      {
+         log_.error("Problem releasing cache to CacheManager -- config is " + cacheConfigName, e);
+      }
+   }
+
+}

Added: trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/distributedcache/impl/jbc2/JBossCacheService.java
===================================================================
--- trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/distributedcache/impl/jbc2/JBossCacheService.java	                        (rev 0)
+++ trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/distributedcache/impl/jbc2/JBossCacheService.java	2008-08-26 16:41:17 UTC (rev 77496)
@@ -0,0 +1,764 @@
+/*
+* JBoss, Home of Professional Open Source
+* Copyright 2005, JBoss Inc., and individual contributors as indicated
+* by the @authors tag. See the copyright.txt 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.web.tomcat.service.session.distributedcache.impl.jbc2;
+
+import java.io.IOException;
+import java.io.Serializable;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Set;
+import java.util.Map.Entry;
+
+import javax.transaction.TransactionManager;
+
+import org.jboss.cache.Cache;
+import org.jboss.cache.CacheException;
+import org.jboss.cache.CacheManager;
+import org.jboss.cache.CacheStatus;
+import org.jboss.cache.Fqn;
+import org.jboss.cache.Node;
+import org.jboss.cache.Region;
+import org.jboss.cache.buddyreplication.BuddyManager;
+import org.jboss.cache.config.BuddyReplicationConfig;
+import org.jboss.cache.config.CacheLoaderConfig;
+import org.jboss.cache.pojo.impl.InternalConstant;
+import org.jboss.cache.transaction.BatchModeTransactionManager;
+import org.jboss.ha.framework.interfaces.CachableMarshalledValue;
+import org.jboss.ha.framework.server.CacheManagerLocator;
+import org.jboss.ha.framework.server.MarshalledValueHelper;
+import org.jboss.ha.framework.server.SimpleCachableMarshalledValue;
+import org.jboss.logging.Logger;
+import org.jboss.web.tomcat.service.session.distributedcache.spi.BatchingManager;
+import org.jboss.web.tomcat.service.session.distributedcache.spi.ClusteringNotSupportedException;
+import org.jboss.web.tomcat.service.session.distributedcache.spi.DistributableSession;
+import org.jboss.web.tomcat.service.session.distributedcache.spi.DistributableSessionMetadata;
+import org.jboss.web.tomcat.service.session.distributedcache.spi.DistributableSessionTimestamp;
+import org.jboss.web.tomcat.service.session.distributedcache.spi.DistributedCacheManager;
+import org.jboss.web.tomcat.service.session.distributedcache.spi.LocalDistributableSessionManager;
+import org.jboss.web.tomcat.service.session.distributedcache.spi.SessionSerializationFactory;
+
+/**
+ * A wrapper class to JBossCache. This is currently needed to handle various operations such as
+ * <ul>
+ * <li>Using MarshalledValue to replace Serializable used inside different web app class loader context.</li>
+ * <li>Stripping out any id string after ".". This is to handle the JK failover properly with
+ * Tomcat JvmRoute.</li>
+ * <li>Cache exception retry.</li>
+ * <li>Helper APIS.</li>
+ * </ul>
+ */
+public class JBossCacheService implements DistributedCacheManager
+{   
+   protected static Logger log_ = Logger.getLogger(JBossCacheService.class);
+   public static final String BUDDY_BACKUP = BuddyManager.BUDDY_BACKUP_SUBTREE;
+   public static final Fqn BUDDY_BACKUP_FQN = BuddyManager.BUDDY_BACKUP_SUBTREE_FQN;
+   public static final String SESSION = "JSESSION";
+   public static final String ATTRIBUTE = "ATTRIBUTE";
+   public static final String VERSION_KEY = "V";
+   public static final String TIMESTAMP_KEY = "T";
+   public static final String METADATA_KEY = "M";
+   public static final String ATTRIBUTE_KEY = "A";
+      
+   public static final String FQN_DELIMITER = "/";
+   
+   private Cache plainCache_;
+   
+   /** name of webapp's virtual host; hostName + webAppPath + session id is a unique combo. */
+   protected String hostName_;
+   /** Context path for webapp; hostName + webAppPath + session id is a unique combo. */
+   protected String webAppPath_;
+   protected BatchingManager batchingManager;
+
+   private LocalDistributableSessionManager manager_;
+   private ClassLoader webAppClassLoader_;
+   private CacheListener cacheListener_;
+   protected JBossCacheWrapper cacheWrapper_;
+   
+   /** Do we have to marshall attributes ourself or can we let JBC do it? */
+   private boolean useTreeCacheMarshalling_ = false;
+   
+   /** Are we configured for passivation? */
+   private boolean usePassivation_ = false;
+   private PassivationListener passivationListener_;
+   
+   /** Is cache configured for buddy replication? */
+   private boolean useBuddyReplication_ = false;
+   
+   private String cacheConfigName_;;
+   
+   public JBossCacheService(String cacheConfigName) throws ClusteringNotSupportedException
+   {
+      this(Util.findPlainCache(cacheConfigName));
+      
+      this.cacheConfigName_ = cacheConfigName;
+   }
+   
+   public JBossCacheService(Cache cache)
+   {
+      plainCache_ = cache;
+      
+      cacheWrapper_ = new JBossCacheWrapper(plainCache_);
+      
+      useTreeCacheMarshalling_ = plainCache_.getConfiguration().isUseRegionBasedMarshalling();
+      CacheLoaderConfig clc = plainCache_.getConfiguration().getCacheLoaderConfig();
+      if(clc != null)
+      {
+         usePassivation_ = (clc.isPassivation() && !clc.isShared());
+      }
+   }
+   
+   protected LocalDistributableSessionManager getManager()
+   {
+      return manager_;
+   }
+   
+   protected Cache getCache()
+   {
+      return plainCache_;
+   }
+   
+   protected void setCache(Cache cache)
+   {
+      this.plainCache_ = cache;
+   }
+
+   public void start(ClassLoader tcl, LocalDistributableSessionManager manager)
+   {
+      manager_ = manager;
+      webAppClassLoader_ = tcl;
+      
+      String path = manager_.getContextName();
+      if( path.length() == 0 || path.equals("/")) {
+         // If this is root.
+         webAppPath_ = "ROOT";
+      } else if ( path.startsWith("/") ) {
+         webAppPath_ = path.substring(1);
+      } else {
+         webAppPath_ = path;
+      }
+      // JBAS-3941 -- context path can be multi-level, but we don't
+      // want that turning into a multilevel Fqn, so escape it
+      // Use '?' which is illegal in a context path
+      webAppPath_ = webAppPath_.replace('/', '?');
+      log_.debug("Old and new web app path are: " +path + ", " +webAppPath_);
+      
+      String host = manager_.getHostName();
+      if( host == null || host.length() == 0) {
+         hostName_ = "localhost";
+      }else {
+         hostName_ = host;
+      }
+      log_.debug("Old and new virtual host name are: " + host + ", " + hostName_);
+
+      if (plainCache_.getCacheStatus() != CacheStatus.STARTED)
+      {
+         plainCache_.start();
+      }
+
+      // We require the cache batchingManager to be BatchModeTransactionManager now.
+      TransactionManager tm = plainCache_.getConfiguration().getRuntimeConfig().getTransactionManager();
+      if( ! (tm instanceof BatchModeTransactionManager) )
+      {
+         throw new RuntimeException("start(): JBoss Cache transaction manager " +
+                                    "is not of type BatchModeTransactionManager. " +
+                                    "It is " + (tm == null ? "null" : tm.getClass().getName()));
+      }
+      this.batchingManager = new BatchingManagerImpl(tm);
+      
+      Object[] objs = new Object[]{SESSION, hostName_, webAppPath_};
+      Fqn pathFqn = Fqn.fromList(Arrays.asList(objs), true);
+      
+      BuddyReplicationConfig brc = plainCache_.getConfiguration().getBuddyReplicationConfig();
+      this.useBuddyReplication_ = brc != null && brc.isEnabled();
+      if (useTreeCacheMarshalling_ || this.useBuddyReplication_)
+      {
+         // JBAS-5628/JBAS-5629 -- clean out persistent store
+         cleanWebappRegion(pathFqn);
+      }
+
+      // Listen for cache changes
+      cacheListener_ = new CacheListener(cacheWrapper_, manager_, hostName_, webAppPath_);
+      plainCache_.addCacheListener(cacheListener_);
+      
+      if(useTreeCacheMarshalling_)
+      {
+         // register the tcl and bring over the state for the webapp
+         try
+         {
+            
+            log_.debug("UseMarshalling is true. We will register the fqn: " +
+                        pathFqn + " with class loader" +webAppClassLoader_ +
+                        " and activate the webapp's Region");
+            Node root = plainCache_.getRoot();
+            if (root.hasChild(pathFqn) == false)
+            {
+               plainCache_.getInvocationContext().getOptionOverrides().setCacheModeLocal(true);
+               root.addChild(pathFqn);
+            }
+            Region region = plainCache_.getRegion(pathFqn, true);
+            region.registerContextClassLoader(webAppClassLoader_);
+            region.activate(); 
+         }
+         catch (Exception ex)
+         {
+            throw new RuntimeException("Can't register class loader", ex);
+         }
+      }
+      
+      if(manager_.isPassivationEnabled())
+      {
+         log_.debug("Passivation is enabled");
+         passivationListener_ = new PassivationListener(manager_, hostName_, webAppPath_);
+         plainCache_.addCacheListener(passivationListener_);
+      }
+      else
+      {
+         log_.debug("Passivation is disabled");
+      }
+   }
+
+   private void cleanWebappRegion(Fqn regionFqn)
+   {
+      try {
+         // Remove locally.
+         plainCache_.getInvocationContext().getOptionOverrides().setCacheModeLocal(true);
+         plainCache_.removeNode(regionFqn);
+      }
+      catch (CacheException e)
+      {
+         log_.error("can't clean content from the underlying distributed cache");
+      }
+   }
+
+   public void stop()
+   {
+      plainCache_.removeCacheListener(cacheListener_);      
+      if (passivationListener_ != null)
+         plainCache_.removeCacheListener(passivationListener_);
+      
+      // Construct the fqn
+      Object[] objs = new Object[]{SESSION, hostName_, webAppPath_};
+      Fqn pathFqn = Fqn.fromList(Arrays.asList(objs), true);
+
+      if(useTreeCacheMarshalling_)
+      {
+         log_.debug("UseMarshalling is true. We will inactivate the fqn: " +
+                    pathFqn + " and un-register its classloader");
+            
+         try {
+            Region region = plainCache_.getRegion(pathFqn, false);
+            if (region != null)
+            {
+               region.deactivate();
+               region.unregisterContextClassLoader();
+            }
+         } 
+         catch (Exception e) 
+         {
+            log_.error("Exception during inactivation of webapp region " + pathFqn + 
+                       " or un-registration of its class loader", e);
+         }
+      } 
+      BuddyReplicationConfig brc = plainCache_.getConfiguration().getBuddyReplicationConfig();
+      this.useBuddyReplication_ = brc != null && brc.isEnabled();
+      if (useTreeCacheMarshalling_ || this.useBuddyReplication_)
+      {
+         // JBAS-5628/JBAS-5629 -- clean out persistent store
+         cleanWebappRegion(pathFqn);
+      }
+      // remove session data
+      // BES 2007/08/18 Can't do this as it will 
+      // 1) blow away passivated sessions
+      // 2) leave the cache in an inconsistent state if the war
+      //    is restarted
+//      cacheWrapper_.removeLocalSubtree(pathFqn);
+      
+      if (cacheConfigName_ != null)
+      {
+         releaseCacheToManager(cacheConfigName_);
+      }
+   }
+
+   /**
+    * Get specfically the BatchModeTransactionManager.
+    */
+   public BatchingManager getBatchingManager()
+   {
+      return batchingManager;
+   }
+   
+   /**
+    * Gets whether TreeCache-based marshalling is available
+    */
+   public boolean isMarshallingAvailable()
+   {
+      return useTreeCacheMarshalling_;
+   }
+
+   /**
+    * Loads any serialized data in the cache into the given session
+    * using its <code>readExternal</code> method.
+    *
+    * @return the session passed as <code>toLoad</code>, or
+    *         <code>null</code> if the cache had no data stored
+    *         under the given session id.
+    */
+   public <T extends DistributableSession> T loadSession(String realId, T toLoad)
+   {
+      Fqn fqn = getSessionFqn(realId);
+      Map sessionData =  cacheWrapper_.getData(fqn, true);
+      
+      if (sessionData == null) {
+         // Requested session is no longer in the cache; return null
+         return null;
+      }
+      
+      setupSessionRegion(toLoad, fqn);
+      
+      Integer version = (Integer) sessionData.get(VERSION_KEY);
+      DistributableSessionTimestamp timestamp = (DistributableSessionTimestamp) sessionData.get(TIMESTAMP_KEY);
+      DistributableSessionMetadata metadata = (DistributableSessionMetadata) sessionData.get(METADATA_KEY);
+      Map attrs = (Map) getUnMarshalledValue(sessionData.get(ATTRIBUTE_KEY));
+      toLoad.update(version, timestamp, metadata, attrs);
+      
+      return toLoad;
+   }
+
+   public void putSession(String realId, DistributableSession session)
+   { 
+      if (log_.isTraceEnabled())
+      {
+         log_.trace("putSession(): putting session " + session.getIdInternal());
+      }     
+      
+      Fqn fqn = getSessionFqn(realId);
+      
+      setupSessionRegion(session, fqn);
+      
+      Map map = new HashMap();
+      map.put(VERSION_KEY, new Integer(session.getVersion()));
+      
+      boolean replicateTimestamp = false;
+      
+      if (session.isSessionMetadataDirty())
+      {   
+         map.put(METADATA_KEY, session.getSessionMetadata()); 
+         replicateTimestamp = true;
+      }
+     
+      if (session.isSessionAttributeMapDirty())
+      {
+         Map attrs = session.getSessionAttributeMap();
+         // May be null if the session type doesn't use this mechanism to store
+         // attributes (i.e. isn't SessionBasedClusteredSession)
+         if (attrs != null)
+         {
+            map.put(ATTRIBUTE_KEY, getMarshalledValue(attrs));
+         } 
+         
+         replicateTimestamp = true;
+      }
+      
+      if (replicateTimestamp || session.getMustReplicateTimestamp())
+      {
+         map.put(TIMESTAMP_KEY, session.getSessionTimestamp());
+      }
+      
+      cacheWrapper_.put(fqn, map);
+   }
+
+   /**
+    * If the session requires a region in the cache, establishes one.
+    * 
+    * @param session the session
+    * @param fqn the fqn for the session
+    */
+   private void setupSessionRegion(DistributableSession session, Fqn fqn)
+   {
+      if (session.needRegionForSession())
+      {
+         plainCache_.getRegion(fqn, true);
+         session.createdRegionForSession();
+         if (log_.isTraceEnabled())
+         {
+            log_.trace("Created region for session at " + fqn);
+         }
+      }
+   }
+
+   public void removeSession(String realId, boolean removeRegion)
+   {
+      Fqn fqn = getSessionFqn(realId);
+      if (log_.isTraceEnabled())
+      {
+         log_.trace("Remove session from distributed store. Fqn: " + fqn);
+      }
+      
+      if (removeRegion)
+      {
+         plainCache_.removeRegion(fqn);
+      }
+
+      cacheWrapper_.remove(fqn);
+   }
+
+   public void removeSessionLocal(String realId, boolean removeRegion)
+   {
+      Fqn fqn = getSessionFqn(realId);
+      if (log_.isTraceEnabled())
+      {
+         log_.trace("Remove session from my own distributed store only. Fqn: " + fqn);
+      }
+      
+      if (removeRegion)
+      {
+         plainCache_.removeRegion(fqn);
+      }
+      
+      cacheWrapper_.removeLocal(fqn);
+   }
+
+   public void removeSessionLocal(String realId, String dataOwner)
+   {
+      if (dataOwner == null)
+      {
+         removeSessionLocal(realId, false);
+      }
+      else
+      {         
+         Fqn fqn = getSessionFqn(realId, dataOwner);
+         if (log_.isTraceEnabled())
+         {
+            log_.trace("Remove session from my own distributed store only. Fqn: " + fqn);
+         }
+         cacheWrapper_.removeLocal(fqn);
+      }
+   }   
+      
+   public void evictSession(String realId)
+   {
+      evictSession(realId, null);      
+   }   
+      
+   public void evictSession(String realId, String dataOwner)
+   {    
+      Fqn fqn = dataOwner == null ? getSessionFqn(realId) : getSessionFqn(realId, dataOwner);
+      if(log_.isTraceEnabled())
+      {
+         log_.trace("evictSession(): evicting session from my distributed store. Fqn: " + fqn);
+      }
+      cacheWrapper_.evictSubtree(fqn);      
+   }
+   
+   public Map getSessionData(String realId, String dataOwner)
+   {
+      Fqn fqn = dataOwner == null ? getSessionFqn(realId) : getSessionFqn(realId, dataOwner);
+      return cacheWrapper_.getData(fqn, false);
+   }
+
+   public Object getAttribute(String realId, String key)
+   {
+      Fqn fqn = getAttributeFqn(realId);
+      return getUnMarshalledValue(cacheWrapper_.get(fqn, key));
+   }
+
+   public void putAttribute(String realId, String key, Object value)
+   {
+      Fqn fqn = getAttributeFqn(realId);
+      cacheWrapper_.put(fqn, key, getMarshalledValue(value));
+   }
+
+   public void putAttribute(String realId, Map map)
+   {
+      // Duplicate the map with marshalled values
+      Map marshalled = new HashMap(map.size());
+      Set entries = map.entrySet();
+      for (Iterator it = entries.iterator(); it.hasNext(); )
+      {
+         Map.Entry entry = (Map.Entry) it.next();
+         marshalled.put(entry.getKey(), getMarshalledValue(entry.getValue()));
+      }
+      
+      Fqn fqn = getAttributeFqn(realId);
+      cacheWrapper_.put(fqn, marshalled);
+      
+   }
+
+   public void removeAttributes(String realId)
+   {
+      Fqn fqn = getAttributeFqn(realId);
+      cacheWrapper_.remove(fqn);
+   }
+
+   public Object removeAttribute(String realId, String key)
+   {
+      Fqn fqn = getAttributeFqn(realId);
+      if (log_.isTraceEnabled())
+      {
+         log_.trace("Remove attribute from distributed store. Fqn: " + fqn + " key: " + key);
+      }
+      return getUnMarshalledValue(cacheWrapper_.remove(fqn, key));
+   }
+
+   public void removeAttributesLocal(String realId)
+   {
+      Fqn fqn = getAttributeFqn(realId);
+      if (log_.isTraceEnabled())
+      {
+         log_.trace("Remove attributes from my own distributed store only. Fqn: " + fqn);
+      }
+      cacheWrapper_.removeLocal(fqn);
+   }
+
+   /**
+    * Obtain the keys associated with this fqn. Note that it is not the fqn children.
+    *
+    */
+   public Set getAttributeKeys(String realId)
+   {
+      Set keys = null;
+      Fqn fqn = getAttributeFqn(realId);
+      try
+      {
+         Node node = plainCache_.getRoot().getChild(fqn);
+         if (node != null)
+            keys = node.getKeys();
+      }
+      catch (CacheException e)
+      {
+         log_.error("getAttributeKeys(): Exception getting keys for session " + realId, e);
+      }
+      
+      return keys;
+   }
+
+   /**
+    * Return all attributes associated with this session id.
+    * 
+    * @param realId the session id with any jvmRoute removed
+    * @return the attributes, or any empty Map if none are found.
+    */
+   public Map getAttributes(String realId)
+   {
+      if (realId == null || realId.length() == 0) return new HashMap();
+      
+      Map attrs = new HashMap();
+      Fqn fqn = getAttributeFqn(realId);
+      
+      Node node = plainCache_.getRoot().getChild(fqn);
+      Map rawData = node.getData();
+      
+      for (Iterator it = rawData.entrySet().iterator(); it.hasNext();)
+      {
+         Entry entry = (Entry) it.next();
+         attrs.put(entry.getKey(), getUnMarshalledValue(entry.getValue()));
+      }
+      
+      return attrs;
+   }
+
+   /**
+    * Gets the ids of all sessions in the underlying cache.
+    *
+    * @return Map<String, String> containing all of the session ids of sessions in the cache
+    *         (with any jvmRoute removed) as keys, and the identifier of the data owner for
+    *         the session as value (or a <code>null</code>  value if buddy
+    *         replication is not enabled.) Will not return <code>null</code>.
+    */
+   public Map<String, String> getSessionIds() throws CacheException
+   {
+      Map<String, String> result = new HashMap<String, String>();
+      
+      Node bbRoot = plainCache_.getRoot().getChild(BUDDY_BACKUP_FQN);
+      if (bbRoot != null)
+      {
+         Set owners = bbRoot.getChildren();
+         if (owners != null)
+         {
+            for (Iterator it = owners.iterator(); it.hasNext();)
+            {
+               Node owner = (Node) it.next();
+               Node webRoot = owner.getChild(getWebappFqn());
+               if (webRoot != null)
+               {
+                  Set<String> ids = webRoot.getChildrenNames();
+                  storeSessionOwners(ids, (String) owner.getFqn().getLastElement(), result);
+               }
+            }
+         }
+      }
+      
+      storeSessionOwners(getChildrenNames(getWebappFqn()), null, result);
+
+      return result;
+   }
+   
+   protected Set getChildrenNames(Fqn fqn)
+   {
+      Node node = plainCache_.getRoot().getChild(fqn);
+      return (node == null ? null : node.getChildrenNames());
+   }
+
+   private void storeSessionOwners(Set<String> ids, String owner, Map<String, String> map)
+   {
+      if (ids != null)
+      {
+         for (String id : ids)
+         {            
+            if (!InternalConstant.JBOSS_INTERNAL_STRING.equals(id))
+            {
+               map.put(id, owner);
+            }
+         }
+      }
+   }
+ 
+   public boolean isPassivationEnabled()
+   {
+      return usePassivation_;      
+   }
+
+   protected Fqn getWebappFqn()
+   {
+      // /SESSION/hostname/webAppPath
+      Object[] objs = new Object[]{SESSION, hostName_, webAppPath_};
+      return Fqn.fromList(Arrays.asList(objs), true);
+   }
+   
+   protected Fqn getSessionFqn(String id)
+   {
+      return getSessionFqn(hostName_, webAppPath_, id);
+   }
+   
+   public static Fqn getSessionFqn(String hostname, String contextPath, String sessionId)
+   {
+      Object[] objs = new Object[]{SESSION, hostname, contextPath, sessionId};
+      return Fqn.fromList(Arrays.asList(objs), true);
+   }
+
+   private Fqn getSessionFqn(String id, String dataOwner)
+   {
+      return getSessionFqn(dataOwner, hostName_, webAppPath_, id);
+   }
+   
+   public static Fqn getSessionFqn(String dataOwner, String hostname, String contextPath, String sessionId)
+   {
+      Object[] objs = new Object[]{BUDDY_BACKUP, dataOwner, SESSION, hostname, contextPath, sessionId};
+      return Fqn.fromList(Arrays.asList(objs), true);
+   }
+
+   protected Fqn getAttributeFqn(String id)
+   {
+      return getAttributeFqn(hostName_, webAppPath_, id);
+   }
+
+   public static Fqn getAttributeFqn(String hostname, String contextPath, String sessionId)
+   {
+      Object[] objs = new Object[]{SESSION, hostname, contextPath, sessionId, ATTRIBUTE};
+      return Fqn.fromList(Arrays.asList(objs), true);
+   }
+   
+   protected void releaseCacheToManager(String cacheConfigName)
+   {      
+      try
+      {
+         CacheManager cm = CacheManagerLocator.getCacheManagerLocator().getCacheManager(null);
+         cm.releaseCache(cacheConfigName);
+      }
+      catch (Exception e)
+      {
+         log_.error("Problem releasing cache to CacheManager -- config is " + cacheConfigName, e);
+      }
+   }
+
+   private Object getMarshalledValue(Object value)
+   {
+      // JBAS-2920.  For now, continue using MarshalledValue, as 
+      // it allows lazy deserialization of the attribute on remote nodes
+      // For Branch_4_0 this is what we have to do anyway for backwards
+      // compatibility. For HEAD we'll follow suit for now.
+      // TODO consider only using MV for complex objects (i.e. not primitives)
+      // and Strings longer than X.
+      
+//      if (useTreeCacheMarshalling_)
+//      {
+//         return value;
+//      }
+//      else
+//      {
+         
+         // JBAS-2921 - replaced MarshalledValue calls with SessionSerializationFactory calls
+         // to allow for switching between JBossSerialization and JavaSerialization using
+         // system property -D=session.serialization.jboss=true / false
+         // MarshalledValue mv = new MarshalledValue(value);
+         if  (MarshalledValueHelper.isTypeExcluded(value.getClass()))
+         {
+            return value;               
+         }
+         else 
+         {
+            try
+            {
+               CachableMarshalledValue mv = SessionSerializationFactory.createMarshalledValue((Serializable) value);
+               return mv;
+            }
+            catch (ClassCastException e)
+            {
+               throw new IllegalArgumentException(value + " does not implement java.io.Serializable");
+            }
+         }
+//      }
+   }
+
+   private Object getUnMarshalledValue(Object obj)
+   {
+      if (!(obj instanceof SimpleCachableMarshalledValue))
+            return obj;
+         
+      // Swap in/out the tcl for this web app. Needed only for un marshalling.
+      ClassLoader prevTCL = Thread.currentThread().getContextClassLoader();
+      Thread.currentThread().setContextClassLoader(webAppClassLoader_);
+      try
+      {
+         SimpleCachableMarshalledValue mv = (SimpleCachableMarshalledValue) obj;
+         mv.setObjectStreamSource(SessionSerializationFactory.getObjectStreamSource());
+         return mv.get();
+      }
+      catch (IOException e)
+      {
+         log_.error("IOException occurred unmarshalling value ", e);
+         return null;
+      }
+      catch (ClassNotFoundException e)
+      {
+         log_.error("ClassNotFoundException occurred unmarshalling value ", e);
+         return null;
+      }
+      finally
+      {
+         Thread.currentThread().setContextClassLoader(prevTCL);
+      }
+   }
+
+}

Added: trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/distributedcache/impl/jbc2/JBossCacheWrapper.java
===================================================================
--- trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/distributedcache/impl/jbc2/JBossCacheWrapper.java	                        (rev 0)
+++ trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/distributedcache/impl/jbc2/JBossCacheWrapper.java	2008-08-26 16:41:17 UTC (rev 77496)
@@ -0,0 +1,255 @@
+/*
+* JBoss, Home of Professional Open Source
+* Copyright 2005, JBoss Inc., and individual contributors as indicated
+* by the @authors tag. See the copyright.txt 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.web.tomcat.service.session.distributedcache.impl.jbc2;
+
+import java.util.Map;
+
+import org.jboss.cache.Cache;
+import org.jboss.cache.Fqn;
+import org.jboss.cache.lock.TimeoutException;
+
+public class JBossCacheWrapper 
+{
+   private static final int RETRY = 3;
+   private static final String RETRY_FAIL_MSG = 
+      "Continued to catch TimeoutException during " + 
+       RETRY + " retry attempts. Giving up.";
+   private Cache plainCache_;
+   
+   JBossCacheWrapper(Cache cache)
+   {
+      plainCache_ = cache;
+   }
+   
+   Map getData(Fqn fqn, boolean gravitate)
+   {
+      TimeoutException ex = null;
+      for (int i = 0; i < RETRY; i++)
+      {
+         try
+         {            
+            if (gravitate)
+            {            
+               plainCache_.getInvocationContext().getOptionOverrides()
+                                                 .setForceDataGravitation(true);
+            }
+            return plainCache_.getData(fqn);
+         }
+         catch (TimeoutException e)
+         {
+            ex = e;
+         }
+      }      
+      throw new RuntimeException(RETRY_FAIL_MSG, ex);      
+   }
+
+   /**
+    * Wrapper to embed retry logic.
+    *
+    * @param fqn
+    * @param id
+    * @return
+    */
+   Object get(Fqn fqn, String id)
+   {
+      return get(fqn, id, false);
+   }
+
+   /**
+    * Wrapper to embed retry logic.
+    *
+    * @param fqn
+    * @param id
+    * @return
+    */
+   Object get(Fqn fqn, String id, boolean gravitate)
+   {
+      TimeoutException ex = null;
+      for (int i = 0; i < RETRY; i++)
+      {
+         try
+         {            
+            if (gravitate)
+            {            
+               plainCache_.getInvocationContext().getOptionOverrides()
+                                                 .setForceDataGravitation(true);
+            }
+            return plainCache_.get(fqn, id);
+         }
+         catch (TimeoutException e)
+         {
+            ex = e;
+         }
+      }
+      
+      throw new RuntimeException(RETRY_FAIL_MSG, ex);
+   }
+
+   /**
+    * Wrapper to embed retry logic.
+    *
+    * @param fqn
+    * @param id
+    * @param value
+    * @return
+    */
+   void put(Fqn fqn, String id, Object value)
+   {
+      TimeoutException ex = null;
+      for (int i = 0; i < RETRY; i++)
+      {
+         try
+         {
+            plainCache_.put(fqn, id, value);
+            return;
+         }
+         catch (TimeoutException e)
+         {
+            ex = e;
+         }
+      }
+      
+      throw new RuntimeException(RETRY_FAIL_MSG, ex);
+   }
+
+
+   /**
+    * Wrapper to embed retry logic.
+    *
+    * @param fqn
+    * @param map
+    */
+   void put(Fqn fqn, Map map)
+   {
+      TimeoutException ex = null;
+      for (int i = 0; i < RETRY; i++)
+      {
+         try
+         {
+            plainCache_.put(fqn, map);
+            return;
+         }
+         catch (TimeoutException e)
+         {
+            ex = e;
+         }
+      }
+      
+      throw new RuntimeException(RETRY_FAIL_MSG, ex);
+   }
+
+   /**
+    * Wrapper to embed retry logic.
+    *
+    * @param fqn
+    * @param id
+    * @return
+    */
+   Object remove(Fqn fqn, String id)
+   {
+      TimeoutException ex = null;
+      for (int i = 0; i < RETRY; i++)
+      {
+         try
+         {
+            return plainCache_.remove(fqn, id);
+         }
+         catch (TimeoutException e)
+         {
+            ex = e;
+         }
+      }
+      
+      throw new RuntimeException(RETRY_FAIL_MSG, ex);
+   }
+
+   /**
+    * Wrapper to embed retry logic.
+    *
+    * @param fqn
+    */
+   void remove(Fqn fqn)
+   {
+      TimeoutException ex = null;
+      for (int i = 0; i < RETRY; i++)
+      {
+         try
+         {
+            plainCache_.removeNode(fqn);
+            return;
+         }
+         catch (TimeoutException e)
+         {
+            ex = e;
+         }
+      }
+      
+      throw new RuntimeException(RETRY_FAIL_MSG, ex);
+   }
+   
+   /**
+    * Wrapper to embed retry logic.
+    *
+    * @param fqn
+    */
+   void removeLocal(Fqn fqn)
+   {
+      TimeoutException ex = null;
+      for (int i = 0; i < RETRY; i++)
+      {
+         try
+         {
+            plainCache_.getInvocationContext().getOptionOverrides()
+                                              .setCacheModeLocal(true);
+            plainCache_.removeNode(fqn);
+            return;
+         }
+         catch (TimeoutException e)
+         {
+            ex = e;
+         }
+      }
+      
+      throw new RuntimeException(RETRY_FAIL_MSG, ex);
+   }
+   
+   void evictSubtree(Fqn fqn)
+   {      
+      TimeoutException ex = null;
+      for (int i = 0; i < RETRY; i++)
+      {
+         try
+         {
+            plainCache_.evict(fqn, true);
+            return;
+            
+         }
+         catch (TimeoutException e)
+         {
+            ex = e;
+         }
+      }
+      
+      throw new RuntimeException(RETRY_FAIL_MSG, ex);
+   }
+
+}

Added: trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/distributedcache/impl/jbc2/PassivationListener.java
===================================================================
--- trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/distributedcache/impl/jbc2/PassivationListener.java	                        (rev 0)
+++ trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/distributedcache/impl/jbc2/PassivationListener.java	2008-08-26 16:41:17 UTC (rev 77496)
@@ -0,0 +1,76 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2006, Red Hat Middleware LLC, 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.web.tomcat.service.session.distributedcache.impl.jbc2;
+
+import org.jboss.cache.Fqn;
+import org.jboss.cache.notifications.annotation.NodeActivated;
+import org.jboss.cache.notifications.event.NodeActivatedEvent;
+import org.jboss.web.tomcat.service.session.distributedcache.spi.LocalDistributableSessionManager;
+
+/**
+ * Listener for JBoss Cache activation events.  Triggers updates of
+ * the passivation counter.
+ * 
+ * @author <a href="brian.stansberry at jboss.com">Brian Stansberry</a>
+ * @version $Revision: 64678 $
+ */
+ at org.jboss.cache.notifications.annotation.CacheListener
+public class PassivationListener extends CacheListenerBase
+{
+
+   PassivationListener(LocalDistributableSessionManager manager, String hostname, String webapp)
+   {      
+      super(manager, hostname, webapp);
+   }
+   
+   // NOTE: Don't track passivation from here -- we know in JBossCacheManager
+   // when we trigger a passivation. Avoid spurious listener callbacks to
+   // webapps that aren't interested.
+   
+//   @NodePassivated
+//   public void nodePassivated(NodePassivatedEvent event)
+//   {
+//      Fqn fqn = event.getFqn();
+//      if (isFqnForOurWebapp(fqn, isBuddyFqn(fqn)))
+//      {
+//         manager_.sessionPassivated();
+//      }
+//   }
+   
+   // We do want activation callbacks, as JBossCacheManager can't readily
+   // track whether a cache read is going to result in an activation
+   
+   @NodeActivated
+   public void nodeActivated(NodeActivatedEvent event)
+   {
+      Fqn fqn = event.getFqn();
+      boolean isBuddy = isBuddyFqn(fqn);      
+      if (isFqnSessionRootSized(fqn, isBuddy) 
+            && isFqnForOurWebapp(fqn, isBuddy))
+      {
+         manager_.sessionActivated();
+      }
+      
+   }
+
+}

Added: trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/distributedcache/impl/jbc2/Util.java
===================================================================
--- trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/distributedcache/impl/jbc2/Util.java	                        (rev 0)
+++ trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/distributedcache/impl/jbc2/Util.java	2008-08-26 16:41:17 UTC (rev 77496)
@@ -0,0 +1,121 @@
+/*
+* JBoss, Home of Professional Open Source
+* Copyright 2006, JBoss Inc., and individual contributors as indicated
+* by the @authors tag. See the copyright.txt 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.web.tomcat.service.session.distributedcache.impl.jbc2;
+
+
+import org.jboss.cache.Cache;
+import org.jboss.cache.CacheManager;
+import org.jboss.cache.pojo.PojoCache;
+import org.jboss.ha.framework.server.CacheManagerLocator;
+import org.jboss.ha.framework.server.PojoCacheManager;
+import org.jboss.ha.framework.server.PojoCacheManagerLocator;
+import org.jboss.web.tomcat.service.session.distributedcache.spi.ClusteringNotSupportedException;
+
+/**
+ * Utility methods related to JBoss distributed sessions.
+ * 
+ * @author Brian Stansberry
+ * @version $Revision: 56542 $
+ */
+public class Util
+{
+   public static PojoCache findPojoCache(String cacheConfigName) throws ClusteringNotSupportedException
+   {
+      PojoCacheManager pcm = getManagerForPojoCache(cacheConfigName);
+      
+      try
+      {
+         return pcm.getPojoCache(cacheConfigName, true);
+      }
+      catch (RuntimeException re)
+      {
+         throw re;
+      }
+      catch (Exception e)
+      {
+         throw new RuntimeException("Problem accessing cache " + cacheConfigName, e);
+      }
+   }
+   
+   public static Cache findPlainCache(String cacheConfigName) throws ClusteringNotSupportedException
+   {
+      CacheManager pcm = getManagerForCache(cacheConfigName);
+      
+      try
+      {
+         return pcm.getCache(cacheConfigName, true);
+      }
+      catch (RuntimeException re)
+      {
+         throw re;
+      }
+      catch (Exception e)
+      {
+         throw new RuntimeException("Problem accessing cache " + cacheConfigName, e);
+      }
+   }
+   
+   private static PojoCacheManager getManagerForPojoCache(String cacheConfigName) 
+      throws ClusteringNotSupportedException
+   {
+      PojoCacheManager pcm = null;
+      try
+      {
+         pcm = PojoCacheManagerLocator.getCacheManagerLocator().getCacheManager(null);
+      }
+      catch (Throwable t)
+      {
+         throw new ClusteringNotSupportedException("Could not access PojoCacheManager for JBossWeb clustering", t);
+      }
+      
+      if (!pcm.getConfigurationNames().contains(cacheConfigName))
+         throw new IllegalStateException("PojoCacheManager does not recognize config " + cacheConfigName);
+      
+      return pcm;
+   }
+   
+   private static CacheManager getManagerForCache(String cacheConfigName) 
+      throws ClusteringNotSupportedException
+   {
+      CacheManager cm = null;
+      try
+      {
+         cm = CacheManagerLocator.getCacheManagerLocator().getCacheManager(null);
+      }
+      catch (Throwable t)
+      {
+         throw new ClusteringNotSupportedException("Could not access CacheManager for JBossWeb clustering", t);
+      }
+      
+      if (!cm.getConfigurationNames().contains(cacheConfigName))
+         throw new IllegalStateException("CacheManager does not recognize config " + cacheConfigName);
+      
+      return cm;
+   }
+   
+   /**
+    * Prevent instantiation.
+    */
+   private Util() {}
+
+}

Added: trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/distributedcache/spi/BatchingManager.java
===================================================================
--- trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/distributedcache/spi/BatchingManager.java	                        (rev 0)
+++ trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/distributedcache/spi/BatchingManager.java	2008-08-26 16:41:17 UTC (rev 77496)
@@ -0,0 +1,35 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2008, Red Hat Middleware LLC, 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.web.tomcat.service.session.distributedcache.spi;
+
+/**
+ * @author Brian Stansberry
+ *
+ */
+public interface BatchingManager
+{
+   boolean isBatchInProgress() throws Exception;
+   void startBatch() throws Exception;
+   void setBatchRollbackOnly() throws Exception;
+   void endBatch();
+}

Added: trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/distributedcache/spi/ClusteringNotSupportedException.java
===================================================================
--- trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/distributedcache/spi/ClusteringNotSupportedException.java	                        (rev 0)
+++ trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/distributedcache/spi/ClusteringNotSupportedException.java	2008-08-26 16:41:17 UTC (rev 77496)
@@ -0,0 +1,46 @@
+/*
+* JBoss, Home of Professional Open Source
+* Copyright 2005, JBoss Inc., and individual contributors as indicated
+* by the @authors tag. See the copyright.txt 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.web.tomcat.service.session.distributedcache.spi;
+
+
+/**
+ * This exception is thrown when the clustered HTTPSession-service
+ * is not found
+ *
+ * @author Thomas Peuss <jboss at peuss.de>
+ * @version $Revision: 56007 $
+ */
+public class ClusteringNotSupportedException extends Exception
+{
+   /** The serialVersionUID */
+   private static final long serialVersionUID = -2365801349845482947L;
+
+   public ClusteringNotSupportedException(String message)
+   {
+      super(message);
+   }
+   
+   public ClusteringNotSupportedException(String message, Throwable cause)
+   {
+      super(message, cause);
+   }
+}

Added: trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/distributedcache/spi/DistributableSession.java
===================================================================
--- trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/distributedcache/spi/DistributableSession.java	                        (rev 0)
+++ trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/distributedcache/spi/DistributableSession.java	2008-08-26 16:41:17 UTC (rev 77496)
@@ -0,0 +1,42 @@
+package org.jboss.web.tomcat.service.session.distributedcache.spi;
+
+import java.util.Map;
+
+import org.jboss.web.tomcat.service.session.ClusteredSession;
+
+/**
+ * Interface implemented by a <code>Session</code> implementation that
+ * can be handled by a DistributedCacheManager.
+ * 
+ * @author Brian Stansberry
+ * @version $Revision: $
+ */
+public interface DistributableSession
+{
+   String getIdInternal();
+   
+   int getVersion();
+
+   boolean getMustReplicateTimestamp();
+
+   DistributableSessionTimestamp getSessionTimestamp();
+
+   DistributableSessionMetadata getSessionMetadata();
+
+   Map<String, Object> getSessionAttributeMap();
+
+   void update(Integer version, DistributableSessionTimestamp timestamp, DistributableSessionMetadata metadata, Map attributes);
+
+   boolean isSessionMetadataDirty();
+
+   boolean isSessionAttributeMapDirty();
+
+   boolean getCreateRegionForSession();
+   
+   boolean needRegionForSession();
+   
+   boolean hasRegionForSession();
+   
+   void createdRegionForSession();
+
+}
\ No newline at end of file

Added: trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/distributedcache/spi/DistributableSessionMetadata.java
===================================================================
--- trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/distributedcache/spi/DistributableSessionMetadata.java	                        (rev 0)
+++ trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/distributedcache/spi/DistributableSessionMetadata.java	2008-08-26 16:41:17 UTC (rev 77496)
@@ -0,0 +1,76 @@
+package org.jboss.web.tomcat.service.session.distributedcache.spi;
+
+import java.io.Serializable;
+
+import org.jboss.metadata.web.jboss.ReplicationTrigger;
+
+/**
+ * Encapsulates the replicated metadata for a session. The wrapped data
+ * can be mutated, allowing the same object to always be stored in 
+ * JBoss Cache. Always storing the same object avoids an earlier, no
+ * longer accurate, object being reverted into the cache during a 
+ * transaction rollback.
+ */   
+public class DistributableSessionMetadata implements Serializable
+{
+ /** The serialVersionUID */
+   private static final long serialVersionUID = -6845914023373746866L;
+   
+   private String id;
+   private long creationTime;
+   private int maxInactiveInterval;
+   private boolean isNew;
+   private boolean isValid;
+   private ReplicationTrigger invalidationPolicy;
+   
+   public String getId()
+   {
+      return id;
+   }
+   public void setId(String id)
+   {
+      this.id = id;
+   }
+   public long getCreationTime()
+   {
+      return creationTime;
+   }
+   public void setCreationTime(long creationTime)
+   {
+      this.creationTime = creationTime;
+   }
+   public int getMaxInactiveInterval()
+   {
+      return maxInactiveInterval;
+   }
+   public void setMaxInactiveInterval(int maxInactiveInterval)
+   {
+      this.maxInactiveInterval = maxInactiveInterval;
+   }
+   public boolean isNew()
+   {
+      return isNew;
+   }
+   public void setNew(boolean isNew)
+   {
+      this.isNew = isNew;
+   }
+   public boolean isValid()
+   {
+      return isValid;
+   }
+   public void setValid(boolean isValid)
+   {
+      this.isValid = isValid;
+   }
+   public ReplicationTrigger getInvalidationPolicy()
+   {
+      return invalidationPolicy;
+   }
+   public void setInvalidationPolicy(ReplicationTrigger invalidationPolicy)
+   {
+      this.invalidationPolicy = invalidationPolicy;
+   }     
+   
+   
+}
\ No newline at end of file

Added: trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/distributedcache/spi/DistributableSessionTimestamp.java
===================================================================
--- trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/distributedcache/spi/DistributableSessionTimestamp.java	                        (rev 0)
+++ trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/distributedcache/spi/DistributableSessionTimestamp.java	2008-08-26 16:41:17 UTC (rev 77496)
@@ -0,0 +1,28 @@
+package org.jboss.web.tomcat.service.session.distributedcache.spi;
+
+import java.io.Serializable;
+
+/**
+ * Encapsulates the "thisAccessedTime" timestamp. The wrapped timestamp long
+ * can be mutated, allowing the same object to always be stored in 
+ * JBoss Cache. Always storing the same object avoids an earlier, no
+ * longer accurate, object being reverted into the cache during a 
+ * transaction rollback.
+ */
+public class DistributableSessionTimestamp implements Serializable
+{
+   /** The serialVersionUID */
+   private static final long serialVersionUID = 2317131983552715467L;
+   
+   private long timestamp = 0L;
+
+   public long getTimestamp()
+   {
+      return timestamp;
+   }
+
+   public void setTimestamp(long timestamp)
+   {
+      this.timestamp = timestamp;
+   }
+}
\ No newline at end of file

Added: trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/distributedcache/spi/DistributedCacheManager.java
===================================================================
--- trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/distributedcache/spi/DistributedCacheManager.java	                        (rev 0)
+++ trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/distributedcache/spi/DistributedCacheManager.java	2008-08-26 16:41:17 UTC (rev 77496)
@@ -0,0 +1,83 @@
+package org.jboss.web.tomcat.service.session.distributedcache.spi;
+
+import java.util.Map;
+import java.util.Set;
+
+
+import org.jboss.cache.CacheException;
+
+public interface DistributedCacheManager
+{
+
+   void start(ClassLoader tcl, LocalDistributableSessionManager manager);
+
+   void stop();
+
+   /**
+    * Gets the BatchingManager.
+    */
+   BatchingManager getBatchingManager();
+
+   /**
+    * Loads any serialized data in the cache into the given session
+    * using its <code>readExternal</code> method.
+    *
+    * @return the session passed as <code>toLoad</code>, or
+    *         <code>null</code> if the cache had no data stored
+    *         under the given session id.
+    */
+   <T extends DistributableSession> T loadSession(String realId, T toLoad);
+
+   void putSession(String realId, DistributableSession session);
+
+   void removeSession(String realId, boolean removeRegion);
+
+   void removeSessionLocal(String realId, boolean removeRegion);
+
+   void removeSessionLocal(String realId, String dataOwner);
+
+   void evictSession(String realId);
+
+   void evictSession(String realId, String dataOwner);
+
+   Map getSessionData(String realId, String dataOwner);
+
+   Object getAttribute(String realId, String key);
+
+   void putAttribute(String realId, String key, Object value);
+
+   void putAttribute(String realId, Map map);
+
+   void removeAttributes(String realId);
+
+   Object removeAttribute(String realId, String key);
+
+   void removeAttributesLocal(String realId);
+
+   /**
+    * Obtain the keys associated with this fqn. Note that it is not the fqn children.
+    *
+    */
+   Set getAttributeKeys(String realId);
+
+   /**
+    * Return all attributes associated with this session id.
+    * 
+    * @param realId the session id with any jvmRoute removed
+    * @return the attributes, or any empty Map if none are found.
+    */
+   Map getAttributes(String realId);
+
+   /**
+    * Gets the ids of all sessions in the underlying cache.
+    *
+    * @return Map<String, String> containing all of the session ids of sessions in the cache
+    *         (with any jvmRoute removed) as keys, and the identifier of the data owner for
+    *         the session as value (or a <code>null</code>  value if buddy
+    *         replication is not enabled.) Will not return <code>null</code>.
+    */
+   Map<String, String> getSessionIds() throws CacheException;
+
+   boolean isPassivationEnabled();
+
+}
\ No newline at end of file

Added: trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/distributedcache/spi/DistributedCacheManagerFactory.java
===================================================================
--- trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/distributedcache/spi/DistributedCacheManagerFactory.java	                        (rev 0)
+++ trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/distributedcache/spi/DistributedCacheManagerFactory.java	2008-08-26 16:41:17 UTC (rev 77496)
@@ -0,0 +1,34 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2008, Red Hat Middleware LLC, 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.web.tomcat.service.session.distributedcache.spi;
+
+/**
+ * @author Brian Stansberry
+ *
+ */
+public interface DistributedCacheManagerFactory
+{
+   DistributedCacheManager getDistributedCacheManager(String cacheConfigName) throws ClusteringNotSupportedException;
+   
+   FieldBasedDistributedCacheManager getFieldBasedDistributedCacheManager(String cacheConfigName) throws ClusteringNotSupportedException;
+}

Added: trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/distributedcache/spi/DistributedCacheManagerFactoryFactory.java
===================================================================
--- trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/distributedcache/spi/DistributedCacheManagerFactoryFactory.java	                        (rev 0)
+++ trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/distributedcache/spi/DistributedCacheManagerFactoryFactory.java	2008-08-26 16:41:17 UTC (rev 77496)
@@ -0,0 +1,164 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2008, Red Hat Middleware LLC, 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.web.tomcat.service.session.distributedcache.spi;
+
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.jboss.util.loading.ContextClassLoader;
+
+public class DistributedCacheManagerFactoryFactory
+{
+   /** 
+    * Default name of the class that {@link #getDistributedCacheManagerFactory()} 
+    * will try to instantiate if no factory is injected. 
+    */
+   public static final String DEFAULT_CLASS_NAME = "org.jboss.web.tomcat.service.session.distributedcache.impl.DistributedCacheManagerFactoryImpl";
+   
+   /** Singleton */
+   private static final DistributedCacheManagerFactoryFactory instance = new DistributedCacheManagerFactoryFactory();
+   
+   /** Gets the singleton instance. */
+   public static final DistributedCacheManagerFactoryFactory getInstance()
+   {
+      return instance;
+   }
+   
+   private DistributedCacheManagerFactory distributedCacheManagerFactory;
+   private String factoryClassName = DEFAULT_CLASS_NAME;
+   private Map<TomcatClusterConfig, TomcatClusterDistributedCacheManagerFactory> tomcatClusterFactories = 
+           new HashMap<TomcatClusterConfig, TomcatClusterDistributedCacheManagerFactory>();
+
+   /**
+    * Returns the factory, creating one if necessary by loading
+    * {@link #getFactoryClassName() the factory class} and invoking 
+    * {@link Class#newInstance()}.
+    * 
+    * @return the factory. Will not return <code>null</code>.
+    * @throws ClusteringNotSupportedException if there is a problem instantiating a factory
+    */
+   public synchronized DistributedCacheManagerFactory getDistributedCacheManagerFactory()
+      throws  ClusteringNotSupportedException
+   {
+      if (distributedCacheManagerFactory == null)
+      {
+         distributedCacheManagerFactory = instantiateFactory();
+         factoryClassName = distributedCacheManagerFactory.getClass().getName();
+      }
+      return distributedCacheManagerFactory;
+   }
+
+   /**
+    * Allows injection of the DistributedCacheManagerFactory.
+    * 
+    * @param distributedCacheManagerFactory the factory.
+    */
+   public synchronized void setDistributedCacheManagerFactory(DistributedCacheManagerFactory distributedCacheManagerFactory)
+   {
+      this.distributedCacheManagerFactory = distributedCacheManagerFactory;
+   }
+   
+   public synchronized TomcatClusterDistributedCacheManagerFactory getTomcatClusterDistributedCacheManagerFactory(TomcatClusterConfig config)
+   throws  ClusteringNotSupportedException
+   {
+      TomcatClusterDistributedCacheManagerFactory factory = tomcatClusterFactories.get(config);
+      if (factory == null)
+      {
+         factory = (TomcatClusterDistributedCacheManagerFactory) instantiateFactory();
+         tomcatClusterFactories.put(config, factory);
+      }
+      factory.setTomcatClusterConfig(config);
+      return factory;
+   }
+   
+   public synchronized void setTomcatClusterDistributedCacheManagerFactory(TomcatClusterConfig config,
+                                                 TomcatClusterDistributedCacheManagerFactory factory)
+   {
+      tomcatClusterFactories.put(config, factory);
+   }
+   
+   /**
+    * Gets the class name of the factory; either the name of any existing 
+    * factory, a class name injected via this property's setter, or
+    * {@link #DEFAULT_CLASS_NAME}.
+    * 
+    * @return the factory class name; will not return <code>null</code>
+    */
+   public synchronized String getFactoryClassName()
+   {
+      return factoryClassName;
+   }
+
+   /**
+    * Allows injection of the factory class name.
+    * 
+    * @param factoryClassName Fully qualified name of a class that implements 
+    *                         {@link DistributedCacheManagerFactory} and exposes
+    *                         a public no-arg constructor.
+    */
+   public synchronized void setFactoryClassName(String factoryClassName)
+   {
+      this.factoryClassName = factoryClassName == null ? DEFAULT_CLASS_NAME : factoryClassName;
+   }
+
+   /**
+    * Loads the factory class and instantiates it.
+    * 
+    * @throws ClusteringNotSupportedException
+    */
+   private DistributedCacheManagerFactory instantiateFactory() throws ClusteringNotSupportedException
+   {
+      String className = factoryClassName == null ? DEFAULT_CLASS_NAME : factoryClassName;
+      ClassLoader cl = getTCCL();
+      try
+      {
+         return (DistributedCacheManagerFactory) cl.loadClass(className).newInstance();
+      }
+      catch (Exception e)
+      {
+         throw new ClusteringNotSupportedException("Cannot create " + DistributedCacheManagerFactory.class.getSimpleName(), e);
+      }
+   }
+
+   private ClassLoader getTCCL()
+   {
+      if (System.getSecurityManager() == null)
+      {
+         return Thread.currentThread().getContextClassLoader();
+      }
+      else
+      {
+         return (ClassLoader) AccessController.doPrivileged(new PrivilegedAction() {
+           
+            public Object run()
+            {
+               return Thread.currentThread().getContextClassLoader();
+            }
+         });
+      }
+   }
+
+   private DistributedCacheManagerFactoryFactory() {}
+}
\ No newline at end of file

Added: trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/distributedcache/spi/FieldBasedDistributedCacheManager.java
===================================================================
--- trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/distributedcache/spi/FieldBasedDistributedCacheManager.java	                        (rev 0)
+++ trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/distributedcache/spi/FieldBasedDistributedCacheManager.java	2008-08-26 16:41:17 UTC (rev 77496)
@@ -0,0 +1,54 @@
+package org.jboss.web.tomcat.service.session.distributedcache.spi;
+
+import java.util.Set;
+
+
+public interface FieldBasedDistributedCacheManager extends DistributedCacheManager
+{
+
+   /**
+    * store the pojo instance in the cache. Note that this is for the aop cache.
+    * THe pojo needs to be "aspectized".
+    * 
+    * @param realId the session id with any jvmRoute removed
+    * @param key    the attribute key
+    * @param pojo
+    * @param createRegion TODO
+    */
+   public abstract Object setPojo(String realId, String key, Object pojo, DistributableSession session);
+
+   /**
+    * Remove pojo from the underlying cache store.
+    * @param realId the session id with any jvmRoute removed
+    * @param key    the attribute key
+    * @return pojo that just removed. Null if there none.
+    */
+   public abstract Object removePojo(String realId, String key);
+
+   /**
+    * Remove all the pojos from the underlying cache store locally 
+    * without replication.
+    * 
+    * @param realId the session id with any jvmRoute removed
+    */
+   public abstract void removePojosLocal(String realId);
+
+   /**
+    * Remove all the pojos from the underlying cache store locally 
+    * without replication.
+    * 
+    * @param realId the session id with any jvmRoute removed
+    */
+   public abstract void removePojoLocal(String realId, String key);
+
+   public abstract Set getPojoKeys(String realId);
+
+   /**
+    *
+    * @param realId the session id with any jvmRoute removed
+    * @param key    the attribute key
+    * @return Pojo that is associated with the attribute
+    */
+   public abstract Object getPojo(String realId, String key);
+
+}
\ No newline at end of file

Added: trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/distributedcache/spi/JBossSerializationObjectStreamSource.java
===================================================================
--- trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/distributedcache/spi/JBossSerializationObjectStreamSource.java	                        (rev 0)
+++ trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/distributedcache/spi/JBossSerializationObjectStreamSource.java	2008-08-26 16:41:17 UTC (rev 77496)
@@ -0,0 +1,54 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2008, Red Hat Middleware LLC, 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.web.tomcat.service.session.distributedcache.spi;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.ObjectInput;
+import java.io.ObjectOutput;
+import java.io.OutputStream;
+
+import org.jboss.ha.framework.interfaces.ObjectStreamSource;
+import org.jboss.serial.io.JBossObjectInputStreamSharedTree;
+import org.jboss.serial.io.JBossObjectOutputStreamSharedTree;
+
+/**
+ * {@link ObjectStreamSource} implementation that provides 
+ * {@link JBossObjectInputStreamSharedTree} and {@link JBossObjectOutputStreamSharedTree}.
+ * 
+ * @author Brian Stansberry
+ */
+public class JBossSerializationObjectStreamSource implements ObjectStreamSource
+{
+
+   public ObjectInput getObjectInput(InputStream input) throws IOException
+   {
+      return new JBossObjectInputStreamSharedTree(input);
+   }
+
+   public ObjectOutput getObjectOutput(OutputStream output) throws IOException
+   {
+      return new JBossObjectOutputStreamSharedTree(output);
+   }
+
+}

Added: trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/distributedcache/spi/LocalDistributableSessionManager.java
===================================================================
--- trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/distributedcache/spi/LocalDistributableSessionManager.java	                        (rev 0)
+++ trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/distributedcache/spi/LocalDistributableSessionManager.java	2008-08-26 16:41:17 UTC (rev 77496)
@@ -0,0 +1,49 @@
+package org.jboss.web.tomcat.service.session.distributedcache.spi;
+
+import org.jboss.metadata.web.jboss.ReplicationGranularity;
+
+
+public interface LocalDistributableSessionManager
+{
+   ReplicationGranularity getReplicationGranularity();
+   
+   boolean isPassivationEnabled();
+   
+   String getContextName();
+   
+   String getHostName();
+   
+   /**
+    * Notifies the manager that a session in the distributed cache has
+    * been invalidated
+    * 
+    * @param realId the session id excluding any jvmRoute
+    */
+   void notifyRemoteInvalidation(String realId);
+
+   /**
+    * Callback from the distributed cache notifying of a local modification
+    * to a session's attributes.  Meant for use with FIELD granularity,
+    * where the session may not be aware of modifications.
+    * 
+    * @param realId the session id excluding any jvmRoute
+    */
+   void notifyLocalAttributeModification(String realId);
+
+   void sessionActivated();
+
+   /**
+    * Callback from the distributed cache to notify us that a session
+    * has been modified remotely.
+    * 
+    * @param realId the session id, without any trailing jvmRoute
+    * @param dataOwner  the owner of the session.  Can be <code>null</code> if
+    *                   the owner is unknown.
+    * @param distributedVersion the session's version per the distributed cache
+    * @param timestamp the session's timestamp per the distributed cache
+    * @param metadata the session's metadata per the distributed cache
+    */
+   boolean sessionChangedInDistributedCache(String realId, String dataOwner, int distributedVersion,
+         DistributableSessionTimestamp timestamp, DistributableSessionMetadata metadata);
+
+}
\ No newline at end of file

Added: trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/distributedcache/spi/SessionSerializationFactory.java
===================================================================
--- trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/distributedcache/spi/SessionSerializationFactory.java	                        (rev 0)
+++ trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/distributedcache/spi/SessionSerializationFactory.java	2008-08-26 16:41:17 UTC (rev 77496)
@@ -0,0 +1,87 @@
+/*
+* JBoss, Home of Professional Open Source
+* Copyright 2005, JBoss Inc., and individual contributors as indicated
+* by the @authors tag. See the copyright.txt 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.web.tomcat.service.session.distributedcache.spi;
+
+import java.io.Serializable;
+
+import org.jboss.ha.framework.interfaces.ObjectStreamSource;
+import org.jboss.ha.framework.server.MarshalledValueObjectStreamSource;
+import org.jboss.ha.framework.server.SimpleCachableMarshalledValue;
+import org.jboss.logging.Logger;
+
+/**
+ * Factory class for creating object output and input streams, 
+ * switching between JDK Serialization and JBoss Serialization classes. 
+ * Using MarshalledValue to replace Serializable used inside different 
+ * web app class loader context. Assuming that the caller classes are already
+ * handling the switch between web app class loader context. 
+ *  
+ * 
+ * @author <a href="hmesha at novell.com">Hany Mesha</a>
+ * @author Brian Stansberry
+ */
+public class SessionSerializationFactory
+{
+   /** 
+    * System property that, if set to <code>true</code>, indicates that 
+    * JBoss Serialization should be used for session serialization.
+    */
+   public static final String JBOSS_SERIALIZATION_SYS_PROP = "session.serialization.jboss";
+   private static Logger log_ = Logger.getLogger(SessionSerializationFactory.class);
+   private static boolean useJBossSerialization = false;
+   private static ObjectStreamSource objectStreamSource; 
+
+   static
+   {
+       String useJBossSerializationStr = System.getProperty(JBOSS_SERIALIZATION_SYS_PROP, "false");
+       useJBossSerialization = Boolean.valueOf(useJBossSerializationStr).booleanValue();
+       objectStreamSource = useJBossSerialization ? new JBossSerializationObjectStreamSource()
+                                                  : new MarshalledValueObjectStreamSource(); 
+       try
+       {
+          if (useJBossSerialization)
+          {
+             log_.debug("Using JBossSerialization for web session replication");
+          }
+       }
+       catch (Throwable ignored) 
+       {
+          // we don't fail a static initializer due to a debug log stmt
+       }
+   }
+   
+   public static SimpleCachableMarshalledValue createMarshalledValue(Serializable o)
+   {
+      return new SimpleCachableMarshalledValue(o, objectStreamSource);
+   }
+   
+   public static ObjectStreamSource getObjectStreamSource()
+   {
+      return objectStreamSource;
+   }
+
+   public static boolean useJBossSerialization()
+   {
+       return useJBossSerialization;
+   }
+
+}

Added: trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/distributedcache/spi/TomcatClusterConfig.java
===================================================================
--- trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/distributedcache/spi/TomcatClusterConfig.java	                        (rev 0)
+++ trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/distributedcache/spi/TomcatClusterConfig.java	2008-08-26 16:41:17 UTC (rev 77496)
@@ -0,0 +1,37 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2008, Red Hat Middleware LLC, 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.web.tomcat.service.session.distributedcache.spi;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+
+/**
+ * @author Brian Stansberry
+ *
+ */
+public interface TomcatClusterConfig
+{
+   String getCacheObjectName();
+   File getCacheConfigFile() throws FileNotFoundException;
+   String getClusterName();
+}

Added: trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/distributedcache/spi/TomcatClusterDistributedCacheManagerFactory.java
===================================================================
--- trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/distributedcache/spi/TomcatClusterDistributedCacheManagerFactory.java	                        (rev 0)
+++ trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/distributedcache/spi/TomcatClusterDistributedCacheManagerFactory.java	2008-08-26 16:41:17 UTC (rev 77496)
@@ -0,0 +1,38 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2008, Red Hat Middleware LLC, 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.web.tomcat.service.session.distributedcache.spi;
+
+/**
+ * @author Brian Stansberry
+ *
+ */
+public interface TomcatClusterDistributedCacheManagerFactory extends DistributedCacheManagerFactory
+{
+   TomcatClusterConfig getTomcatClusterConfig();
+   
+   void setTomcatClusterConfig(TomcatClusterConfig clusterConfig);
+   
+   void start() throws Exception;
+   
+   void stop() throws Exception;
+}




More information about the jboss-cvs-commits mailing list