[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