[jbosscache-commits] JBoss Cache SVN: r7186 - in core/trunk/src/test: java/org/jboss/cache/integration/websession and 3 other directories.

jbosscache-commits at lists.jboss.org jbosscache-commits at lists.jboss.org
Mon Nov 24 02:01:36 EST 2008


Author: bstansberry at jboss.com
Date: 2008-11-24 02:01:36 -0500 (Mon, 24 Nov 2008)
New Revision: 7186

Added:
   core/trunk/src/test/java/org/jboss/cache/integration/CacheManagerSupport.java
   core/trunk/src/test/java/org/jboss/cache/integration/JGroupsUtil.java
   core/trunk/src/test/java/org/jboss/cache/integration/MockChannelFactory.java
   core/trunk/src/test/java/org/jboss/cache/integration/UnitTestCacheFactoryCacheManager.java
   core/trunk/src/test/java/org/jboss/cache/integration/UnitTestCacheFactoryConfigurationRegistry.java
   core/trunk/src/test/java/org/jboss/cache/integration/websession/
   core/trunk/src/test/java/org/jboss/cache/integration/websession/BuddyReplicationFailoverTest.java
   core/trunk/src/test/java/org/jboss/cache/integration/websession/util/
   core/trunk/src/test/java/org/jboss/cache/integration/websession/util/AbstractServlet.java
   core/trunk/src/test/java/org/jboss/cache/integration/websession/util/BuddyReplicationAssertions.java
   core/trunk/src/test/java/org/jboss/cache/integration/websession/util/DirtyMetadataServlet.java
   core/trunk/src/test/java/org/jboss/cache/integration/websession/util/FqnUtil.java
   core/trunk/src/test/java/org/jboss/cache/integration/websession/util/GetAttributesServlet.java
   core/trunk/src/test/java/org/jboss/cache/integration/websession/util/InvalidationServlet.java
   core/trunk/src/test/java/org/jboss/cache/integration/websession/util/MultipleActionServlet.java
   core/trunk/src/test/java/org/jboss/cache/integration/websession/util/RemoveAttributesServlet.java
   core/trunk/src/test/java/org/jboss/cache/integration/websession/util/Request.java
   core/trunk/src/test/java/org/jboss/cache/integration/websession/util/Servlet.java
   core/trunk/src/test/java/org/jboss/cache/integration/websession/util/Session.java
   core/trunk/src/test/java/org/jboss/cache/integration/websession/util/SessionManager.java
   core/trunk/src/test/java/org/jboss/cache/integration/websession/util/SessionManagerSupport.java
   core/trunk/src/test/java/org/jboss/cache/integration/websession/util/SessionMetadata.java
   core/trunk/src/test/java/org/jboss/cache/integration/websession/util/SetAttributesServlet.java
   core/trunk/src/test/java/org/jboss/cache/integration/websession/util/WebAppMetadata.java
   core/trunk/src/test/java/org/jboss/cache/integration/websession/util/WebSessionTestBase.java
   core/trunk/src/test/resources/configs/integration/
   core/trunk/src/test/resources/configs/integration/hibernate-cache-configs.xml
   core/trunk/src/test/resources/configs/integration/jgroups-channelfactory-stacks.xml
   core/trunk/src/test/resources/configs/integration/sfsb-cache-configs.xml
   core/trunk/src/test/resources/configs/integration/web-session-cache-configs.xml
Log:
[JBCACHE-1445] Add test showing problem with cleanup after data gravitation

Added: core/trunk/src/test/java/org/jboss/cache/integration/CacheManagerSupport.java
===================================================================
--- core/trunk/src/test/java/org/jboss/cache/integration/CacheManagerSupport.java	                        (rev 0)
+++ core/trunk/src/test/java/org/jboss/cache/integration/CacheManagerSupport.java	2008-11-24 07:01:36 UTC (rev 7186)
@@ -0,0 +1,89 @@
+/*
+ * 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.cache.integration;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.jboss.cache.CacheManager;
+
+/**
+ * @author Brian Stansberry
+ */
+public class CacheManagerSupport
+{
+   /**
+    * For each thread holds list of cache managers created using this factory.
+    */
+   private static final ThreadLocal<List<UnitTestCacheFactoryCacheManager>> threadCacheManagers =
+         new ThreadLocal<List<UnitTestCacheFactoryCacheManager>>()
+         {
+            @Override
+            protected List<UnitTestCacheFactoryCacheManager> initialValue()
+            {
+               return new ArrayList<UnitTestCacheFactoryCacheManager>();
+            }
+         };
+         
+  public static List<CacheManager> createCacheManagers(int count, String cacheConfigFileName, String stacksXmlFileName)
+  {
+     List<UnitTestCacheFactoryCacheManager> existing = threadCacheManagers.get();
+     List<CacheManager> result = new ArrayList<CacheManager>(count);
+     int added = 0;
+     for (UnitTestCacheFactoryCacheManager cm : existing)
+     {
+        if (cacheConfigFileName.equals(cm.getCacheConfigFileName()) && stacksXmlFileName.equals(cm.getStacksXMLFileName()))
+        {
+           result.add(cm);
+           added++;
+        }
+     }
+     
+     for (; added < count; added++)
+     {
+        UnitTestCacheFactoryCacheManager cm = new UnitTestCacheFactoryCacheManager(cacheConfigFileName, stacksXmlFileName);
+        try
+        {
+            cm.start();
+        }
+        catch (Exception e)
+        {
+           throw new RuntimeException(e);
+        }
+        result.add(cm);
+        existing.add(cm);
+     }
+     
+     return result;
+  }
+  
+  public static void tearDown()
+  {
+     List<UnitTestCacheFactoryCacheManager> existing = threadCacheManagers.get();
+     for (UnitTestCacheFactoryCacheManager cm : existing)
+     {
+        cm.stop();
+     }
+     threadCacheManagers.remove();
+  }
+}

Added: core/trunk/src/test/java/org/jboss/cache/integration/JGroupsUtil.java
===================================================================
--- core/trunk/src/test/java/org/jboss/cache/integration/JGroupsUtil.java	                        (rev 0)
+++ core/trunk/src/test/java/org/jboss/cache/integration/JGroupsUtil.java	2008-11-24 07:01:36 UTC (rev 7186)
@@ -0,0 +1,153 @@
+/*
+ * 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.cache.integration;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.HashMap;
+import java.util.Map;
+
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+
+import org.jboss.cache.config.ConfigurationException;
+import org.jboss.cache.util.FileLookup;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.NamedNodeMap;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+
+/**
+ * JGroups-related utilities.
+ * 
+ * @author Brian Stansberry
+ */
+public class JGroupsUtil
+{
+   private final static String PROTOCOL_STACKS = "protocol_stacks";
+   private final static String STACK = "stack";
+   private static final String NAME = "name";
+   private static final String CONFIG = "config";
+
+   public static Map<String, Element> getStackConfigs(String stacksXmlResource) throws Exception
+   {
+      FileLookup fileLookup = new FileLookup();
+      InputStream is = fileLookup.lookupFile(stacksXmlResource);
+      if (is == null)
+      {
+         throw new ConfigurationException("Unable to find config file " + stacksXmlResource + " either in classpath or on the filesystem!");
+      }
+      
+      return getStackConfigs(is);
+   }
+   
+   /**
+    * Parses a set if "config" elements out of a JGroups stacks.xml file.
+    * 
+    * @param input
+    * @return
+    * @throws Exception
+    */
+   public static Map<String, Element> getStackConfigs(InputStream input) throws Exception
+   {
+      DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
+      factory.setValidating(false); //for now
+      DocumentBuilder builder = factory.newDocumentBuilder();
+      Document document = builder.parse(input);
+
+      // The root element of the document should be the "config" element,
+      // but the parser(Element) method checks this so a check is not
+      // needed here.
+      Element configElement = document.getDocumentElement();
+      return getStackConfigs(configElement);
+   }
+
+   private static Map<String, Element> getStackConfigs(Element root) throws Exception
+   {
+      Map<String, Element> result = new HashMap<String, Element>();
+
+      String root_name = root.getNodeName();
+      if (!PROTOCOL_STACKS.equals(root_name.trim().toLowerCase()))
+      {
+         String error = "XML protocol stack configuration does not start with a '<config>' element; "
+               + "maybe the XML configuration needs to be converted to the new format ?\n"
+               + "use 'java org.jgroups.conf.XmlConfigurator <old XML file> -new_format' to do so";
+         throw new IOException("invalid XML configuration: " + error);
+      }
+
+      NodeList tmp_stacks = root.getChildNodes();
+      for (int i = 0; i < tmp_stacks.getLength(); i++)
+      {
+         Node node = tmp_stacks.item(i);
+         if (node.getNodeType() != Node.ELEMENT_NODE)
+            continue;
+
+         Element stack = (Element) node;
+         String tmp = stack.getNodeName();
+         if (!STACK.equals(tmp.trim().toLowerCase()))
+         {
+            throw new IOException("invalid configuration: didn't find a \"" + STACK + "\" element under \""
+                  + PROTOCOL_STACKS + "\"");
+         }
+
+         NamedNodeMap attrs = stack.getAttributes();
+         Node name = attrs.getNamedItem(NAME);
+         String st_name = name.getNodeValue();
+         
+         NodeList configs = stack.getChildNodes();
+         for (int j = 0; j < configs.getLength(); j++)
+         {
+            Node tmp_config = configs.item(j);
+            if (tmp_config.getNodeType() != Node.ELEMENT_NODE)
+               continue;
+            Element cfg = (Element) tmp_config;
+            tmp = cfg.getNodeName();
+            if (!CONFIG.equals(tmp))
+               throw new IOException("invalid configuration: didn't find a \"" + CONFIG + "\" element under \"" + STACK
+                     + "\"");
+
+            if (!result.containsKey(st_name))
+            {
+               result.put(st_name, cfg);
+            }
+            else
+            {
+               throw new IllegalStateException("didn't add config '" + st_name
+                     + " because one of the same name already existed");
+            }
+         }
+      }
+
+      return result;
+   }
+
+   /**
+    * Prevent instantiation.
+    */
+   private JGroupsUtil()
+   {
+      throw new UnsupportedOperationException("just a static util class");
+   }
+
+}

Added: core/trunk/src/test/java/org/jboss/cache/integration/MockChannelFactory.java
===================================================================
--- core/trunk/src/test/java/org/jboss/cache/integration/MockChannelFactory.java	                        (rev 0)
+++ core/trunk/src/test/java/org/jboss/cache/integration/MockChannelFactory.java	2008-11-24 07:01:36 UTC (rev 7186)
@@ -0,0 +1,94 @@
+/*
+ * 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.cache.integration;
+
+import java.io.File;
+import java.net.URL;
+
+import org.jgroups.Channel;
+import org.jgroups.ChannelException;
+import org.jgroups.ChannelFactory;
+import org.w3c.dom.Element;
+
+/**
+ * @author Brian Stansberry
+ *
+ */
+public class MockChannelFactory implements ChannelFactory
+{
+   public static final MockChannelFactory INSTANCE = new MockChannelFactory();
+   
+   private MockChannelFactory() {}
+
+   public Channel createChannel() throws ChannelException
+   {
+      throw new UnsupportedOperationException();
+   }
+
+   public Channel createChannel(Object props) throws ChannelException
+   {
+      throw new UnsupportedOperationException();
+   }
+
+   public Channel createChannel(String stack_name) throws Exception
+   {
+      throw new UnsupportedOperationException();
+   }
+
+   public Channel createMultiplexerChannel(String stack_name, String id) throws Exception
+   {
+      throw new UnsupportedOperationException();
+   }
+
+   public Channel createMultiplexerChannel(String stack_name, String id, boolean register_for_state_transfer,
+         String substate_id) throws Exception
+   {
+      throw new UnsupportedOperationException();
+   }
+
+   public void setMultiplexerConfig(Object properties) throws Exception
+   {
+      throw new UnsupportedOperationException();
+   }
+
+   public void setMultiplexerConfig(File properties) throws Exception
+   {
+      throw new UnsupportedOperationException();
+   }
+
+   public void setMultiplexerConfig(Element properties) throws Exception
+   {
+      throw new UnsupportedOperationException();
+   }
+
+   public void setMultiplexerConfig(URL properties) throws Exception
+   {
+      throw new UnsupportedOperationException();
+   }
+
+   public void setMultiplexerConfig(String properties) throws Exception
+   {
+      throw new UnsupportedOperationException();
+   }
+
+}

Added: core/trunk/src/test/java/org/jboss/cache/integration/UnitTestCacheFactoryCacheManager.java
===================================================================
--- core/trunk/src/test/java/org/jboss/cache/integration/UnitTestCacheFactoryCacheManager.java	                        (rev 0)
+++ core/trunk/src/test/java/org/jboss/cache/integration/UnitTestCacheFactoryCacheManager.java	2008-11-24 07:01:36 UTC (rev 7186)
@@ -0,0 +1,81 @@
+/*
+ * 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.cache.integration;
+
+import org.jboss.cache.Cache;
+import org.jboss.cache.CacheManagerImpl;
+import org.jboss.cache.UnitTestCacheFactory;
+import org.jboss.cache.config.Configuration;
+
+/**
+ * CacheManager implementation that integrates with UnitTestCacheFactory.
+ * 
+ * @author Brian Stansberry
+ */
+public class UnitTestCacheFactoryCacheManager extends CacheManagerImpl
+{
+   private final String cacheConfigFileName;
+   private final String stacksXMLFileName;
+   
+   /**
+    * Create a new UnitTestCacheFactoryCacheManager.
+    * 
+    * @param configFileName
+    * @param factory
+    */
+   public UnitTestCacheFactoryCacheManager(String cacheConfigFileName, String stacksXMLFileName)
+   {
+      super(new UnitTestCacheFactoryConfigurationRegistry(cacheConfigFileName, stacksXMLFileName), MockChannelFactory.INSTANCE);
+      this.cacheConfigFileName = cacheConfigFileName;
+      this.stacksXMLFileName = stacksXMLFileName;
+   }
+
+   public String getCacheConfigFileName()
+   {
+      return cacheConfigFileName;
+   }
+   
+   public String getStacksXMLFileName()
+   {
+      return stacksXMLFileName;
+   }
+
+   @Override
+   public void start() throws Exception
+   {
+      ((UnitTestCacheFactoryConfigurationRegistry) getConfigurationRegistry()).start();
+      super.start();
+   }
+
+   /**
+    * Overrides superclass to use UnitTestCacheFactory to create the cache.
+    */
+   @Override
+   protected Cache<Object, Object> createCache(Configuration config)
+   {
+      return new UnitTestCacheFactory<Object, Object>().createCache(config, false);
+   }
+   
+   
+
+}

Added: core/trunk/src/test/java/org/jboss/cache/integration/UnitTestCacheFactoryConfigurationRegistry.java
===================================================================
--- core/trunk/src/test/java/org/jboss/cache/integration/UnitTestCacheFactoryConfigurationRegistry.java	                        (rev 0)
+++ core/trunk/src/test/java/org/jboss/cache/integration/UnitTestCacheFactoryConfigurationRegistry.java	2008-11-24 07:01:36 UTC (rev 7186)
@@ -0,0 +1,177 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2000 - 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.cache.integration;
+
+import java.util.HashSet;
+import java.util.Hashtable;
+import java.util.Map;
+import java.util.Set;
+
+import org.jboss.cache.config.Configuration;
+import org.jboss.cache.config.ConfigurationRegistry;
+import org.jboss.cache.config.parsing.CacheConfigsXmlParser;
+import org.w3c.dom.Element;
+
+/**
+ * XmlParsingConfigurationRegistry variant that replaces any MuxStackName in
+ * configurations with a corresponding Element parsed out of the provided
+ * JGroups stacks.xml file. UnitTestCacheFactory can then mangle the element.
+ *
+ * @author <a href="brian.stansberry at jboss.com">Brian Stansberry</a>
+ * @version $Revision: 7168 $
+ */
+public class UnitTestCacheFactoryConfigurationRegistry implements ConfigurationRegistry
+{
+   public static final String DEFAULT_STACKS_XML_RESOURCE = "configs/integration/jgroups-channelfactory-stacks.xml";
+   
+   private final CacheConfigsXmlParser parser;
+   private final String configResource;
+   private final Map<String, Element> stacks;
+   private final Map<String, Configuration> configs = new Hashtable<String, Configuration>();
+   private boolean started;
+
+   public UnitTestCacheFactoryConfigurationRegistry(String cacheConfigResource) 
+   {
+      this(cacheConfigResource, DEFAULT_STACKS_XML_RESOURCE);
+   }
+   
+   public UnitTestCacheFactoryConfigurationRegistry(String cacheConfigResource, String stacksXmlResource) 
+   {
+      parser = new CacheConfigsXmlParser();
+      this.configResource = cacheConfigResource;
+      try
+      {
+         this.stacks = JGroupsUtil.getStackConfigs(stacksXmlResource);
+      }
+      catch (RuntimeException e)
+      {
+         throw e;
+      }
+      catch (Exception e)
+      {
+         throw new RuntimeException("problem parsing JGroups stacks", e);
+      }
+   }
+
+   public void start() throws Exception
+   {
+      if (!started)
+      {
+         if (configResource != null)
+         {
+            Map<String, Configuration> parsed = parser.parseConfigs(configResource);
+            for (Map.Entry<String, Configuration> entry : parsed.entrySet())
+            {
+               registerConfiguration(entry.getKey(), entry.getValue());
+            }
+         }
+         started = true;
+      }
+   }
+
+   public void stop()
+   {
+      if (started)
+      {
+         synchronized (configs)
+         {
+            configs.clear();
+         }
+         started = false;
+      }
+   }
+
+   public String getConfigResource()
+   {
+      return configResource;
+   }
+
+   public Set<String> getConfigurationNames()
+   {
+      return new HashSet<String>(configs.keySet());
+   }
+
+   public void registerConfiguration(String configName, Configuration config)
+         throws CloneNotSupportedException
+   {
+      synchronized (configs)
+      {
+         if (configs.containsKey(configName))
+            throw new IllegalStateException(configName + " already registered");
+         Configuration clone = config.clone();
+         fixJGroupsConfig(clone);
+         configs.put(configName, clone);
+      }
+   }
+
+   public void unregisterConfiguration(String configName)
+   {
+      synchronized (configs)
+      {
+         if (configs.remove(configName) == null)
+            throw new IllegalStateException(configName + " not registered");
+      }
+   }
+
+   public Configuration getConfiguration(String configName)
+   {
+      Configuration config;
+      synchronized (configs)
+      {
+         config = configs.get(configName);
+      }
+
+      if (config == null)
+         throw new IllegalArgumentException("unknown config " + configName);
+
+      // Don't hand out a ref to our master copy
+      try
+      {
+         return config.clone();
+      }
+      catch (CloneNotSupportedException e)
+      {
+         // This should not happen, as we already cloned the config
+         throw new RuntimeException("Could not clone configuration " + configName, e);
+      }
+   }
+
+   /** Replace a stack name with a stack element that UnitTestCacheFactory can mangle */
+   private void fixJGroupsConfig(Configuration clone)
+   {
+      String stackName = clone.getMultiplexerStack();
+      if (stackName != null)
+      {
+         clone.setMultiplexerStack(null);
+         Element e = stacks.get(stackName);
+         if (e == null)
+         {
+            throw new IllegalStateException("unknown stack " + stackName);            
+         }
+         
+         clone.setClusterConfig(e);         
+      }
+      
+   }
+   
+   
+}

Added: core/trunk/src/test/java/org/jboss/cache/integration/websession/BuddyReplicationFailoverTest.java
===================================================================
--- core/trunk/src/test/java/org/jboss/cache/integration/websession/BuddyReplicationFailoverTest.java	                        (rev 0)
+++ core/trunk/src/test/java/org/jboss/cache/integration/websession/BuddyReplicationFailoverTest.java	2008-11-24 07:01:36 UTC (rev 7186)
@@ -0,0 +1,159 @@
+/*
+ * 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.cache.integration.websession;
+
+import java.util.Collections;
+
+import org.jboss.cache.commands.write.PutDataMapCommand;
+import org.jboss.cache.commands.write.RemoveNodeCommand;
+import org.jboss.cache.integration.websession.util.BuddyReplicationAssertions;
+import org.jboss.cache.integration.websession.util.GetAttributesServlet;
+import org.jboss.cache.integration.websession.util.InvalidationServlet;
+import org.jboss.cache.integration.websession.util.MultipleActionServlet;
+import org.jboss.cache.integration.websession.util.Request;
+import org.jboss.cache.integration.websession.util.SessionManager;
+import org.jboss.cache.integration.websession.util.SetAttributesServlet;
+import org.jboss.cache.integration.websession.util.WebSessionTestBase;
+import org.jboss.cache.util.internals.replicationlisteners.ReplicationListener;
+import org.testng.annotations.Test;
+
+
+/**
+ * @author Brian Stansberry
+ *
+ */
+ at Test(groups = "integration", sequential = true, testName = "integration.websession.BuddyReplicationFailoverTest")
+public class BuddyReplicationFailoverTest extends WebSessionTestBase
+{
+   public static final String KEY = "key";
+
+   @Override
+   protected String getCacheConfigName()
+   {
+      return "br-standard-session-cache";
+   }
+
+   @Override
+   protected int getNumCacheManagers()
+   {
+      return 4;
+   }
+   
+   @Override
+   protected boolean getCreateManagersInSetup()
+   {
+      return true;
+   }
+   
+   public void testFailoverAndFailBack()
+   {
+      int attr = 0;
+      
+      SessionManager mgr0 = getSessionManagers().get(0);
+      SessionManager mgr1 = getSessionManagers().get(1);
+      SessionManager mgr2 = getSessionManagers().get(2);
+      SessionManager mgr3 = getSessionManagers().get(3);
+      
+      String contextHostName = mgr0.getContextHostName();
+
+      // Create the session
+      SetAttributesServlet sas = new SetAttributesServlet(Collections.singletonMap(KEY, getAttributeValue(attr++)));
+      ReplicationListener replListener1 = ReplicationListener.getReplicationListener(mgr1.getCache());
+      replListener1.expectWithTx(PutDataMapCommand.class);
+      
+      Request req = new Request(mgr0, null, sas);
+      req.execute();      
+      replListener1.waitForReplicationToOccur();      
+      
+      String sessionId = sas.getSessionId();
+      assert sessionId != null : "session id is null";
+      // validate cache contents
+      BuddyReplicationAssertions.assertBuddyBackup(contextHostName, sessionId, mgr0.getCache(), mgr1.getCache());
+      
+      // Modify the session
+      sas = new SetAttributesServlet(Collections.singletonMap(KEY, getAttributeValue(attr++))); 
+      replListener1.expectWithTx(PutDataMapCommand.class);
+      
+      req = new Request(mgr0, sessionId, sas);
+      req.execute();      
+      replListener1.waitForReplicationToOccur();
+      
+      // Fail over; request reads the session and then modifies the session
+      GetAttributesServlet gas = new GetAttributesServlet(Collections.singleton(KEY));
+      sas = new SetAttributesServlet(Collections.singletonMap(KEY, getAttributeValue(attr++)));   
+      MultipleActionServlet mas = new MultipleActionServlet(gas, sas);
+      ReplicationListener replListener0 = ReplicationListener.getReplicationListener(mgr0.getCache());
+      replListener0.expectWithTx(PutDataMapCommand.class); 
+      
+      req = new Request(mgr3, sessionId, mas);
+      req.execute();      
+      replListener0.waitForReplicationToOccur();
+      
+      assert sessionId.equals(mas.getSessionId()) : "wrong session id; expected " + sessionId + " got " + mas.getSessionId();
+      Integer integer = (Integer) gas.getReadAttributes().get(KEY);
+      assert integer != null : "null attribute value";
+      assert integer.intValue() == (attr - 2) : "wrong val " + integer + " expected " + (attr -2);
+      
+      BuddyReplicationAssertions.assertBuddyBackup(contextHostName, sessionId, mgr3.getCache(), mgr0.getCache());
+      BuddyReplicationAssertions.assertUnrelated(contextHostName, sessionId, mgr1.getCache());
+      
+      // Modify the session again
+      sas = new SetAttributesServlet(Collections.singletonMap(KEY, getAttributeValue(attr++)));
+      replListener0.expectWithTx(PutDataMapCommand.class);
+      
+      req = new Request(mgr3, sessionId, sas);
+      req.execute();      
+      replListener0.waitForReplicationToOccur();
+      
+      // Fail back; request reads the session and then modifies the session
+      gas = new GetAttributesServlet(Collections.singleton(KEY));
+      sas = new SetAttributesServlet(Collections.singletonMap(KEY, getAttributeValue(attr++)));   
+      mas = new MultipleActionServlet(gas, sas);
+      replListener1.expectWithTx(PutDataMapCommand.class); 
+      
+      req = new Request(mgr0, sessionId, mas);
+      req.execute();      
+      replListener1.waitForReplicationToOccur();
+      
+      assert sessionId.equals(mas.getSessionId()) : "wrong session id; expected " + sessionId + " got " + mas.getSessionId();
+      integer = (Integer) gas.getReadAttributes().get(KEY);
+      assert integer != null : "null attribute value";
+      assert integer.intValue() == attr - 2 : "wrong val " + integer + " expected " + (attr -2);
+      
+      BuddyReplicationAssertions.assertBuddyBackup(contextHostName, sessionId, mgr0.getCache(), mgr1.getCache());
+      BuddyReplicationAssertions.assertUnrelated(contextHostName, sessionId, mgr3.getCache());
+      
+      // Invalidate the session
+      InvalidationServlet invs = new InvalidationServlet();
+      replListener1.expectWithTx(RemoveNodeCommand.class); 
+      
+      req = new Request(mgr0, sessionId, invs);
+      req.execute();      
+      replListener1.waitForReplicationToOccur();
+
+      BuddyReplicationAssertions.assertUnrelated(contextHostName, sessionId, mgr0.getCache());
+      BuddyReplicationAssertions.assertUnrelated(contextHostName, sessionId, mgr1.getCache());
+      BuddyReplicationAssertions.assertUnrelated(contextHostName, sessionId, mgr2.getCache());
+      BuddyReplicationAssertions.assertUnrelated(contextHostName, sessionId, mgr3.getCache());
+   }
+}

Added: core/trunk/src/test/java/org/jboss/cache/integration/websession/util/AbstractServlet.java
===================================================================
--- core/trunk/src/test/java/org/jboss/cache/integration/websession/util/AbstractServlet.java	                        (rev 0)
+++ core/trunk/src/test/java/org/jboss/cache/integration/websession/util/AbstractServlet.java	2008-11-24 07:01:36 UTC (rev 7186)
@@ -0,0 +1,57 @@
+/*
+ * 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.cache.integration.websession.util;
+
+/**
+ * Basic Servlet impl. Caches a reference to the session.
+ * @author Brian Stansberry
+ *
+ */
+public abstract class AbstractServlet implements Servlet
+{
+   private Session session;
+   
+   protected Session extractSession(Request request)
+   {
+      if (session == null)
+      {
+         session = request.getSession(true);
+      }
+      return session;
+   }
+   
+   protected void clearSession()
+   {
+      session = null;
+   }
+   
+   public Session getSession()
+   {
+      return session;
+   }
+   
+   public String getSessionId()
+   {
+      return session == null ? null : session.getId();
+   }
+}

Added: core/trunk/src/test/java/org/jboss/cache/integration/websession/util/BuddyReplicationAssertions.java
===================================================================
--- core/trunk/src/test/java/org/jboss/cache/integration/websession/util/BuddyReplicationAssertions.java	                        (rev 0)
+++ core/trunk/src/test/java/org/jboss/cache/integration/websession/util/BuddyReplicationAssertions.java	2008-11-24 07:01:36 UTC (rev 7186)
@@ -0,0 +1,89 @@
+/*
+ * 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.cache.integration.websession.util;
+
+import java.util.Map;
+
+import org.jboss.cache.Cache;
+import org.jboss.cache.Fqn;
+import org.jboss.cache.Node;
+import org.jboss.cache.buddyreplication.BuddyFqnTransformer;
+import org.jboss.cache.buddyreplication.BuddyManager;
+
+/**
+ * @author Brian Stansberry
+ *
+ */
+public class BuddyReplicationAssertions
+{
+   public static void assertBuddyBackup(String contextHostName, String sessionId, Cache<Object, Object> owner, Cache<Object, Object> backup)
+   {
+      Fqn<String> fqn = Fqn.fromElements(FqnUtil.JSESSION, contextHostName, sessionId);
+      Map<Object, Object> owned = owner.getData(fqn);
+      assert owned != null : "owned is null";
+      
+      Fqn bFqn = new BuddyFqnTransformer().getBackupFqn(owner.getLocalAddress(), fqn);
+      Map<Object, Object> backed = owner.getData(fqn);
+      assert backed != null : "backed is null";
+      
+      assert owned.size() == backed.size() : "sizes differ; owned = " + owned.size() + " backed = " + backed.size();
+      
+      for (Map.Entry<Object, Object> entry : owned.entrySet())
+      {
+         Object backVal = backed.get(entry.getKey());
+         assert backVal != null : "null backVal for " + entry.getKey();
+         assert backVal.equals(entry.getValue()) : "differing val for " + entry.getKey() + " " + entry.getValue() + " vs. " + backVal;
+      }
+      
+      assertMainTreeClear(contextHostName, sessionId, backup);
+      assertBuddyTreeClear(contextHostName, sessionId, owner);
+   }
+   
+   public static void assertUnrelated(String contextHostName, String sessionId, Cache<Object, Object> cache)
+   {
+      assertMainTreeClear(contextHostName, sessionId, cache);
+      assertBuddyTreeClear(contextHostName, sessionId, cache);
+   }
+   
+   public static void assertMainTreeClear(String contextHostName, String sessionId, Cache<Object, Object> cache)
+   {
+      Fqn<String> fqn = Fqn.fromElements(FqnUtil.JSESSION, contextHostName, sessionId);
+      assert cache.getNode(fqn) == null : "found node for " + fqn;
+   }
+   
+   public static void assertBuddyTreeClear(String contextHostName, String sessionId, Cache<Object, Object> cache)
+   {
+      Fqn<String> fqn = Fqn.fromElements(FqnUtil.JSESSION, contextHostName, sessionId);
+      Node<Object, Object> bbRoot = cache.getNode(BuddyManager.BUDDY_BACKUP_SUBTREE_FQN);
+      if (bbRoot != null)
+      {
+         for(Node<Object, Object> child : bbRoot.getChildren())
+         {
+            Node<Object, Object> bad = child.getChild(fqn);
+            assert bad == null : "found bad node at " + Fqn.fromRelativeFqn(child.getFqn(), fqn);
+         }
+      }
+   }
+   
+   private BuddyReplicationAssertions() {}
+}

Added: core/trunk/src/test/java/org/jboss/cache/integration/websession/util/DirtyMetadataServlet.java
===================================================================
--- core/trunk/src/test/java/org/jboss/cache/integration/websession/util/DirtyMetadataServlet.java	                        (rev 0)
+++ core/trunk/src/test/java/org/jboss/cache/integration/websession/util/DirtyMetadataServlet.java	2008-11-24 07:01:36 UTC (rev 7186)
@@ -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.cache.integration.websession.util;
+
+
+/**
+ * A Servlet the marks the session metadata dirty.
+ * 
+ * @author Brian Stansberry
+ */
+public class DirtyMetadataServlet extends AbstractServlet
+{
+  public void handleRequest(Request request)
+   {
+      extractSession(request).setMetadataDirty();
+   }
+
+}

Added: core/trunk/src/test/java/org/jboss/cache/integration/websession/util/FqnUtil.java
===================================================================
--- core/trunk/src/test/java/org/jboss/cache/integration/websession/util/FqnUtil.java	                        (rev 0)
+++ core/trunk/src/test/java/org/jboss/cache/integration/websession/util/FqnUtil.java	2008-11-24 07:01:36 UTC (rev 7186)
@@ -0,0 +1,135 @@
+/*
+ * 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.cache.integration.websession.util;
+
+import org.jboss.cache.Fqn;
+import org.jboss.cache.buddyreplication.BuddyManager;
+
+/**
+ * @author Brian Stansberry
+ *
+ */
+public class FqnUtil
+{
+   public static final String JSESSION = "JSESSION";
+   public static final String ATTRIBUTE = "ATTRIBUTE";
+   private static final int JSESSION_FQN_INDEX = 0;
+   private static final int WEBAPP_FQN_INDEX = 1;
+   private static final int SESSION_ID_FQN_INDEX = 2;
+   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();
+   private static final int BUDDY_BACKUP_ROOT_OWNER_SIZE = BUDDY_BACKUP_ROOT_OWNER_INDEX + 1;
+   // 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;
+   
+   public static boolean isBuddyFqn(Fqn<String> 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;
+      }      
+   }
+
+   public static boolean isFqnSessionRootSized(Fqn<String> fqn, boolean isBuddy)
+   {
+      return fqn.size() == (isBuddy ? BUDDY_BACKUP_ROOT_OWNER_SIZE + SESSION_FQN_SIZE : SESSION_FQN_SIZE);
+   }
+   
+   public static String getPojoKeyFromFqn(Fqn<String> 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
+    * ++ /contextPath_hostname
+    * ++++ /sessionid
+    * ++++++ /ATTRIBUTE
+    * ++++++ /_JBossInternal_
+    * ++++++++ etc etc
+    * 
+    * If the Fqn size is big enough to be "etc etc" or lower, but the 4th
+    * level is not "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<String> fqn)
+   {      
+      return (fqn.size() > POJO_INTERNAL_FQN_SIZE 
+            && ATTRIBUTE.equals(fqn.get(POJO_INTERNAL_FQN_INDEX)) == false);
+   }
+   
+   public static String getIdFromFqn(Fqn<String> fqn, boolean isBuddy)
+   {
+      return (String)fqn.get(isBuddy ? BUDDY_BACKUP_ROOT_OWNER_SIZE + SESSION_ID_FQN_INDEX : SESSION_ID_FQN_INDEX);
+   }
+
+   /**
+    * 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<String> fqn)
+   {
+      return (String) fqn.get(BUDDY_BACKUP_ROOT_OWNER_INDEX);     
+   }
+
+   public static boolean isFqnForOurWebapp(Fqn<String> fqn, String contextHostPath, boolean isBuddy)
+   {
+      try
+      {
+         if (contextHostPath.equals(fqn.get(isBuddy ? BUDDY_BACKUP_ROOT_OWNER_SIZE + WEBAPP_FQN_INDEX : WEBAPP_FQN_INDEX))
+               && JSESSION.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;
+   }
+
+   private FqnUtil() {}
+}

Added: core/trunk/src/test/java/org/jboss/cache/integration/websession/util/GetAttributesServlet.java
===================================================================
--- core/trunk/src/test/java/org/jboss/cache/integration/websession/util/GetAttributesServlet.java	                        (rev 0)
+++ core/trunk/src/test/java/org/jboss/cache/integration/websession/util/GetAttributesServlet.java	2008-11-24 07:01:36 UTC (rev 7186)
@@ -0,0 +1,58 @@
+/*
+ * 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.cache.integration.websession.util;
+
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * A Servlet that reads attributes from the session.
+ * 
+ * @author Brian Stansberry
+ */
+public class GetAttributesServlet extends AbstractServlet
+{
+   private final Set<String> toGet;
+   private final Map<String, Object> readAttributes = new HashMap<String, Object>();
+   
+   public GetAttributesServlet(Set<String> toGet)
+   {
+      this.toGet = new HashSet<String>(toGet);
+   }
+
+   public void handleRequest(Request request)
+   {
+      Session session = extractSession(request);
+      for (String key : toGet)
+      {
+         readAttributes.put(key, session.getAttribute(key));
+      }
+   }
+   public Map<String, Object> getReadAttributes()
+   {
+      return readAttributes;
+   }
+
+}

Added: core/trunk/src/test/java/org/jboss/cache/integration/websession/util/InvalidationServlet.java
===================================================================
--- core/trunk/src/test/java/org/jboss/cache/integration/websession/util/InvalidationServlet.java	                        (rev 0)
+++ core/trunk/src/test/java/org/jboss/cache/integration/websession/util/InvalidationServlet.java	2008-11-24 07:01:36 UTC (rev 7186)
@@ -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.cache.integration.websession.util;
+
+
+/**
+ * A Servlet that invalidates the session.
+ * 
+ * @author Brian Stansberry
+ */
+public class InvalidationServlet extends AbstractServlet
+{
+  public void handleRequest(Request request)
+   {
+      extractSession(request).invalidate();
+   }
+
+}

Added: core/trunk/src/test/java/org/jboss/cache/integration/websession/util/MultipleActionServlet.java
===================================================================
--- core/trunk/src/test/java/org/jboss/cache/integration/websession/util/MultipleActionServlet.java	                        (rev 0)
+++ core/trunk/src/test/java/org/jboss/cache/integration/websession/util/MultipleActionServlet.java	2008-11-24 07:01:36 UTC (rev 7186)
@@ -0,0 +1,51 @@
+/*
+ * 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.cache.integration.websession.util;
+
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * A Servlet that call other servlets in order.
+ * 
+ * @author Brian Stansberry
+ */
+public class MultipleActionServlet extends AbstractServlet
+{
+   private final List<Servlet> actions;
+   
+   public MultipleActionServlet(Servlet... action)
+   {
+      this.actions = Arrays.asList(action);
+   }
+
+   public void handleRequest(Request request)
+   {
+      extractSession(request);
+      for (Servlet action : actions)
+      {
+         action.handleRequest(request);
+      }
+   }
+
+}

Added: core/trunk/src/test/java/org/jboss/cache/integration/websession/util/RemoveAttributesServlet.java
===================================================================
--- core/trunk/src/test/java/org/jboss/cache/integration/websession/util/RemoveAttributesServlet.java	                        (rev 0)
+++ core/trunk/src/test/java/org/jboss/cache/integration/websession/util/RemoveAttributesServlet.java	2008-11-24 07:01:36 UTC (rev 7186)
@@ -0,0 +1,51 @@
+/*
+ * 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.cache.integration.websession.util;
+
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * A Servlet that removes attributes from the session.
+ * 
+ * @author Brian Stansberry
+ */
+public class RemoveAttributesServlet extends AbstractServlet
+{
+   private final Set<String> toRemove;
+   
+   public RemoveAttributesServlet(Set<String> toRemove)
+   {
+      this.toRemove = new HashSet<String>(toRemove);
+   }
+
+   public void handleRequest(Request request)
+   {
+      Session session = extractSession(request);
+      for (String key : toRemove)
+      {
+         session.removeAttribute(key);
+      }
+   }
+
+}

Added: core/trunk/src/test/java/org/jboss/cache/integration/websession/util/Request.java
===================================================================
--- core/trunk/src/test/java/org/jboss/cache/integration/websession/util/Request.java	                        (rev 0)
+++ core/trunk/src/test/java/org/jboss/cache/integration/websession/util/Request.java	2008-11-24 07:01:36 UTC (rev 7186)
@@ -0,0 +1,91 @@
+/*
+ * 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.cache.integration.websession.util;
+
+/**
+ * Mocks the JBoss AS clustered webapp request handling.
+ * 
+ * @author Brian Stansberry
+ */
+public class Request
+{
+   private final SessionManager manager;
+   private final String sessionId;
+   private final Servlet servlet;
+   private Session session;
+   
+   public Request(SessionManager manager, String sessionId, Servlet servlet)
+   {
+      this.manager = manager;
+      this.sessionId = sessionId;
+      this.servlet = servlet;
+   }
+   
+   public void execute()
+   {
+      // Gravitate the session outside of the batch
+      getSession(false);
+      
+      manager.startBatch();
+      try
+      {
+         servlet.handleRequest(this);
+      }
+      finally
+      {
+         try
+         {
+            if (session != null && session.isValid())
+               session.store();
+         }
+         finally
+         {
+            manager.endBatch();
+         }
+      }
+   }
+   
+   /**
+    * Meant for internal use in this class and by a Servlet; test driver
+    * should get a session ref from a Servlet impl.
+    */
+   public Session getSession(boolean create)
+   {
+      if (session == null && sessionId != null)
+      {
+         session = manager.findSession(sessionId);         
+      }
+      
+      if (session != null && !session.isValid())
+         session = null;
+      
+      if (create && session == null)
+         session = manager.createSession();
+      
+      if (session != null)
+         session.access();
+      
+      return session;
+   }
+
+}

Added: core/trunk/src/test/java/org/jboss/cache/integration/websession/util/Servlet.java
===================================================================
--- core/trunk/src/test/java/org/jboss/cache/integration/websession/util/Servlet.java	                        (rev 0)
+++ core/trunk/src/test/java/org/jboss/cache/integration/websession/util/Servlet.java	2008-11-24 07:01:36 UTC (rev 7186)
@@ -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.cache.integration.websession.util;
+
+/**
+ * Implementations of this interface will actually do something with
+ * the session.
+ * 
+ * @author Brian Stansberry
+ */
+public interface Servlet
+{
+   void handleRequest(Request request);
+}

Added: core/trunk/src/test/java/org/jboss/cache/integration/websession/util/Session.java
===================================================================
--- core/trunk/src/test/java/org/jboss/cache/integration/websession/util/Session.java	                        (rev 0)
+++ core/trunk/src/test/java/org/jboss/cache/integration/websession/util/Session.java	2008-11-24 07:01:36 UTC (rev 7186)
@@ -0,0 +1,200 @@
+/*
+ * 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.cache.integration.websession.util;
+
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.atomic.AtomicLong;
+
+/**
+ * Mock version of a JBoss AS clustered session.
+ * 
+ * @author Brian Stansberry
+ */
+public class Session
+{
+   private volatile SessionMetadata metadata = new SessionMetadata();
+   private final AtomicInteger version = new AtomicInteger();
+   private final AtomicLong timestamp = new AtomicLong(metadata.creationTime);
+   private final Map<String, Object> attributes = new ConcurrentHashMap<String, Object>();
+   private final Set<String> modifiedKeys = new HashSet<String>();
+   private final Set<String> removedKeys = new HashSet<String>();
+   private final SessionManager manager;
+   private boolean outdated;
+   private boolean metadataDirty = true;
+   
+   public Session(SessionManager manager)
+   {
+      this.manager = manager;
+   }
+   
+   public String getId()
+   {
+      return metadata.id;
+   }
+   
+   public long getCreationTime()
+   {
+      return metadata.creationTime;
+   }
+   
+   public boolean isValid()
+   {
+      return metadata.valid;
+   }
+   
+   public void invalidate()
+   {
+      this.metadata.valid = false;
+      manager.removeSession(getId(), false, true);
+   }
+   
+   public Object setAttribute(String key, Object value)
+   {
+      modifiedKeys.add(key);
+      removedKeys.remove(key);
+      return attributes.put(key, value);
+   }
+   
+   public Object removeAttribute(String key)
+   {
+      removedKeys.add(key);
+      modifiedKeys.remove(key);
+      return attributes.remove(key);
+   }
+   
+   public Object getAttribute(String key)
+   {
+      return attributes.get(key);
+   }
+
+   public AtomicLong getTimestamp()
+   {
+      return timestamp;
+   }
+   
+   public void setMetadataDirty()
+   {
+      this.metadataDirty = true;
+   }
+
+   public void access()
+   {
+      this.timestamp.set(System.currentTimeMillis());      
+   } 
+   
+   public void store()
+   {
+      version.incrementAndGet();
+      
+      switch (manager.getGranularity())
+      {
+         case SESSION:
+            manager.storeSession(getId(), version, timestamp, getMetadataForReplication(), getAttributesForReplication());
+            break;
+         case ATTRIBUTE:
+            Map<String, Object> modified = new HashMap<String, Object>();
+            for (String key : modifiedKeys)
+            {
+               modified.put(key, attributes.get(key));
+            }
+            manager.storeSession(getId(), version, timestamp, getMetadataForReplication(), modified, removedKeys);
+            break;
+         case FIELD:
+            throw new UnsupportedOperationException("implement me");            
+      }
+      this.metadataDirty = false;
+   }
+
+   public SessionManager getManager()
+   {
+      return manager;
+   }
+
+   protected Map<String, Object> getAttributes()
+   {
+      return attributes;
+   }
+
+   protected SessionMetadata getMetadata()
+   {
+      return metadata;
+   }
+
+   protected AtomicInteger getVersion()
+   {
+      return version;
+   }
+
+   protected Map<String, Object> getAttributesForReplication()
+   {
+      return attributes;
+   }
+   
+   private SessionMetadata getMetadataForReplication()
+   {
+      return metadataDirty ? metadata : null;
+   }
+   
+   protected void replicateAttributeChanges()
+   {
+      // no-op
+   }
+
+   protected boolean isOutdated()
+   {
+      return outdated;
+   }
+
+   protected void setOutdated(boolean outdated)
+   {
+      this.outdated = outdated;
+   }
+
+   protected void update(AtomicInteger version, AtomicLong timestamp, SessionMetadata metadata,
+         Map<String, Object> attributes)
+   {
+      if (version == null)
+         throw new IllegalArgumentException("version is null");
+      if (timestamp == null)
+         throw new IllegalArgumentException("timestamp is null");
+      if (metadata == null)
+         throw new IllegalArgumentException("metadata is null");
+      if (attributes == null)
+         throw new IllegalArgumentException("attributes is null");
+      
+      this.version.set(version.get());
+      this.timestamp.set(version.get());
+      this.metadata = metadata;
+      
+      this.attributes.clear();
+      this.attributes.putAll(attributes);
+      
+      this.outdated = false;
+   }  
+   
+}

Added: core/trunk/src/test/java/org/jboss/cache/integration/websession/util/SessionManager.java
===================================================================
--- core/trunk/src/test/java/org/jboss/cache/integration/websession/util/SessionManager.java	                        (rev 0)
+++ core/trunk/src/test/java/org/jboss/cache/integration/websession/util/SessionManager.java	2008-11-24 07:01:36 UTC (rev 7186)
@@ -0,0 +1,530 @@
+/*
+ * 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.cache.integration.websession.util;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.atomic.AtomicLong;
+
+import javax.transaction.SystemException;
+import javax.transaction.TransactionManager;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.jboss.cache.Cache;
+import org.jboss.cache.CacheManager;
+import org.jboss.cache.CacheStatus;
+import org.jboss.cache.Fqn;
+import org.jboss.cache.config.BuddyReplicationConfig;
+import org.jboss.cache.integration.websession.util.WebAppMetadata.Granularity;
+import org.jboss.cache.notifications.annotation.CacheListener;
+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;
+
+/**
+ * Mock version of a JBoss AS web session manager.
+ * 
+ * @author Brian Stansberry
+ */
+ at CacheListener
+public class SessionManager
+{
+   
+   private static final Integer VERSION = Integer.valueOf(0);
+   private static final Integer TIMESTAMP = Integer.valueOf(1);
+   private static final Integer METADATA = Integer.valueOf(2);
+   private static final Integer ATTRIBUTES = Integer.valueOf(3);
+   
+   private final CacheManager cacheManager;
+   private final WebAppMetadata appMetadata;
+   private final String contextHostName;
+   private final Fqn<String> baseFqn;
+   private final Map<String, Session> sessions = new ConcurrentHashMap<String, Session>(); 
+   private Cache<Object, Object> cache;
+   private TransactionManager tm;
+   private boolean buddyReplication;
+   private boolean started;
+   private final boolean fieldBased;
+   private final boolean attributeBased;
+   private final Log log = LogFactory.getLog(getClass());
+   
+   // ------------------------------------------------------------ Constructors
+   
+   public SessionManager(CacheManager cacheManager, WebAppMetadata metadata)
+   {
+      this.cacheManager = cacheManager;
+      this.appMetadata = metadata;
+      this.contextHostName = appMetadata.warName + "_localhost";
+      this.baseFqn = Fqn.fromElements(FqnUtil.JSESSION, contextHostName);
+      this.fieldBased = appMetadata.granularity == Granularity.FIELD;
+      this.attributeBased = appMetadata.granularity == Granularity.ATTRIBUTE;
+   }
+   
+   // --------------------------------------------------------- Test Driver API
+   
+   public boolean isStarted()
+   {
+      return started;
+   }
+   
+   public void start()
+   {
+      this.started = true;;
+      
+      try
+      {
+         this.cache = cacheManager.getCache(appMetadata.cacheConfigName, false);
+      }
+      catch (RuntimeException e)
+      {
+         throw e;
+      }
+      catch (Exception e)
+      {
+         throw new RuntimeException(e);
+      }
+      
+      cache.addCacheListener(this);
+      
+      if (cache.getCacheStatus() != CacheStatus.STARTED)
+      {
+         cache.start();
+      }
+      this.tm = cache.getConfiguration().getRuntimeConfig().getTransactionManager();
+      if (tm == null)
+      {
+         throw new IllegalStateException("tm is null");
+      }
+      BuddyReplicationConfig brc = cache.getConfiguration().getBuddyReplicationConfig();
+      this.buddyReplication = brc != null && brc.isEnabled();      
+   }
+   
+   public void stop()
+   {     
+      if (started)
+      {
+         
+      
+         if (cache != null)
+         {
+            cache.removeCacheListener(this);
+            
+            // FIXME see if we need more sophisticated cache cleanup
+            cache.getInvocationContext().getOptionOverrides().setCacheModeLocal(true);
+            cache.removeNode(baseFqn);
+            cacheManager.releaseCache(appMetadata.cacheConfigName);
+            cache = null;
+         }
+      }
+   }
+   
+   /**
+    * Allows test driver to mock Tomcat background processes' expiration
+    * of an overage session.
+    * 
+    * FIXME deal with buddy backup tree
+    * 
+    * @param id the session id
+    */
+   public void expireSession(String id)
+   {
+      Session session = removeSession(id, true, true);
+      if (session != null)
+         session.getMetadata().valid = false;
+   }
+   
+   /**
+    * Allows test driver to mock Tomcat background processes' passivation
+    * of a session.
+    * 
+    * FIXME deal with buddy backup tree
+    * 
+    * @param id the session id
+    */
+   public void passivate(String id)
+   {
+      if (!appMetadata.passivation)
+         throw new IllegalStateException("passivation not supported");
+      
+      sessions.remove(id);
+      
+      cache.evict(getSessionFqn(id), true);
+   }
+   
+   public Cache<Object, Object> getCache()
+   {
+      return cache;
+   }
+   
+   public String getContextHostName()
+   {
+      return contextHostName;
+   }
+   
+   
+   
+   // ----------------------------------------------------------- CacheListener
+   
+
+   @NodeRemoved
+   public void nodeRemoved(NodeRemovedEvent event)
+   {      
+      if (event.isPre())
+         return;
+      
+      boolean local = event.isOriginLocal();
+      if (!fieldBased && local)
+         return;
+      
+      @SuppressWarnings("unchecked")
+      Fqn<String> fqn = event.getFqn();
+      boolean isBuddy = FqnUtil.isBuddyFqn(fqn);
+      
+      if (!local 
+            && FqnUtil.isFqnSessionRootSized(fqn, isBuddy) 
+            && FqnUtil.isFqnForOurWebapp(fqn, contextHostName, isBuddy))
+      {
+         // A session has been invalidated from another node;
+         // need to inform manager
+         String sessId = FqnUtil.getIdFromFqn(fqn, isBuddy);
+         removeSession(sessId, true, false);
+      }
+      else if (local && !isBuddy
+                  && FqnUtil.isPossibleInternalPojoFqn(fqn) 
+                  && FqnUtil.isFqnForOurWebapp(fqn, contextHostName, isBuddy))
+      {
+         // One of our sessions' pojos is modified; need to inform
+         // the manager so it can mark the session dirty
+         String sessId = FqnUtil.getIdFromFqn(fqn, isBuddy);
+         notifyLocalAttributeModification(sessId);
+      }
+   }
+
+   @NodeModified
+   public void nodeModified(NodeModifiedEvent event)
+   {      
+      if (event.isPre())
+         return;
+      
+      boolean local = event.isOriginLocal();
+      if (!fieldBased && local)
+         return;
+      
+      @SuppressWarnings("unchecked")
+      Fqn<String> fqn = event.getFqn();
+      boolean isBuddy = FqnUtil.isBuddyFqn(fqn);      
+      
+      if (!local 
+             && FqnUtil.isFqnSessionRootSized(fqn, isBuddy)
+             && FqnUtil.isFqnForOurWebapp(fqn, contextHostName, isBuddy))
+      {
+         // Query if we have version value in the distributed cache. 
+         // If we have a version value, compare the version and invalidate if necessary.
+         @SuppressWarnings("unchecked")
+         Map<Object, Object> data = event.getData();
+         AtomicInteger version = (AtomicInteger) data.get(VERSION);
+         if(version != null)
+         {
+            String realId = FqnUtil.getIdFromFqn(fqn, isBuddy);
+            String owner = isBuddy ? FqnUtil.getBuddyOwner(fqn) : null;
+            AtomicLong timestamp = (AtomicLong) data.get(TIMESTAMP);
+            if (timestamp == null)
+            {
+               log.warn("No timestamp attribute found in " + fqn);
+            }
+            else
+            {
+               // Notify the manager that a session has been updated
+               boolean updated = sessionChangedInDistributedCache(realId, owner, 
+                                                  version.get());
+               if (!updated && !isBuddy)
+               {
+                  log.warn("Possible concurrency problem: Replicated version id " + 
+                            version + " is less than or equal to 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 if (!attributeBased) // other granularities can modify attributes only
+         {
+            log.warn("No version attribute found in " + fqn);
+         }
+      }
+      else if (local && !isBuddy
+            && FqnUtil.isPossibleInternalPojoFqn(fqn) 
+            && FqnUtil.isFqnForOurWebapp(fqn, contextHostName, isBuddy))
+      {
+         // One of our sessions' pojos is modified; need to inform
+         // the manager so it can mark the session dirty
+         String sessId = FqnUtil.getIdFromFqn(fqn, isBuddy);
+         notifyLocalAttributeModification(sessId);
+      }
+   }
+
+   // ------------------------------------------------------------ Internal API
+   
+   protected boolean isBatchStarted()
+   {
+      try
+      {
+         return tm.getTransaction() != null;
+      }
+      catch (SystemException e)
+      {
+         throw new RuntimeException("failed checking for tx", e);
+      }
+   }
+   
+   protected void startBatch()
+   {
+      try
+      {
+         tm.begin();
+      }
+      catch (Exception e)
+      {
+         throw new RuntimeException("failed starting tx", e);
+      }
+   }
+   
+   protected void endBatch()
+   {
+      try
+      {
+         tm.commit();
+      }
+      catch (Exception e)
+      {
+         throw new RuntimeException("failed committing tx", e);
+      }
+   }
+   
+   protected Session findSession(String id)
+   {
+      Session session = sessions.get(id);
+      if (session == null || session.isOutdated())
+      {
+         session = loadSession(id);
+         if (session != null)
+         {
+            sessions.put(id, session);
+         }
+      }
+      return session;
+   }
+
+   protected Session createSession()
+   {
+      Session session = createEmptySession();
+      sessions.put(session.getId(), session);
+      return session;
+   }
+   
+   protected Session removeSession(String id, boolean localOnly, boolean localCall)
+   {
+      Session session = sessions.remove(id);
+      if (localCall)
+      {
+         // TODO mock the bit where each individual attribute is removed first
+         
+         // Remove the session node
+         if (localOnly)
+         {
+            cache.getInvocationContext().getOptionOverrides().setCacheModeLocal(true);
+         }
+         cache.removeNode(getSessionFqn(id));
+      }
+      
+      return session;
+   }
+   
+   protected Granularity getGranularity()
+   {
+      return appMetadata.granularity;
+   }
+   
+   private Fqn<String> getSessionFqn(String id)
+   {
+      return Fqn.fromRelativeElements(baseFqn, id);
+   }
+
+   private Session createEmptySession()
+   {
+      Session session = null;
+      switch (appMetadata.granularity)
+      {
+         case SESSION:
+         case ATTRIBUTE:
+            session = new Session(this);
+            break;
+         case FIELD:
+            throw new IllegalStateException("implement");
+      }
+      return session;
+   }
+   
+   /**
+    * JBC read of a session.
+    */
+   private Session loadSession(String id)
+   {
+      Session session = null;
+      
+      boolean startTx = !isBatchStarted();      
+      if (startTx)
+         startBatch();
+      Map<Object, Object> data = null;
+      try
+      {
+         if (buddyReplication)
+         {
+            cache.getInvocationContext().getOptionOverrides().setForceDataGravitation(true);
+         }
+         data = cache.getData(getSessionFqn(id));
+      }
+      finally
+      {
+         if (startTx)
+            endBatch();
+      }
+      
+      if (data != null)
+      {
+         session = createEmptySession();
+         AtomicInteger version = (AtomicInteger) data.get(VERSION);
+         AtomicLong timestamp = (AtomicLong) data.get(TIMESTAMP);
+         SessionMetadata metadata = (SessionMetadata) data.get(METADATA);
+         Map<String, Object> attributes = loadSessionAttributes(id, data);
+         session.update(version, timestamp, metadata, attributes);
+      }
+      return session;
+   }
+   
+   @SuppressWarnings("unchecked")
+   protected Map<String, Object> loadSessionAttributes(String id, Map<Object, Object> sessionNodeData)
+   {
+      Map<String, Object> result = new HashMap<String, Object>();
+      switch (appMetadata.granularity)
+      {
+         case SESSION:
+            result.putAll((Map<String, Object>) sessionNodeData.get(ATTRIBUTES));
+            break;
+         case ATTRIBUTE:
+            for (Map.Entry<Object, Object> entry : sessionNodeData.entrySet())
+            {
+               if (entry.getKey() instanceof String)
+               {
+                  result.put((String) entry.getKey(), entry.getValue());
+               }
+            }
+            break;
+         case FIELD:
+            throw new IllegalStateException("implement");
+      }
+      
+      return result;
+   }
+   
+   /**
+    * JBC write for granularity SESSION.
+    */
+   protected void storeSession(String id, AtomicInteger version, AtomicLong timestamp, SessionMetadata metadata, Map<String, Object> attributes)
+   {
+      if (version == null)
+         throw new IllegalArgumentException("version is null");
+      if (timestamp == null)
+         throw new IllegalArgumentException("timestamp is null");
+      
+      Fqn<String> fqn = getSessionFqn(id);
+      
+      Map<Object, Object> data = new HashMap<Object, Object>();
+      data.put(VERSION, version);
+      data.put(TIMESTAMP, timestamp);
+      if (metadata != null)
+      {
+         data.put(METADATA, metadata);
+      }
+      if (attributes != null)
+      {
+         data.put(ATTRIBUTES, attributes);         
+      }
+      
+      cache.put(fqn, data);
+   }
+   
+   /**
+    * JBC write for granularity ATTRIBUTE.
+    */
+   protected void storeSession(String id, AtomicInteger version, AtomicLong timestamp, SessionMetadata metadata, Map<String, Object> modifiedAttributes, Set<String> removedAttributes)
+   {
+      storeSession(id, version, timestamp, metadata, null);
+      
+      Fqn<String> fqn = getSessionFqn(id);
+      
+      if (modifiedAttributes != null)
+      {      
+         cache.put(fqn, modifiedAttributes);
+      }
+      
+      if (removedAttributes != null)
+      {
+         for (String key : removedAttributes)
+         {
+            cache.remove(fqn, key);
+         }
+      }
+   }
+   
+   private boolean sessionChangedInDistributedCache(String realId, String owner, int version)
+   {
+      Session session = sessions.get(realId);
+      if (session != null)
+      {
+         session.setOutdated(true);
+         if (session.getVersion().get() >= version)
+            return false;
+      }
+      return true;
+   }
+   
+   private void notifyLocalAttributeModification(String sessId)
+   {
+      Session session = sessions.get(sessId);
+      if (session != null)
+      {
+         session.setOutdated(true);
+      }      
+   }
+
+}

Added: core/trunk/src/test/java/org/jboss/cache/integration/websession/util/SessionManagerSupport.java
===================================================================
--- core/trunk/src/test/java/org/jboss/cache/integration/websession/util/SessionManagerSupport.java	                        (rev 0)
+++ core/trunk/src/test/java/org/jboss/cache/integration/websession/util/SessionManagerSupport.java	2008-11-24 07:01:36 UTC (rev 7186)
@@ -0,0 +1,73 @@
+/*
+ * 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.cache.integration.websession.util;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.jboss.cache.CacheManager;
+
+/**
+ * @author Brian Stansberry
+ *
+ */
+public class SessionManagerSupport
+{
+   /**
+    * For each thread holds list of session managers created using this factory.
+    */
+   private static final ThreadLocal<List<SessionManager>> threadSessionManagers =
+         new ThreadLocal<List<SessionManager>>()
+         {
+            @Override
+            protected List<SessionManager> initialValue()
+            {
+               return new ArrayList<SessionManager>();
+            }
+         };
+         
+  public static List<SessionManager> createSessionManagers(List<CacheManager> cacheManagers, WebAppMetadata metadata)
+  {
+     List<SessionManager> existing = threadSessionManagers.get();
+     existing.clear();     
+     for (CacheManager cm : cacheManagers)
+     {
+        SessionManager sm = new SessionManager(cm, metadata);
+        existing.add(sm);
+     }
+     
+     return new ArrayList<SessionManager>(existing);
+  }
+  
+  public static void tearDown()
+  {
+     List<SessionManager> existing = threadSessionManagers.get();
+     for (SessionManager sm : existing)
+     {
+        if (sm.isStarted())
+           sm.stop();
+     }
+     existing.clear();
+  }
+
+}

Added: core/trunk/src/test/java/org/jboss/cache/integration/websession/util/SessionMetadata.java
===================================================================
--- core/trunk/src/test/java/org/jboss/cache/integration/websession/util/SessionMetadata.java	                        (rev 0)
+++ core/trunk/src/test/java/org/jboss/cache/integration/websession/util/SessionMetadata.java	2008-11-24 07:01:36 UTC (rev 7186)
@@ -0,0 +1,42 @@
+/*
+ * 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.cache.integration.websession.util;
+
+import java.io.Serializable;
+
+/**
+ * Metadata about a session that is only replicated infrequently.
+ * 
+ * @author Brian Stansberry
+ */
+public class SessionMetadata implements Serializable
+{
+   /** The serialVersionUID */
+   private static final long serialVersionUID = 1L;
+   
+   private static volatile int counter;
+   
+   public final String id = String.valueOf(++counter);
+   public final long creationTime =  System.currentTimeMillis();
+   public boolean valid = true;
+}

Added: core/trunk/src/test/java/org/jboss/cache/integration/websession/util/SetAttributesServlet.java
===================================================================
--- core/trunk/src/test/java/org/jboss/cache/integration/websession/util/SetAttributesServlet.java	                        (rev 0)
+++ core/trunk/src/test/java/org/jboss/cache/integration/websession/util/SetAttributesServlet.java	2008-11-24 07:01:36 UTC (rev 7186)
@@ -0,0 +1,51 @@
+/*
+ * 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.cache.integration.websession.util;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * A Servlet that sets attributes on the session.
+ * 
+ * @author Brian Stansberry
+ */
+public class SetAttributesServlet extends AbstractServlet
+{
+   private final Map<String, Object> toSet;
+   
+   public SetAttributesServlet(Map<String, Object> toSet)
+   {
+      this.toSet = new HashMap<String, Object>(toSet);
+   }
+
+   public void handleRequest(Request request)
+   {
+      Session session = extractSession(request);
+      for (Map.Entry<String, Object> entry : toSet.entrySet())
+      {
+         session.setAttribute(entry.getKey(), entry.getValue());
+      }
+   }
+
+}

Added: core/trunk/src/test/java/org/jboss/cache/integration/websession/util/WebAppMetadata.java
===================================================================
--- core/trunk/src/test/java/org/jboss/cache/integration/websession/util/WebAppMetadata.java	                        (rev 0)
+++ core/trunk/src/test/java/org/jboss/cache/integration/websession/util/WebAppMetadata.java	2008-11-24 07:01:36 UTC (rev 7186)
@@ -0,0 +1,53 @@
+/*
+ * 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.cache.integration.websession.util;
+
+/**
+ * Metadata about a webapp; mocks the object created from a jboss-web.xml.
+ * 
+ * @author Brian Stansberry
+ */
+public class WebAppMetadata
+{
+   public static final String DEFAULT_CACHE_CONFIG = "standard-session-cache";
+   
+   public enum Granularity { SESSION, ATTRIBUTE, FIELD };
+   
+   public final String warName;
+   public final String cacheConfigName;
+   public final boolean passivation;
+   public final Granularity granularity;
+   
+   public WebAppMetadata(String warName)
+   {
+      this(warName, DEFAULT_CACHE_CONFIG, true, Granularity.SESSION);
+   }
+   
+   public WebAppMetadata(String warName, String cacheConfigName, boolean passivation, Granularity granularity)
+   {
+      this.warName = warName;
+      this.cacheConfigName = cacheConfigName;
+      this.passivation = passivation;
+      this.granularity = granularity;
+   }
+}

Added: core/trunk/src/test/java/org/jboss/cache/integration/websession/util/WebSessionTestBase.java
===================================================================
--- core/trunk/src/test/java/org/jboss/cache/integration/websession/util/WebSessionTestBase.java	                        (rev 0)
+++ core/trunk/src/test/java/org/jboss/cache/integration/websession/util/WebSessionTestBase.java	2008-11-24 07:01:36 UTC (rev 7186)
@@ -0,0 +1,157 @@
+/*
+ * 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.cache.integration.websession.util;
+
+import java.util.List;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import org.jboss.cache.Cache;
+import org.jboss.cache.CacheManager;
+import org.jboss.cache.CacheStatus;
+import org.jboss.cache.integration.CacheManagerSupport;
+import org.jboss.cache.integration.UnitTestCacheFactoryConfigurationRegistry;
+import org.jboss.cache.integration.websession.util.WebAppMetadata.Granularity;
+import org.testng.annotations.AfterClass;
+import org.testng.annotations.AfterMethod;
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.BeforeMethod;
+
+/**
+ * Base class that handles standard before/after class/method stuff.
+ * 
+ * @author Brian Stansberry
+ */
+public abstract class WebSessionTestBase
+{
+   public static final String DEFAULT_CACHE_CONFIG_FILE_NAME = "configs/integration/web-session-cache-configs.xml";
+
+   private AtomicInteger testCount = new AtomicInteger();
+   private List<CacheManager> cacheManagers;
+   private List<SessionManager> sessionManagers;
+   
+   @BeforeClass(alwaysRun = true)
+   public void beforeClass() throws Exception
+   {
+      cacheManagers = CacheManagerSupport.createCacheManagers(getNumCacheManagers(), getCacheConfigFileName(), getStacksXmlFileName());
+      if (getStartCachesInBeforeClass() && getCacheConfigName() != null)
+      {
+         for (CacheManager cm : cacheManagers)
+         {
+            Cache<Object, Object> cache = cm.getCache(getCacheConfigName(), true);
+            if (cache.getCacheStatus() != CacheStatus.STARTED)
+               cache.start();
+         }
+      }
+   }
+   
+   @AfterClass(alwaysRun = true)
+   public void afterClass()
+   {
+      CacheManagerSupport.tearDown();
+   }
+   
+   @BeforeMethod(alwaysRun = true)
+   public void setup()
+   {
+      testCount.incrementAndGet();
+      
+      if (getCreateManagersInSetup() && getWebAppMetaData() != null)
+      {
+         sessionManagers = SessionManagerSupport.createSessionManagers(cacheManagers, getWebAppMetaData());
+         for (SessionManager mgr : sessionManagers)
+         {
+            mgr.start();
+         }
+      }      
+   }
+   
+   @AfterMethod(alwaysRun = true)
+   public void tearDown() throws Exception
+   {
+      SessionManagerSupport.tearDown();
+   }
+   
+   protected abstract int getNumCacheManagers();
+   
+   protected String getCacheConfigFileName()
+   {
+      return DEFAULT_CACHE_CONFIG_FILE_NAME;
+   }
+   
+   protected String getStacksXmlFileName()
+   {
+      return UnitTestCacheFactoryConfigurationRegistry.DEFAULT_STACKS_XML_RESOURCE;
+   }
+   
+   protected boolean getStartCachesInBeforeClass()
+   {
+      return true;      
+   }
+   
+   protected abstract String getCacheConfigName();
+   
+   protected boolean getCreateManagersInSetup()
+   {
+      return false;
+   }
+   
+   protected boolean getUsePassivation()
+   {
+      return true;
+   }
+   
+   protected Granularity getGranularity()
+   {
+      return Granularity.SESSION;
+   }
+   
+   protected WebAppMetadata getWebAppMetaData()
+   {
+      return new WebAppMetadata(getWarNameForTest(), getCacheConfigName(), getUsePassivation(), getGranularity());
+   }
+   
+   protected List<CacheManager> getCacheManagers()
+   {
+      return cacheManagers;
+   }
+   
+   protected List<SessionManager> getSessionManagers()
+   {
+       return sessionManagers;
+   }
+   
+   protected int getTestCount()
+   {
+      return testCount.get();
+   }
+   
+   protected String getWarNameForTest()
+   {
+      return getClass().getSimpleName() + getTestCount();
+   }
+   
+   protected Object getAttributeValue(int value)
+   {
+      return Integer.valueOf(value);
+   }
+}

Added: core/trunk/src/test/resources/configs/integration/hibernate-cache-configs.xml
===================================================================
--- core/trunk/src/test/resources/configs/integration/hibernate-cache-configs.xml	                        (rev 0)
+++ core/trunk/src/test/resources/configs/integration/hibernate-cache-configs.xml	2008-11-24 07:01:36 UTC (rev 7186)
@@ -0,0 +1,741 @@
+<cache-configs>
+
+        <!-- 
+      | Following are JBoss Cache configurations suitable for different 
+      | Hibernate 2nd Level Cache uses (e.g. entities vs. queries).
+      |
+      | In all cases, TransactionManager configuration not required.
+      | Hibernate will plug in its own transaction manager integration. 
+    -->
+    
+    
+    <!-- A config appropriate for entity/collection caching. -->
+    <cache-config name="optimistic-entity">
+
+        <!-- Node locking scheme -->
+        <attribute name="NodeLockingScheme">OPTIMISTIC</attribute>
+
+        <!-- Mode of communication with peer caches.
+        
+             INVALIDATION_SYNC is highly recommended as the mode for use
+             with entity and collection caches.
+        -->
+        <attribute name="CacheMode">INVALIDATION_SYNC</attribute>
+
+        <!-- Name of cluster. Needs to be the same for all members, in order
+             to find each other -->
+        <attribute name="ClusterName">${jboss.partition.name:JBCTest}-optimistic-entity</attribute>
+        
+        <!-- Use a UDP (multicast) based stack. A udp-sync stack might be
+             slightly better (no JGroups FC) but we stick with udp to
+             help ensure this cache and others like timestamps-cache
+             that require FC can use the same underlying JGroups resources. -->
+        <attribute name="MultiplexerStack">${jboss.default.jgroups.stack:udp}</attribute>
+
+        <!-- Whether or not to fetch state on joining a cluster. -->
+        <attribute name="FetchInMemoryState">false</attribute>
+
+        <!--
+          The max amount of time (in milliseconds) we wait until the
+          state (ie. the contents of the cache) are retrieved from
+          existing members at startup. Ignored if FetchInMemoryState=false.
+        -->
+        <attribute name="StateRetrievalTimeout">20000</attribute>
+
+        <!--
+            Number of milliseconds to wait until all responses for a
+            synchronous call have been received.
+        -->
+        <attribute name="SyncReplTimeout">20000</attribute>
+
+        <!-- Max number of milliseconds to wait for a lock acquisition -->
+        <attribute name="LockAcquisitionTimeout">15000</attribute>
+
+       <!--
+          Indicate whether to use marshalling or not. Set this to true if you 
+          are running under a scoped class loader, e.g., inside an application 
+          server.
+       -->
+       <attribute name="UseRegionBasedMarshalling">true</attribute>
+       <!-- Must match the value of "useRegionBasedMarshalling" -->
+       <attribute name="InactiveOnStartup">true</attribute>
+
+       <!-- Disable asynchronous RPC marshalling/sending -->
+       <attribute name="SerializationExecutorPoolSize">0</attribute>
+        
+       <!-- We have no asynchronous notification listeners -->
+       <attribute name="ListenerAsyncPoolSize">0</attribute>
+
+       <!--  Eviction policy configurations. -->
+       <attribute name="EvictionPolicyConfig">
+        <config>
+          <attribute name="wakeUpIntervalSeconds">5</attribute>
+          <!-- Name of the DEFAULT eviction policy class. -->
+          <attribute name="policyClass">org.jboss.cache.eviction.LRUPolicy</attribute>
+          <!--  Cache wide default -->
+          <region name="/_default_">
+            <!-- Evict LRU node once we have more than this number of nodes -->
+            <attribute name="maxNodes">10000</attribute>
+            <!-- And, evict any node that hasn't been accessed in this many seconds -->
+            <attribute name="timeToLiveSeconds">1000</attribute>
+            <!-- Don't evict a node that's been accessed within this many seconds. 
+                 Set this to a value greater than your max expected transaction length. -->
+            <attribute name="minTimeToLiveSeconds">120</attribute>
+          </region>
+          <!--  Don't ever evict modification timestamps -->
+          <region name="/TS" policyClass="org.jboss.cache.eviction.NullEvictionPolicy"/>
+        </config>
+     </attribute>
+
+    </cache-config>
+
+    <!-- A config appropriate for entity/collection caching that
+         uses pessimistic locking -->
+    <cache-config name="pessimistic-entity">
+
+        <!-- Node locking scheme -->
+        <attribute name="NodeLockingScheme">PESSIMISTIC</attribute>
+
+        <!--
+            READ_COMMITTED is as strong as necessary for most 
+            2nd Level Cache use cases.
+        -->
+        <attribute name="IsolationLevel">READ_COMMITTED</attribute>
+
+        <!-- Mode of communication with peer caches.
+        
+             INVALIDATION_SYNC is highly recommended as the mode for use
+             with entity and collection caches.
+        -->
+        <attribute name="CacheMode">INVALIDATION_SYNC</attribute>
+
+        <!-- Name of cluster. Needs to be the same for all members, in order
+             to find each other -->
+        <attribute name="ClusterName">${jboss.partition.name:JBCTest}-pessimistic-entity</attribute>
+        
+        <!-- Use a UDP (multicast) based stack. A udp-sync stack might be
+             slightly better (no JGroups FC) but we stick with udp to
+             help ensure this cache and others like timestamps-cache
+             that require FC can use the same underlying JGroups resources. -->
+        <attribute name="MultiplexerStack">${jboss.default.jgroups.stack:udp}</attribute>
+
+        <!-- Whether or not to fetch state on joining a cluster. -->
+        <attribute name="FetchInMemoryState">false</attribute>
+
+        <!--
+          The max amount of time (in milliseconds) we wait until the
+          state (ie. the contents of the cache) are retrieved from
+          existing members at startup. Ignored if FetchInMemoryState=false.
+        -->
+        <attribute name="StateRetrievalTimeout">20000</attribute>
+
+        <!--
+            Number of milliseconds to wait until all responses for a
+            synchronous call have been received.
+        -->
+        <attribute name="SyncReplTimeout">20000</attribute>
+
+        <!-- Max number of milliseconds to wait for a lock acquisition -->
+        <attribute name="LockAcquisitionTimeout">15000</attribute>
+
+       <!--
+          Indicate whether to use marshalling or not. Set this to true if you 
+          are running under a scoped class loader, e.g., inside an application 
+          server.
+       -->
+       <attribute name="UseRegionBasedMarshalling">true</attribute>
+       <!-- Must match the value of "useRegionBasedMarshalling" -->
+       <attribute name="InactiveOnStartup">true</attribute>
+
+       <!-- Disable asynchronous RPC marshalling/sending -->
+       <attribute name="SerializationExecutorPoolSize">0</attribute>
+        
+       <!-- We have no asynchronous notification listeners -->
+       <attribute name="ListenerAsyncPoolSize">0</attribute>
+
+      <!--  Eviction policy configurations. -->
+      <attribute name="EvictionPolicyConfig">
+        <config>
+          <attribute name="wakeUpIntervalSeconds">5</attribute>
+          <!-- Name of the DEFAULT eviction policy class. -->
+          <attribute name="policyClass">org.jboss.cache.eviction.LRUPolicy</attribute>
+          <!--  Cache wide default -->
+          <region name="/_default_">
+            <!-- Evict LRU node once we have more than this number of nodes -->
+            <attribute name="maxNodes">10000</attribute>
+            <!-- And, evict any node that hasn't been accessed in this many seconds -->
+            <attribute name="timeToLiveSeconds">1000</attribute>
+            <!-- Don't evict a node that's been accessed within this many seconds. 
+                 Set this to a value greater than your max expected transaction length. -->
+            <attribute name="minTimeToLiveSeconds">120</attribute>
+          </region>
+          <!--  Don't ever evict modification timestamps -->
+          <region name="/TS" policyClass="org.jboss.cache.eviction.NullEvictionPolicy"/>
+        </config>
+     </attribute>
+
+    </cache-config>        
+    
+    
+
+    <!-- Same as "pessimistic-entity" but here we use REPEATABLE_READ
+         instead of READ_COMMITTED. REPEATABLE_READ is only useful if the 
+         application evicts/clears entities from the Hibernate Session and 
+         then expects to repeatably re-read them in the same transaction.
+         Otherwise, the Session's internal cache provides a repeatable-read 
+         semantic. Before choosing this config, carefully read the docs
+         and make sure you really need REPEATABLE_READ.
+    -->
+    <cache-config name="pessimistic-entity-repeatable">
+
+        <!-- Node locking scheme -->
+        <attribute name="NodeLockingScheme">PESSIMISTIC</attribute>
+
+        <!-- Here we  use REPEATABLE_READ. -->
+        <attribute name="IsolationLevel">REPEATABLE_READ</attribute>
+
+        <!-- Mode of communication with peer caches.
+        
+             INVALIDATION_SYNC is highly recommended as the mode for use
+             with entity and collection caches.
+        -->
+        <attribute name="CacheMode">INVALIDATION_SYNC</attribute>
+
+        <!-- Name of cluster. Needs to be the same for all members, in order
+             to find each other -->
+        <attribute name="ClusterName">${jboss.partition.name:JBCTest}-pessimistic-entity-rr</attribute>
+        
+        <!-- Use a UDP (multicast) based stack. A udp-sync stack might be
+             slightly better (no JGroups FC) but we stick with udp to
+             help ensure this cache and others like timestamps-cache
+             that require FC can use the same underlying JGroups resources. -->
+        <attribute name="MultiplexerStack">${jboss.default.jgroups.stack:udp}</attribute>
+
+        <!-- Whether or not to fetch state on joining a cluster. -->
+        <attribute name="FetchInMemoryState">false</attribute>
+
+        <!--
+          The max amount of time (in milliseconds) we wait until the
+          state (ie. the contents of the cache) are retrieved from
+          existing members at startup. Ignored if FetchInMemoryState=false.
+        -->
+        <attribute name="StateRetrievalTimeout">20000</attribute>
+
+        <!--
+            Number of milliseconds to wait until all responses for a
+            synchronous call have been received.
+        -->
+        <attribute name="SyncReplTimeout">20000</attribute>
+
+        <!-- Max number of milliseconds to wait for a lock acquisition -->
+        <attribute name="LockAcquisitionTimeout">15000</attribute>
+
+       <!--
+          Indicate whether to use marshalling or not. Set this to true if you 
+          are running under a scoped class loader, e.g., inside an application 
+          server.
+       -->
+       <attribute name="UseRegionBasedMarshalling">true</attribute>
+       <!-- Must match the value of "useRegionBasedMarshalling" -->
+       <attribute name="InactiveOnStartup">true</attribute>
+
+       <!-- Disable asynchronous RPC marshalling/sending -->
+       <attribute name="SerializationExecutorPoolSize">0</attribute>
+        
+       <!-- We have no asynchronous notification listeners -->
+       <attribute name="ListenerAsyncPoolSize">0</attribute>
+
+       <!--  Eviction policy configurations. -->
+       <attribute name="EvictionPolicyConfig">
+        <config>
+          <attribute name="wakeUpIntervalSeconds">5</attribute>
+          <!-- Name of the DEFAULT eviction policy class. -->
+          <attribute name="policyClass">org.jboss.cache.eviction.LRUPolicy</attribute>
+          <!--  Cache wide default -->
+          <region name="/_default_">
+            <!-- Evict LRU node once we have more than this number of nodes -->
+            <attribute name="maxNodes">10000</attribute>
+            <!-- And, evict any node that hasn't been accessed in this many seconds -->
+            <attribute name="timeToLiveSeconds">1000</attribute>
+            <!-- Don't evict a node that's been accessed within this many seconds. 
+                 Set this to a value greater than your max expected transaction length. -->
+            <attribute name="minTimeToLiveSeconds">120</attribute>
+          </region>
+          <!--  Don't ever evict modification timestamps -->
+          <region name="/TS" policyClass="org.jboss.cache.eviction.NullEvictionPolicy"/>
+        </config>
+     </attribute>
+
+    </cache-config>     
+
+
+
+    <!-- A config appropriate for query caching. Does not replicate
+         queries. DO NOT STORE TIMESTAMPS IN THIS CACHE.
+    -->
+    <cache-config name="local-query">
+
+        <!-- Node locking scheme -->
+        <attribute name="NodeLockingScheme">OPTIMISTIC</attribute>
+
+        <attribute name="CacheMode">LOCAL</attribute>
+
+        <!-- Max number of milliseconds to wait for a lock acquisition -->
+        <attribute name="LockAcquisitionTimeout">15000</attribute>
+
+        <!--  Eviction policy configurations. -->
+        <attribute name="EvictionPolicyConfig">
+          <config>
+            <attribute name="wakeUpIntervalSeconds">5</attribute>
+            <!-- Name of the DEFAULT eviction policy class. -->
+            <attribute name="policyClass">org.jboss.cache.eviction.LRUPolicy</attribute>
+            <!--  Cache wide default -->
+            <region name="/_default_">
+               <!-- Evict LRU node once we have more than this number of nodes -->
+               <attribute name="maxNodes">10000</attribute>
+               <!-- And, evict any node that hasn't been accessed in this many seconds -->
+               <attribute name="timeToLiveSeconds">1000</attribute>
+               <!-- Don't evict a node that's been accessed within this many seconds. 
+                    Set this to a value greater than your max expected transaction length. -->
+               <attribute name="minTimeToLiveSeconds">120</attribute>
+            </region>
+            <!--  Don't ever evict modification timestamps -->
+            <region name="/TS" policyClass="org.jboss.cache.eviction.NullEvictionPolicy"/>
+          </config>
+       </attribute>
+
+    </cache-config> 
+    
+
+    <!-- A query cache that replicates queries. Replication is asynchronous.
+         DO NOT STORE TIMESTAMPS IN THIS CACHE as no initial state transfer
+         is performed.
+    -->
+    <cache-config name="replicated-query">
+
+        <!-- Node locking scheme -->
+        <attribute name="NodeLockingScheme">OPTIMISTIC</attribute>
+
+        <!-- Mode of communication with peer caches.
+        
+             REPL_ASYNC means replicate but sender does not block waiting for
+             peers to acknowledge applying the change. Valid for queries as
+             the timestamp cache mechanism will allow Hibernate to discard
+             out-of-date queries.
+        -->
+        <attribute name="CacheMode">REPL_ASYNC</attribute>
+
+        <!-- Name of cluster. Needs to be the same for all members, in order
+             to find each other -->
+        <attribute name="ClusterName">${jboss.partition.name:JBCTest}-replicated-query</attribute>
+        
+        <!-- Use a UDP (multicast) based stack -->
+        <attribute name="MultiplexerStack">${jboss.default.jgroups.stack:udp}</attribute>
+
+        <!-- Whether or not to fetch state on joining a cluster. -->
+        <attribute name="FetchInMemoryState">false</attribute>
+
+        <!--
+          The max amount of time (in milliseconds) we wait until the
+          state (ie. the contents of the cache) are retrieved from
+          existing members at startup. Ignored if FetchInMemoryState=false.
+        -->
+        <attribute name="StateRetrievalTimeout">20000</attribute>
+
+        <!--
+            Number of milliseconds to wait until all responses for a
+            synchronous call have been received.
+        -->
+        <attribute name="SyncReplTimeout">20000</attribute>
+
+        <!-- Max number of milliseconds to wait for a lock acquisition -->
+        <attribute name="LockAcquisitionTimeout">15000</attribute>
+
+       <!--
+          Indicate whether to use marshalling or not. Set this to true if you 
+          are running under a scoped class loader, e.g., inside an application 
+          server.
+       -->
+       <attribute name="UseRegionBasedMarshalling">true</attribute>
+       <!-- Must match the value of "useRegionBasedMarshalling" -->
+       <attribute name="InactiveOnStartup">true</attribute>
+
+       <!-- Disable asynchronous RPC marshalling/sending -->
+       <attribute name="SerializationExecutorPoolSize">0</attribute>
+        
+       <!-- We have no asynchronous notification listeners -->
+       <attribute name="ListenerAsyncPoolSize">0</attribute>
+
+       <!--  Eviction policy configurations. -->
+       <attribute name="EvictionPolicyConfig">
+        <config>
+          <attribute name="wakeUpIntervalSeconds">5</attribute>
+          <!-- Name of the DEFAULT eviction policy class. -->
+          <attribute name="policyClass">org.jboss.cache.eviction.LRUPolicy</attribute>
+          <!--  Cache wide default -->
+          <region name="/_default_">
+            <!-- Evict LRU node once we have more than this number of nodes -->
+            <attribute name="maxNodes">10000</attribute>
+            <!-- And, evict any node that hasn't been accessed in this many seconds -->
+            <attribute name="timeToLiveSeconds">1000</attribute>
+            <!-- Don't evict a node that's been accessed within this many seconds. 
+                 Set this to a value greater than your max expected transaction length. -->
+            <attribute name="minTimeToLiveSeconds">120</attribute>
+          </region>
+          <!--  Don't ever evict modification timestamps -->
+          <region name="/TS" policyClass="org.jboss.cache.eviction.NullEvictionPolicy"/>
+        </config>
+     </attribute>
+
+    </cache-config>     
+    
+    
+
+    <!-- Optimized for timestamp caching. A clustered timestamp cache
+         is required if query caching is used, even if the query cache
+         itself is configured with CacheMode=LOCAL.
+    -->
+    <cache-config name="timestamps-cache">
+
+        <!-- Node locking scheme -->
+        <attribute name="NodeLockingScheme">PESSIMISTIC</attribute>
+
+        <!--
+            READ_COMMITTED is as strong as necessary.
+        -->
+        <attribute name="IsolationLevel">REPEATABLE_READ</attribute>
+
+        <!-- Cannot be INVALIDATION. ASYNC for improved performance. -->
+        <attribute name="CacheMode">REPL_ASYNC</attribute>
+
+        <!-- Name of cluster. Needs to be the same for all members, in order
+             to find each other -->
+        <attribute name="ClusterName">${jboss.partition.name:JBCTest}-timestamps-cache</attribute>
+        
+        <!-- Use a UDP (multicast) based stack -->
+        <attribute name="MultiplexerStack">${jboss.default.jgroups.stack:udp}</attribute>
+
+        <!-- Used for timestamps, so must fetch state. -->
+        <attribute name="FetchInMemoryState">true</attribute>
+
+        <!--
+          The max amount of time (in milliseconds) we wait until the
+          state (ie. the contents of the cache) are retrieved from
+          existing members at startup. Ignored if FetchInMemoryState=false.
+        -->
+        <attribute name="StateRetrievalTimeout">20000</attribute>
+
+        <!--
+            Number of milliseconds to wait until all responses for a
+            synchronous call have been received.
+        -->
+        <attribute name="SyncReplTimeout">20000</attribute>
+
+        <!-- Max number of milliseconds to wait for a lock acquisition -->
+        <attribute name="LockAcquisitionTimeout">15000</attribute>
+
+       <!--
+          Indicate whether to use marshalling or not. Set this to true if you 
+          are running under a scoped class loader, e.g., inside an application 
+          server.
+       -->
+       <attribute name="UseRegionBasedMarshalling">true</attribute>
+       <!-- Must match the value of "useRegionBasedMarshalling" -->
+       <attribute name="InactiveOnStartup">true</attribute>
+
+       <!-- Disable asynchronous RPC marshalling/sending -->
+       <attribute name="SerializationExecutorPoolSize">0</attribute>
+        
+       <!-- We have no asynchronous notification listeners -->
+       <attribute name="ListenerAsyncPoolSize">0</attribute>
+
+      <!--  Eviction policy configurations. -->
+      <attribute name="EvictionPolicyConfig">
+        <config>
+          <attribute name="wakeUpIntervalSeconds">5</attribute>
+          <!-- Name of the DEFAULT eviction policy class. -->
+          <attribute name="policyClass">org.jboss.cache.eviction.LRUPolicy</attribute>
+          <!--  Cache wide default -->
+          <region name="/_default_">
+            <!-- Evict LRU node once we have more than this number of nodes -->
+            <attribute name="maxNodes">10000</attribute>
+            <!-- And, evict any node that hasn't been accessed in this many seconds -->
+            <attribute name="timeToLiveSeconds">1000</attribute>
+            <!-- Don't evict a node that's been accessed within this many seconds. 
+                 Set this to a value greater than your max expected transaction length. -->
+            <attribute name="minTimeToLiveSeconds">120</attribute>
+          </region>
+          <!--  Don't ever evict modification timestamps -->
+          <region name="/TS" policyClass="org.jboss.cache.eviction.NullEvictionPolicy"/>
+        </config>
+     </attribute>
+
+    </cache-config>  
+    
+    
+
+    <!-- A config appropriate for a cache that's shared for
+         entity, collection, query and timestamp caching. Not an advised
+         configuration, since it requires cache mode REPL_SYNC, which is the 
+         least efficient mode. Also requires a full state transfer at startup,
+         which can be expensive. Uses optimistic locking -->
+    <cache-config name="optimistic-shared">
+
+        <!-- Node locking scheme -->
+        <attribute name="NodeLockingScheme">OPTIMISTIC</attribute>
+
+        <!-- Must use REPL since used for timestamp caching. 
+             Must use SYNC to maintain cache consistency for entities.
+        -->
+        <attribute name="CacheMode">REPL_SYNC</attribute>
+        
+        <!-- With OPTIMISTIC with replication we need to use synchronous commits. -->
+        <attribute name="SyncCommitPhase">true</attribute>
+        <attribute name="SyncRollbackPhase">true</attribute>
+        
+        <!-- Name of cluster. Needs to be the same for all members, in order
+             to find each other -->
+        <attribute name="ClusterName">${jboss.partition.name:JBCTest}-optimistic-shared</attribute>
+        
+        <!-- Use a UDP (multicast) based stack. Need JGroups flow control (FC)
+             because timestamp communication will not require a synchronous response.
+        -->
+        <attribute name="MultiplexerStack">${jboss.default.jgroups.stack:udp}</attribute>
+
+        <!-- Used for timestamps, so must fetch state. -->
+        <attribute name="FetchInMemoryState">true</attribute>
+
+        <!--
+          The max amount of time (in milliseconds) we wait until the
+          state (ie. the contents of the cache) are retrieved from
+          existing members at startup. Ignored if FetchInMemoryState=false.
+        -->
+        <attribute name="StateRetrievalTimeout">20000</attribute>
+
+        <!--
+            Number of milliseconds to wait until all responses for a
+            synchronous call have been received.
+        -->
+        <attribute name="SyncReplTimeout">20000</attribute>
+
+        <!-- Max number of milliseconds to wait for a lock acquisition -->
+        <attribute name="LockAcquisitionTimeout">15000</attribute>
+
+       <!--
+          Indicate whether to use marshalling or not. Set this to true if you 
+          are running under a scoped class loader, e.g., inside an application 
+          server.
+       -->
+       <attribute name="UseRegionBasedMarshalling">true</attribute>
+       <!-- Must match the value of "useRegionBasedMarshalling" -->
+       <attribute name="InactiveOnStartup">true</attribute>
+
+       <!-- Disable asynchronous RPC marshalling/sending -->
+       <attribute name="SerializationExecutorPoolSize">0</attribute>
+        
+       <!-- We have no asynchronous notification listeners -->
+       <attribute name="ListenerAsyncPoolSize">0</attribute>
+
+       <!--  Eviction policy configurations. -->
+       <attribute name="EvictionPolicyConfig">
+        <config>
+          <attribute name="wakeUpIntervalSeconds">5</attribute>
+          <!-- Name of the DEFAULT eviction policy class. -->
+          <attribute name="policyClass">org.jboss.cache.eviction.LRUPolicy</attribute>
+          <!--  Cache wide default -->
+          <region name="/_default_">
+            <!-- Evict LRU node once we have more than this number of nodes -->
+            <attribute name="maxNodes">10000</attribute>
+            <!-- And, evict any node that hasn't been accessed in this many seconds -->
+            <attribute name="timeToLiveSeconds">1000</attribute>
+            <!-- Don't evict a node that's been accessed within this many seconds. 
+                 Set this to a value greater than your max expected transaction length. -->
+            <attribute name="minTimeToLiveSeconds">120</attribute>
+          </region>
+          <!--  Don't ever evict modification timestamps -->
+          <region name="/TS" policyClass="org.jboss.cache.eviction.NullEvictionPolicy"/>
+        </config>
+     </attribute>
+
+    </cache-config>   
+    
+    
+
+    <!-- A config appropriate for a cache that's shared for
+         entity, collection, query and timestamp caching. Not an advised
+         configuration, since it requires cache mode REPL_SYNC, which is the 
+         least efficient mode. Also requires a full state transfer at startup,
+         which can be expensive. Uses pessmistic locking.
+    -->
+    <cache-config name="pessimistic-shared">
+
+        <!-- Node locking scheme -->
+        <attribute name="NodeLockingScheme">PESSIMISTIC</attribute>
+
+        <!--
+            READ_COMMITTED is as strong as necessary for most 
+            2nd Level Cache use cases.
+        -->
+        <attribute name="IsolationLevel">REPEATABLE_READ</attribute>
+
+        <!-- Must use REPL since used for timestamp caching. 
+             Must use SYNC to maintain cache consistency for entities.
+        -->
+        <attribute name="CacheMode">REPL_SYNC</attribute>
+
+        <!-- Name of cluster. Needs to be the same for all members, in order
+             to find each other -->
+        <attribute name="ClusterName">${jboss.partition.name:JBCTest}-pessimistic-shared</attribute>
+        
+        <!-- Use a UDP (multicast) based stack. Need JGroups flow control (FC)
+             because timestamp communication will not require a synchronous response.
+        -->
+        <attribute name="MultiplexerStack">${jboss.default.jgroups.stack:udp}</attribute>
+
+        <!-- Used for timestamps, so must fetch state. -->
+        <attribute name="FetchInMemoryState">true</attribute>
+
+        <!--
+          The max amount of time (in milliseconds) we wait until the
+          state (ie. the contents of the cache) are retrieved from
+          existing members at startup.
+        -->
+        <attribute name="StateRetrievalTimeout">20000</attribute>
+
+        <!--
+            Number of milliseconds to wait until all responses for a
+            synchronous call have been received.
+        -->
+        <attribute name="SyncReplTimeout">20000</attribute>
+
+        <!-- Max number of milliseconds to wait for a lock acquisition -->
+        <attribute name="LockAcquisitionTimeout">15000</attribute>
+
+       <!--
+          Indicate whether to use marshalling or not. Set this to true if you 
+          are running under a scoped class loader, e.g., inside an application 
+          server. Default is "false".
+       -->
+       <attribute name="UseRegionBasedMarshalling">true</attribute>
+       <!-- Must match the value of "useRegionBasedMarshalling" -->
+       <attribute name="InactiveOnStartup">true</attribute>
+
+       <!-- Disable asynchronous RPC marshalling/sending -->
+       <attribute name="SerializationExecutorPoolSize">0</attribute>
+        
+       <!-- We have no asynchronous notification listeners -->
+       <attribute name="ListenerAsyncPoolSize">0</attribute>
+
+       <!--  Eviction policy configurations. -->
+       <attribute name="EvictionPolicyConfig">
+        <config>
+          <attribute name="wakeUpIntervalSeconds">5</attribute>
+          <!-- Name of the DEFAULT eviction policy class. -->
+          <attribute name="policyClass">org.jboss.cache.eviction.LRUPolicy</attribute>
+          <!--  Cache wide default -->
+          <region name="/_default_">
+            <!-- Evict LRU node once we have more than this number of nodes -->
+            <attribute name="maxNodes">10000</attribute>
+            <!-- And, evict any node that hasn't been accessed in this many seconds -->
+            <attribute name="timeToLiveSeconds">1000</attribute>
+            <!-- Don't evict a node that's been accessed within this many seconds. 
+                 Set this to a value greater than your max expected transaction length. -->
+            <attribute name="minTimeToLiveSeconds">120</attribute>
+          </region>
+          <!--  Don't ever evict modification timestamps -->
+          <region name="/TS" policyClass="org.jboss.cache.eviction.NullEvictionPolicy"/>
+        </config>
+     </attribute>
+
+    </cache-config>  
+    
+    
+
+    <!-- Same as "pessimistic-shared" but here we use REPEATABLE_READ
+         instead of READ_COMMITTED. REPEATABLE_READ is only useful if the 
+         application evicts/clears entities from the Hibernate Session and 
+         then expects to repeatably re-read them in the same transaction.
+         Otherwise, the Session's internal cache provides a repeatable-read 
+         semantic. Before choosing this config, carefully read the docs
+         and make sure you really need REPEATABLE_READ.
+    -->
+    <cache-config name="pessimistic-shared-repeatable">
+
+        <!-- Node locking scheme -->
+        <attribute name="NodeLockingScheme">PESSIMISTIC</attribute>
+
+        <!-- Here we  use REPEATABLE_READ. -->
+        <attribute name="IsolationLevel">REPEATABLE_READ</attribute>
+
+        <!-- Must use REPL since used for timestamp caching. 
+             Must use SYNC to maintain cache coherency for entities.
+        -->
+        <attribute name="CacheMode">REPL_SYNC</attribute>
+
+        <!-- Name of cluster. Needs to be the same for all members, in order
+             to find each other -->
+        <attribute name="ClusterName">${jboss.partition.name:JBCTest}-pessimistic-shared-rr</attribute>
+        
+        <!-- Use a UDP (multicast) based stack. Need JGroups flow control (FC)
+             because timestamp communication will not require a synchronous response.
+        -->
+        <attribute name="MultiplexerStack">${jboss.default.jgroups.stack:udp}</attribute>
+
+        <!-- Used for timestamps, so must fetch state. -->
+        <attribute name="FetchInMemoryState">true</attribute>
+
+        <!--
+          The max amount of time (in milliseconds) we wait until the
+          state (ie. the contents of the cache) are retrieved from
+          existing members at startup.
+        -->
+        <attribute name="StateRetrievalTimeout">20000</attribute>
+
+        <!--
+            Number of milliseconds to wait until all responses for a
+            synchronous call have been received.
+        -->
+        <attribute name="SyncReplTimeout">20000</attribute>
+
+        <!-- Max number of milliseconds to wait for a lock acquisition -->
+        <attribute name="LockAcquisitionTimeout">15000</attribute>
+
+       <!--
+          Indicate whether to use marshalling or not. Set this to true if you 
+          are running under a scoped class loader, e.g., inside an application 
+          server.
+       -->
+       <attribute name="UseRegionBasedMarshalling">true</attribute>
+       <!-- Must match the value of "useRegionBasedMarshalling" -->
+       <attribute name="InactiveOnStartup">true</attribute>
+
+       <!-- Disable asynchronous RPC marshalling/sending -->
+       <attribute name="SerializationExecutorPoolSize">0</attribute>
+        
+       <!-- We have no asynchronous notification listeners -->
+       <attribute name="ListenerAsyncPoolSize">0</attribute>
+
+       <!--  Eviction policy configurations. -->
+       <attribute name="EvictionPolicyConfig">
+        <config>
+          <attribute name="wakeUpIntervalSeconds">5</attribute>
+          <!-- Name of the DEFAULT eviction policy class. -->
+          <attribute name="policyClass">org.jboss.cache.eviction.LRUPolicy</attribute>
+          <!--  Cache wide default -->
+          <region name="/_default_">
+            <!-- Evict LRU node once we have more than this number of nodes -->
+            <attribute name="maxNodes">10000</attribute>
+            <!-- And, evict any node that hasn't been accessed in this many seconds -->
+            <attribute name="timeToLiveSeconds">1000</attribute>
+            <!-- Don't evict a node that's been accessed within this many seconds. 
+                 Set this to a value greater than your max expected transaction length. -->
+            <attribute name="minTimeToLiveSeconds">120</attribute>
+          </region>
+          <!--  Don't ever evict modification timestamps -->
+          <region name="/TS" policyClass="org.jboss.cache.eviction.NullEvictionPolicy"/>
+        </config>
+     </attribute>
+
+    </cache-config>
+    
+</cache-configs>

Added: core/trunk/src/test/resources/configs/integration/jgroups-channelfactory-stacks.xml
===================================================================
--- core/trunk/src/test/resources/configs/integration/jgroups-channelfactory-stacks.xml	                        (rev 0)
+++ core/trunk/src/test/resources/configs/integration/jgroups-channelfactory-stacks.xml	2008-11-24 07:01:36 UTC (rev 7186)
@@ -0,0 +1,378 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE protocol_stacks [
+   <!ENTITY shared-udp '
+		<!--  UDP transport config meant to be shared between different channels
+		      with different requirements. Message bundling is disabled in this 
+		      general-purpose config as it can add latency to synchronous RPCs. -->
+		<UDP
+		     mcast_port="${jboss.jgroups.udp.mcast_port:45688}"
+		     mcast_addr="${jgroups.udp.mcast_addr:228.11.11.11}"
+		     tos="8"
+		     ucast_recv_buf_size="20000000"
+		     ucast_send_buf_size="640000"
+		     mcast_recv_buf_size="25000000"
+		     mcast_send_buf_size="640000"
+		     loopback="true"
+		     discard_incompatible_packets="true"
+		     enable_bundling="false"
+		     max_bundle_size="64000"
+		     max_bundle_timeout="30"
+		     use_incoming_packet_handler="true"
+		     ip_ttl="2"
+		     thread_naming_pattern="cl"
+		     timer.num_threads="12"
+		                 
+		     use_concurrent_stack="true"
+		
+		     thread_pool.enabled="true"
+		     thread_pool.min_threads="20"
+		     thread_pool.max_threads="200"
+		     thread_pool.keep_alive_time="5000"
+		     thread_pool.queue_enabled="true"
+		     thread_pool.queue_max_size="1000"
+		     thread_pool.rejection_policy="discard"
+		      
+		     oob_thread_pool.enabled="true"
+		     oob_thread_pool.min_threads="1"
+		     oob_thread_pool.max_threads="20"
+		     oob_thread_pool.keep_alive_time="5000"
+		     oob_thread_pool.queue_enabled="false"
+		     oob_thread_pool.queue_max_size="100"
+		     oob_thread_pool.rejection_policy="run"/>
+		'>
+]>
+
+
+<!--
+  Standard JGroups protocol stacks definitions, used by the JChannelFactory bean.
+  
+  Author: Bela Ban, Brian Stansberry
+  Version: $Id:jgroups-channelfactory-stacks.xml 71313 2008-03-26 19:46:59Z bstansberry at jboss.com $
+-->
+<protocol_stacks>
+    <stack name="udp"
+           description="Default: IP multicast based stack, with flow control.">
+        <config>
+          <!--  UDP transport config meant to be shared between different channels,
+                including a JBoss Messaging channel that uses the 'jbm-control' 
+                stack listed below. Message bundling is disabled, as it can add 
+                latency to synchronous group RPCs. Services that only make
+                asynchronous RPCs (e.g. JBoss Cache configured for REPL_ASYNC)
+                and do so in high volume may be able to improve performance by
+                configuring their cache to use the udp-async stack below.
+                Services that only make synchronous RPCs (e.g. JBoss Cache
+                configured for REPL_SYNC or INVALIDATION_SYNC) may be able
+                to improve performance by using the udp-sync stack below, which
+                does not include flow control.
+                
+                The UDP config is included via an XML entity to ensure that
+                it remains consistent between this stack and the 'jbm-control' 
+                stack below.
+          -->
+          &shared-udp;
+          <PING timeout="2000" num_initial_members="3"/>
+          <MERGE2 max_interval="100000" min_interval="20000"/>
+          <FD_SOCK/>
+          <FD timeout="6000" max_tries="5" shun="true"/>
+          <VERIFY_SUSPECT timeout="1500"/>
+          <pbcast.NAKACK use_mcast_xmit="false" gc_lag="0"
+                   retransmit_timeout="300,600,1200,2400,4800"
+                   discard_delivered_msgs="true"/>
+          <UNICAST timeout="300,600,1200,2400,3600"/>
+          <pbcast.STABLE stability_delay="1000" desired_avg_gossip="50000"
+                   max_bytes="400000"/>
+          <pbcast.GMS print_local_addr="true" join_timeout="3000"
+                   shun="true"
+                   view_bundling="true"
+                   view_ack_collection_timeout="5000"/>
+          <FC max_credits="2000000" min_threshold="0.10"/>
+          <FRAG2 frag_size="60000"/>
+          <!-- pbcast.STREAMING_STATE_TRANSFER use_reading_thread="true"/ -->
+          <pbcast.STATE_TRANSFER/>
+          <pbcast.FLUSH timeout="0"/>
+        </config>
+    </stack>
+    
+    
+    <stack name="udp-async"
+           description="Same as the default 'udp' stack above, except message bundling
+                        is enabled in the transport protocol (enable_bundling=true). 
+                        Useful for services that make high-volume asynchronous 
+                        RPCs (e.g. high volume JBoss Cache instances configured 
+                        for REPL_ASYNC) where message bundling may improve performance.">
+        <config>
+          <UDP
+             mcast_port="${jboss.jgroups.udp_async.mcast_port:45689}"
+             mcast_addr="${jgroups.udp.mcast_addr:228.11.11.11}"
+             tos="8"
+             ucast_recv_buf_size="20000000"
+             ucast_send_buf_size="640000"
+             mcast_recv_buf_size="25000000"
+             mcast_send_buf_size="640000"
+             loopback="true"
+             discard_incompatible_packets="true"
+             enable_bundling="false"
+             max_bundle_size="64000"
+             max_bundle_timeout="30"
+             use_incoming_packet_handler="true"
+             ip_ttl="2"
+             thread_naming_pattern="cl"
+             timer.num_threads="12"
+                 
+             use_concurrent_stack="true"
+
+             thread_pool.enabled="true"
+             thread_pool.min_threads="8"
+             thread_pool.max_threads="200"
+             thread_pool.keep_alive_time="5000"
+             thread_pool.queue_enabled="true"
+             thread_pool.queue_max_size="1000"
+             thread_pool.rejection_policy="discard"
+      
+             oob_thread_pool.enabled="true"
+             oob_thread_pool.min_threads="1"
+             oob_thread_pool.max_threads="8"
+             oob_thread_pool.keep_alive_time="5000"
+             oob_thread_pool.queue_enabled="false"
+             oob_thread_pool.queue_max_size="100"
+             oob_thread_pool.rejection_policy="run"/>
+          <PING timeout="2000" num_initial_members="3"/>
+          <MERGE2 max_interval="100000" min_interval="20000"/>
+          <FD_SOCK/>
+          <FD timeout="6000" max_tries="5" shun="true"/>
+          <VERIFY_SUSPECT timeout="1500"/>
+          <pbcast.NAKACK use_mcast_xmit="false" gc_lag="0"
+                   retransmit_timeout="300,600,1200,2400,4800"
+                   discard_delivered_msgs="true"/>
+          <UNICAST timeout="300,600,1200,2400,3600"/>
+          <pbcast.STABLE stability_delay="1000" desired_avg_gossip="50000"
+                   max_bytes="400000"/>
+          <pbcast.GMS print_local_addr="true" join_timeout="3000"
+                   shun="true"
+                   view_bundling="true"
+                   view_ack_collection_timeout="5000"/>
+          <FC max_credits="2000000" min_threshold="0.10"/>
+          <FRAG2 frag_size="60000"/>
+          <!-- pbcast.STREAMING_STATE_TRANSFER use_reading_thread="true"/ -->
+          <pbcast.STATE_TRANSFER/>
+          <pbcast.FLUSH timeout="0"/>
+        </config>
+    </stack>
+    
+
+    <stack name="udp-sync"
+           description="IP multicast based stack, without flow control and 
+                        without message bundling. This should be used instead 
+                        of 'udp' if (1) synchronous calls are used and (2) the 
+                        message volume (rate and size) is not that large. Don't 
+                        use this configuration if you send messages at a high 
+                        sustained rate, or you might run out of memory">
+        <config>
+            <UDP
+                 mcast_port="${jgroups.udp_sync.mcast_port:45699}"
+                 mcast_addr="${jgroups.udp.mcast_addr:229.11.11.11}"
+                 tos="8"
+                 ucast_recv_buf_size="20000000"
+                 ucast_send_buf_size="640000"
+                 mcast_recv_buf_size="25000000"
+                 mcast_send_buf_size="640000"
+                 loopback="true"
+                 discard_incompatible_packets="true"
+                 enable_bundling="false"
+                 max_bundle_size="64000"
+                 max_bundle_timeout="30"
+                 use_incoming_packet_handler="true"
+                 ip_ttl="2"
+                 
+                 use_concurrent_stack="true"
+
+		           thread_pool.enabled="true"
+		           thread_pool.min_threads="8"
+		           thread_pool.max_threads="200"
+    		        thread_pool.keep_alive_time="5000"
+    		        thread_pool.queue_enabled="true"
+    		        thread_pool.queue_max_size="1000"
+    		        thread_pool.rejection_policy="discard"
+    		
+    		        oob_thread_pool.enabled="true"
+    		        oob_thread_pool.min_threads="1"
+    		        oob_thread_pool.max_threads="8"
+    		        oob_thread_pool.keep_alive_time="5000"
+    		        oob_thread_pool.queue_enabled="false"
+    		        oob_thread_pool.queue_max_size="100"
+    		        oob_thread_pool.rejection_policy="run"/>
+            <PING timeout="2000" num_initial_members="3"/>
+            <MERGE2 max_interval="100000" min_interval="20000"/>
+            <FD_SOCK/>
+            <FD timeout="6000" max_tries="5" shun="true"/>
+            <VERIFY_SUSPECT timeout="1500"/>
+            <pbcast.NAKACK use_mcast_xmit="false" gc_lag="0"
+                    retransmit_timeout="300,600,1200,2400,4800"
+                    discard_delivered_msgs="true"/>
+            <UNICAST timeout="300,600,1200,2400,3600"/>
+            <pbcast.STABLE stability_delay="1000" desired_avg_gossip="50000"
+                    max_bytes="400000"/>
+            <pbcast.GMS print_local_addr="true" join_timeout="3000"
+                    shun="true"
+                    view_bundling="true"
+                    view_ack_collection_timeout="5000"/>
+            <FRAG2 frag_size="60000"/>
+            <!--pbcast.STREAMING_STATE_TRANSFER use_reading_thread="true"/ -->
+            <pbcast.STATE_TRANSFER/>
+            <pbcast.FLUSH timeout="0"/>
+        </config>
+    </stack>
+
+
+    <stack name="tcp"
+           description="TCP based stack, with flow control and message bundling. 
+                        This is usually used when IP multicasting cannot be used 
+                        in a network, e.g. because it is disabled (e.g. routers 
+                        discard multicast)">
+        <config>
+            <TCP
+                 start_port="7600"
+                 tcp_nodelay="true"
+                 loopback="false"
+                 recv_buf_size="20000000"
+                 send_buf_size="640000"
+                 discard_incompatible_packets="true"
+                 max_bundle_size="64000"
+                 max_bundle_timeout="30"
+                 use_incoming_packet_handler="true"
+                 enable_bundling="true"
+                 use_send_queues="false"
+                 sock_conn_timeout="300"
+                 skip_suspected_members="true"
+                 timer.num_threads="12"
+                 
+                 use_concurrent_stack="true"
+	
+		           thread_pool.enabled="true"
+		           thread_pool.min_threads="20"
+		           thread_pool.max_threads="200"
+		           thread_pool.keep_alive_time="5000"
+		           thread_pool.queue_enabled="true"
+		           thread_pool.queue_max_size="1000"
+		           thread_pool.rejection_policy="discard"
+		
+                 oob_thread_pool.enabled="true"
+		           oob_thread_pool.min_threads="1"
+		           oob_thread_pool.max_threads="20"
+		           oob_thread_pool.keep_alive_time="5000"
+		           oob_thread_pool.queue_enabled="false"
+		           oob_thread_pool.queue_max_size="100"
+		           oob_thread_pool.rejection_policy="run"/>
+		      <!-- Alternative 1: multicast-based automatic discovery. -->   
+            <MPING timeout="3000"
+                   num_initial_members="3"
+                   mcast_addr="${jgroups.udp.mcast_addr:230.11.11.11}"
+                   mcast_port="${jgroups.tcp_mping.mcast_port:45700}"
+                   ip_ttl="2"/>            
+            <!-- Alternative 2: non multicast-based replacement for MPING. Requires a static configuration
+                 of *all* possible cluster members.
+            <TCPPING timeout="3000"
+                     initial_hosts="${jgroups.tcpping.initial_hosts:localhost[7600],localhost[7601]}"
+                     port_range="1"
+                     num_initial_members="3"/>
+             -->
+            <MERGE2 max_interval="100000" min_interval="20000"/>
+            <FD_SOCK/>
+            <FD timeout="6000" max_tries="5" shun="true"/>
+            <VERIFY_SUSPECT timeout="1500"/>
+            <pbcast.NAKACK use_mcast_xmit="false" gc_lag="0"
+                           retransmit_timeout="300,600,1200,2400,4800"
+                           discard_delivered_msgs="true"/>
+            <UNICAST timeout="300,600,1200,2400,3600"/>
+            <pbcast.STABLE stability_delay="1000" desired_avg_gossip="50000"
+                           max_bytes="400000"/>
+            <pbcast.GMS print_local_addr="true" join_timeout="3000"
+                        shun="true"
+                        view_bundling="true"
+                        view_ack_collection_timeout="5000"/>
+            <FC max_credits="2000000" min_threshold="0.10"/>
+            <FRAG2 frag_size="60000"/>
+            <!-- pbcast.STREAMING_STATE_TRANSFER use_reading_thread="true"/ -->
+            <pbcast.STATE_TRANSFER/>
+            <pbcast.FLUSH timeout="0"/>
+        </config>
+    </stack>
+
+
+    <stack name="tcp-sync"
+           description="TCP based stack, without flow control and without 
+                        message bundling. This is usually used when IP 
+                        multicasting cannot be used in a network (e.g.routers 
+                        discard multicast). This configuration should be used 
+                        instead of 'tcp' above when (1) synchronous calls are 
+                        used and (2) the message volume (rate and size) is not 
+                        that large.">
+        <config>
+            <TCP
+                 start_port="7650"
+                 tcp_nodelay="true"
+                 loopback="false"
+                 recv_buf_size="20000000"
+                 send_buf_size="640000"
+                 discard_incompatible_packets="true"
+                 max_bundle_size="64000"
+                 max_bundle_timeout="30"
+                 use_incoming_packet_handler="true"
+                 enable_bundling="false"
+                 use_send_queues="false"
+                 sock_conn_timeout="300"
+                 skip_suspected_members="true"
+                 
+                 use_concurrent_stack="true"
+                 
+                 thread_pool.enabled="true"
+		           thread_pool.min_threads="8"
+		           thread_pool.max_threads="200"
+		           thread_pool.keep_alive_time="5000"
+		           thread_pool.queue_enabled="true"
+		           thread_pool.queue_max_size="1000"
+		           thread_pool.rejection_policy="discard"
+		
+		           oob_thread_pool.enabled="true"
+		           oob_thread_pool.min_threads="1"
+		           oob_thread_pool.max_threads="8"
+		           oob_thread_pool.keep_alive_time="5000"
+		           oob_thread_pool.queue_enabled="false"
+		           oob_thread_pool.queue_max_size="100"
+		           oob_thread_pool.rejection_policy="run"/>
+            <!-- Alternative 1: multicast-based automatic discovery. -->   
+            <MPING timeout="3000"
+                   num_initial_members="3"
+                   mcast_addr="${jgroups.udp.mcast_addr:231.11.11.11}"
+                   mcast_port="${jgroups.tcp_sync_mping.mcast_port:45701}"
+                   ip_ttl="2"/>           
+            <!-- Alternative 2: non multicast-based replacement for MPING. Requires a static configuration
+                 of all possible cluster members.
+            <TCPPING timeout="3000"
+                     initial_hosts="${jgroups.tcpping.initial_hosts:localhost[7650],localhost[7651]}"
+                     port_range="1"
+                     num_initial_members="3"/>
+            -->
+            <MERGE2 max_interval="100000" min_interval="20000"/>
+            <FD_SOCK/>
+            <FD timeout="6000" max_tries="5" shun="true"/>
+            <VERIFY_SUSPECT timeout="1500"/>
+            <pbcast.NAKACK use_mcast_xmit="false" gc_lag="0"
+                           retransmit_timeout="300,600,1200,2400,4800"
+                           discard_delivered_msgs="true"/>
+            <UNICAST timeout="300,600,1200,2400,3600"/>
+            <pbcast.STABLE stability_delay="1000" desired_avg_gossip="50000"
+                           max_bytes="400000"/>
+            <pbcast.GMS print_local_addr="true" join_timeout="3000"
+                        shun="true"
+                        view_bundling="true"
+                        view_ack_collection_timeout="5000"/>
+            <!-- pbcast.STREAMING_STATE_TRANSFER use_reading_thread="true"/ -->
+            <pbcast.STATE_TRANSFER/>
+            <pbcast.FLUSH timeout="0"/>
+        </config>
+    </stack>
+
+</protocol_stacks>
+
+

Added: core/trunk/src/test/resources/configs/integration/sfsb-cache-configs.xml
===================================================================
--- core/trunk/src/test/resources/configs/integration/sfsb-cache-configs.xml	                        (rev 0)
+++ core/trunk/src/test/resources/configs/integration/sfsb-cache-configs.xml	2008-11-24 07:01:36 UTC (rev 7186)
@@ -0,0 +1,263 @@
+<cache-configs>
+
+    <!-- 
+      | A config appropriate for EJB3 SFSB caching.
+    -->
+    <cache-config name="sfsb-cache">
+        <!--  No transaction manager lookup -->
+        
+        <attribute name="NodeLockingScheme">PESSIMISTIC</attribute>
+        <attribute name="IsolationLevel">REPEATABLE_READ</attribute>
+
+        <!-- Valid modes are LOCAL
+                             REPL_ASYNC
+                             REPL_SYNC
+        -->
+        <attribute name="CacheMode">REPL_ASYNC</attribute>
+
+        <!-- Name of cluster. Needs to be the same for all members, in order
+             to find each other -->
+        <attribute name="ClusterName">${jboss.partition.name:JBCTest}-SFSBCache</attribute>
+        
+        <!-- Use a UDP (multicast) based stack. Need JGroups flow control (FC)
+             because we are using asynchronous replication.
+        -->
+        <attribute name="MultiplexerStack">${jboss.default.jgroups.stack:udp}</attribute>
+
+        <!-- Must fetch state. -->
+        <attribute name="FetchInMemoryState">true</attribute>
+
+        <!--
+          The max amount of time (in milliseconds) we wait until the
+          state (ie. the contents of the cache) are retrieved from
+          existing members at startup.
+        -->
+        <attribute name="StateRetrievalTimeout">60000</attribute>
+
+        <!--
+            Number of milliseconds to wait until all responses for a
+            synchronous call have been received.
+        -->
+        <attribute name="SyncReplTimeout">17500</attribute>
+
+        <!-- Max number of milliseconds to wait for a lock acquisition -->
+        <attribute name="LockAcquisitionTimeout">15000</attribute>
+
+        <!--
+          SFSBs use region-based marshalling to provide for partial state
+          transfer during deployment/undeployment.
+        -->
+        <attribute name="UseRegionBasedMarshalling">true</attribute>
+        <!-- Must match the value of "useRegionBasedMarshalling" -->
+        <attribute name="InactiveOnStartup">true</attribute>
+
+        <!-- Disable asynchronous RPC marshalling/sending -->
+        <attribute name="SerializationExecutorPoolSize">0</attribute>
+        
+        <!-- We have no asynchronous notification listeners -->
+        <attribute name="ListenerAsyncPoolSize">0</attribute>
+           
+        <attribute name="ExposeManagementStatistics">true</attribute>
+
+        <!-- Buddy Replication config -->
+        <attribute name="BuddyReplicationConfig">
+          <config>
+            <buddyReplicationEnabled>false</buddyReplicationEnabled>
+            <buddyLocatorClass>org.jboss.cache.buddyreplication.NextMemberBuddyLocator</buddyLocatorClass>
+            <!-- numBuddies is the number of backup nodes each node maintains.  
+                 ignoreColocatedBuddies means that each node will *try* to 
+                 select a buddy on a different physical host. If not able to do 
+                 so though, it will fall back to colocated nodes. -->
+            <buddyLocatorProperties>
+               numBuddies = 1
+               ignoreColocatedBuddies = true
+            </buddyLocatorProperties>
+
+            <!-- A way to specify a preferred replication group.  If specified, 
+                 we try and pick a buddy why shares the same pool name (falling 
+                 back to other buddies if not available).  This allows the sysdmin 
+                 to hint at backup buddies are picked, so for example, nodes may 
+                 be hinted topick buddies on a different physical rack or power 
+                 supply for added fault tolerance.  
+            -->
+            <buddyPoolName>default</buddyPoolName>
+            <!-- communication timeout for inter-buddy group organisation 
+                 messages (such as assigning to and removing from groups -->
+            <buddyCommunicationTimeout>17500</buddyCommunicationTimeout>
+
+            <!-- Do not change these -->
+            <autoDataGravitation>false</autoDataGravitation>
+            <dataGravitationRemoveOnFind>true</dataGravitationRemoveOnFind>
+            <dataGravitationSearchBackupTrees>true</dataGravitationSearchBackupTrees>
+
+          </config>
+        </attribute>
+        
+        <!-- Cache Loader for session passivation --> 
+        <attribute name="CacheLoaderConfig">
+          <config>
+            <passivation>true</passivation>
+            <shared>false</shared>
+
+            <purgeOnStartup>true</purgeOnStartup>
+
+            <cacheloader>
+               <class>org.jboss.cache.loader.FileCacheLoader</class>
+               <properties>
+                  location=${java.io.tmpdir}${/}sfsb
+               </properties>
+               <async>false</async>
+               <fetchPersistentState>true</fetchPersistentState>
+               <ignoreModifications>false</ignoreModifications>
+               <checkCharacterPortability>false</checkCharacterPortability>
+            </cacheloader>
+
+          </config>
+        </attribute>
+
+        <!-- SFSBs use JBoss Cache eviction -->
+        <attribute name="EvictionPolicyConfig">
+          <config>
+            <attribute name="wakeUpIntervalSeconds">5</attribute>
+            <!-- Name of the DEFAULT eviction policy class. -->
+            <attribute name="policyClass">org.jboss.cache.eviction.NullEvictionPolicy</attribute>
+            <!--  Cache wide default region -->
+            <region name="/_default_"/>
+            <!-- EJB3 integration code will programatically create
+                 other regions as beans are deployed -->
+          </config>
+        </attribute>
+
+    </cache-config>
+
+    <!-- 
+      | A buddy replication enabled config appropriate for EJB3 SFSB caching.
+    -->
+    <cache-config name="br-sfsb-cache">
+        <!--  No transaction manager lookup -->
+        
+        <attribute name="NodeLockingScheme">PESSIMISTIC</attribute>
+        <attribute name="IsolationLevel">REPEATABLE_READ</attribute>
+
+        <!-- Valid modes are LOCAL
+                             REPL_ASYNC
+                             REPL_SYNC
+        -->
+        <attribute name="CacheMode">REPL_ASYNC</attribute>
+
+        <!-- Name of cluster. Needs to be the same for all members, in order
+             to find each other -->
+        <attribute name="ClusterName">${jboss.partition.name:JBCTest}-SFSBCache</attribute>
+        
+        <!-- Use a UDP (multicast) based stack. Need JGroups flow control (FC)
+             because we are using asynchronous replication.
+        -->
+        <attribute name="MultiplexerStack">${jboss.default.jgroups.stack:udp}</attribute>
+
+        <!-- Must fetch state. -->
+        <attribute name="FetchInMemoryState">true</attribute>
+
+        <!--
+          The max amount of time (in milliseconds) we wait until the
+          state (ie. the contents of the cache) are retrieved from
+          existing members at startup.
+        -->
+        <attribute name="StateRetrievalTimeout">60000</attribute>
+
+        <!--
+            Number of milliseconds to wait until all responses for a
+            synchronous call have been received.
+        -->
+        <attribute name="SyncReplTimeout">17500</attribute>
+
+        <!-- Max number of milliseconds to wait for a lock acquisition -->
+        <attribute name="LockAcquisitionTimeout">15000</attribute>
+
+        <!--
+          SFSBs use region-based marshalling to provide for partial state
+          transfer during deployment/undeployment.
+        -->
+        <attribute name="UseRegionBasedMarshalling">true</attribute>
+        <!-- Must match the value of "useRegionBasedMarshalling" -->
+        <attribute name="InactiveOnStartup">true</attribute>
+
+        <!-- Disable asynchronous RPC marshalling/sending -->
+        <attribute name="SerializationExecutorPoolSize">0</attribute>
+        
+        <!-- We have no asynchronous notification listeners -->
+        <attribute name="ListenerAsyncPoolSize">0</attribute>
+           
+        <attribute name="ExposeManagementStatistics">true</attribute>
+
+        <!-- Buddy Replication config -->
+        <attribute name="BuddyReplicationConfig">
+          <config>
+            <buddyReplicationEnabled>true</buddyReplicationEnabled>
+            <buddyLocatorClass>org.jboss.cache.buddyreplication.NextMemberBuddyLocator</buddyLocatorClass>
+            <!-- numBuddies is the number of backup nodes each node maintains.  
+                 ignoreColocatedBuddies means that each node will *try* to 
+                 select a buddy on a different physical host. If not able to do 
+                 so though, it will fall back to colocated nodes. -->
+            <buddyLocatorProperties>
+               numBuddies = 1
+               ignoreColocatedBuddies = true
+            </buddyLocatorProperties>
+
+            <!-- A way to specify a preferred replication group.  If specified, 
+                 we try and pick a buddy why shares the same pool name (falling 
+                 back to other buddies if not available).  This allows the sysdmin 
+                 to hint at backup buddies are picked, so for example, nodes may 
+                 be hinted topick buddies on a different physical rack or power 
+                 supply for added fault tolerance.  
+            -->
+            <buddyPoolName>default</buddyPoolName>
+            <!-- communication timeout for inter-buddy group organisation 
+                 messages (such as assigning to and removing from groups -->
+            <buddyCommunicationTimeout>17500</buddyCommunicationTimeout>
+
+            <!-- Do not change these -->
+            <autoDataGravitation>false</autoDataGravitation>
+            <dataGravitationRemoveOnFind>true</dataGravitationRemoveOnFind>
+            <dataGravitationSearchBackupTrees>true</dataGravitationSearchBackupTrees>
+
+          </config>
+        </attribute>
+        
+        <!-- Cache Loader for session passivation --> 
+        <attribute name="CacheLoaderConfig">
+          <config>
+            <passivation>true</passivation>
+            <shared>false</shared>
+
+            <purgeOnStartup>true</purgeOnStartup>
+
+            <cacheloader>
+               <class>org.jboss.cache.loader.FileCacheLoader</class>
+               <properties>
+                  location=${java.io.tmpdir}${/}sfsb
+               </properties>
+               <async>false</async>
+               <fetchPersistentState>true</fetchPersistentState>
+               <ignoreModifications>false</ignoreModifications>
+               <checkCharacterPortability>false</checkCharacterPortability>
+            </cacheloader>
+
+          </config>
+        </attribute>
+
+        <!-- SFSBs use JBoss Cache eviction -->
+        <attribute name="EvictionPolicyConfig">
+          <config>
+            <attribute name="wakeUpIntervalSeconds">5</attribute>
+            <!-- Name of the DEFAULT eviction policy class. -->
+            <attribute name="policyClass">org.jboss.cache.eviction.NullEvictionPolicy</attribute>
+            <!--  Cache wide default region -->
+            <region name="/_default_"/>
+            <!-- EJB3 integration code will programatically create
+                 other regions as beans are deployed -->
+          </config>
+        </attribute>
+
+    </cache-config>
+    
+</cache-configs>

Added: core/trunk/src/test/resources/configs/integration/web-session-cache-configs.xml
===================================================================
--- core/trunk/src/test/resources/configs/integration/web-session-cache-configs.xml	                        (rev 0)
+++ core/trunk/src/test/resources/configs/integration/web-session-cache-configs.xml	2008-11-24 07:01:36 UTC (rev 7186)
@@ -0,0 +1,372 @@
+<cache-configs>
+
+    <!-- 
+      | A config appropriate for HttpSession caches.
+      | Not for use with FIELD replication granularity webapps.
+    -->
+    <cache-config name="standard-session-cache">
+
+        <attribute name="TransactionManagerLookupClass">org.jboss.cache.transaction.BatchModeTransactionManagerLookup</attribute>
+
+        <attribute name="NodeLockingScheme">PESSIMISTIC</attribute>
+        <attribute name="IsolationLevel">REPEATABLE_READ</attribute>
+
+        <!-- Valid modes are REPL_ASYNC and REPL_SYNC -->
+        <attribute name="CacheMode">REPL_ASYNC</attribute>
+
+        <!-- Name of cluster. Needs to be the same for all members, in order
+             to find each other -->
+        <attribute name="ClusterName">${jboss.partition.name:JBCTest}-SessionCache</attribute>
+        
+        <!-- Use a UDP (multicast) based stack. Need JGroups flow control (FC)
+             because we are using asynchronous replication.
+        -->
+        <attribute name="MultiplexerStack">${jboss.default.jgroups.stack:udp}</attribute>
+
+        <!-- Must fetch state. -->
+        <attribute name="FetchInMemoryState">true</attribute>
+
+        <!--
+          The max amount of time (in milliseconds) we wait until the
+          state (ie. the contents of the cache) are retrieved from
+          existing members at startup.
+        -->
+        <attribute name="StateRetrievalTimeout">60000</attribute>
+
+        <!--
+            Number of milliseconds to wait until all responses for a
+            synchronous call have been received.
+        -->
+        <attribute name="SyncReplTimeout">17500</attribute>
+
+        <!-- Max number of milliseconds to wait for a lock acquisition -->
+        <attribute name="LockAcquisitionTimeout">15000</attribute>
+
+        <!-- Region-based marshalling is not needed. -->
+        <attribute name="UseRegionBasedMarshalling">false</attribute>
+        <!-- Must match the value of "UseRegionBasedMarshalling" -->
+        <attribute name="InactiveOnStartup">false</attribute>
+
+        <!-- Disable asynchronous RPC marshalling/sending -->
+        <attribute name="SerializationExecutorPoolSize">0</attribute>
+        
+        <!-- We have no asynchronous notification listeners -->
+        <attribute name="ListenerAsyncPoolSize">0</attribute>
+           
+        <attribute name="ExposeManagementStatistics">true</attribute>
+
+        <!-- Buddy Replication config -->
+        <attribute name="BuddyReplicationConfig">
+          <config>
+            <buddyReplicationEnabled>false</buddyReplicationEnabled>
+            <buddyLocatorClass>org.jboss.cache.buddyreplication.NextMemberBuddyLocator</buddyLocatorClass>
+            <!-- numBuddies is the number of backup nodes each node maintains.  
+                 ignoreColocatedBuddies means that each node will *try* to 
+                 select a buddy on a different physical host. If not able to do 
+                 so though, it will fall back to colocated nodes. -->
+            <buddyLocatorProperties>
+               numBuddies = 1
+               ignoreColocatedBuddies = true
+            </buddyLocatorProperties>
+
+            <!-- A way to specify a preferred replication group.  If specified, 
+                 we try and pick a buddy why shares the same pool name (falling 
+                 back to other buddies if not available).  This allows the sysdmin 
+                 to hint at backup buddies are picked, so for example, nodes may 
+                 be hinted topick buddies on a different physical rack or power 
+                 supply for added fault tolerance.  
+            -->
+            <buddyPoolName>default</buddyPoolName>
+            <!-- communication timeout for inter-buddy group organisation 
+                 messages (such as assigning to and removing from groups -->
+            <buddyCommunicationTimeout>17500</buddyCommunicationTimeout>
+
+            <!-- Do not change these -->
+            <autoDataGravitation>false</autoDataGravitation>
+            <dataGravitationRemoveOnFind>true</dataGravitationRemoveOnFind>
+            <dataGravitationSearchBackupTrees>true</dataGravitationSearchBackupTrees>
+
+          </config>
+        </attribute>
+        
+        <!-- Cache Loader for session passivation --> 
+        <attribute name="CacheLoaderConfig">
+          <config>
+            <passivation>true</passivation>
+            <shared>false</shared>
+
+            <purgeOnStartup>true</purgeOnStartup>
+
+            <cacheloader>
+               <class>org.jboss.cache.loader.FileCacheLoader</class>
+               <properties>
+                  location=${java.io.tmpdir}${/}session
+               </properties>
+               <async>false</async>
+               <fetchPersistentState>true</fetchPersistentState>
+               <ignoreModifications>false</ignoreModifications>
+               <checkCharacterPortability>false</checkCharacterPortability>
+            </cacheloader>
+
+          </config>
+        </attribute>
+
+        <!-- 
+           JBoss Cache eviction is not needed; webapp or SFSB container 
+           manages eviction by itself.
+        -->
+
+    </cache-config>
+
+    <!-- 
+      | A config appropriate for HttpSession caches for webapps 
+      | that use FIELD replication granularity.
+    -->
+    <cache-config name="field-granularity-session-cache">
+
+        <attribute name="TransactionManagerLookupClass">org.jboss.cache.transaction.BatchModeTransactionManagerLookup</attribute>
+
+        <attribute name="NodeLockingScheme">PESSIMISTIC</attribute>
+        <attribute name="IsolationLevel">REPEATABLE_READ</attribute>
+
+        <!-- Valid modes are LOCAL
+                             REPL_ASYNC
+                             REPL_SYNC
+        -->
+        <attribute name="CacheMode">REPL_ASYNC</attribute>
+
+        <!-- Name of cluster. Needs to be the same for all members, in order
+             to find each other -->
+        <attribute name="ClusterName">${jboss.partition.name:JBCTest}-FieldSessionCache</attribute>
+        
+        <!-- Use a UDP (multicast) based stack. Need JGroups flow control (FC)
+             because we are using asynchronous replication.
+        -->
+        <attribute name="MultiplexerStack">${jboss.default.jgroups.stack:udp}</attribute>
+
+        <!-- Must fetch state. -->
+        <attribute name="FetchInMemoryState">true</attribute>
+
+        <!--
+          The max amount of time (in milliseconds) we wait until the
+          state (ie. the contents of the cache) are retrieved from
+          existing members at startup.
+        -->
+        <attribute name="StateRetrievalTimeout">60000</attribute>
+
+        <!--
+            Number of milliseconds to wait until all responses for a
+            synchronous call have been received.
+        -->
+        <attribute name="SyncReplTimeout">17500</attribute>
+
+        <!-- Max number of milliseconds to wait for a lock acquisition -->
+        <attribute name="LockAcquisitionTimeout">15000</attribute>
+
+        <!--
+          Indicate whether to use marshalling or not. Set this to true if you 
+          are running under a scoped class loader, e.g., inside an application 
+          server. Default is "false".
+        -->
+        <attribute name="UseRegionBasedMarshalling">true</attribute>
+        <!-- Must match the value of "useRegionBasedMarshalling" -->
+        <attribute name="InactiveOnStartup">true</attribute>
+
+        <!-- Disable asynchronous RPC marshalling/sending -->
+        <attribute name="SerializationExecutorPoolSize">0</attribute>
+        
+        <!-- We have no asynchronous notification listeners -->
+        <attribute name="ListenerAsyncPoolSize">0</attribute>
+           
+        <attribute name="ExposeManagementStatistics">true</attribute>
+
+        <!-- Buddy Replication config -->
+        <attribute name="BuddyReplicationConfig">
+          <config>
+            <buddyReplicationEnabled>false</buddyReplicationEnabled>
+            <buddyLocatorClass>org.jboss.cache.buddyreplication.NextMemberBuddyLocator</buddyLocatorClass>
+            <!-- numBuddies is the number of backup nodes each node maintains.  
+                 ignoreColocatedBuddies means that each node will *try* to 
+                 select a buddy on a different physical host. If not able to do 
+                 so though, it will fall back to colocated nodes. -->
+            <buddyLocatorProperties>
+               numBuddies = 1
+               ignoreColocatedBuddies = true
+            </buddyLocatorProperties>
+
+            <!-- A way to specify a preferred replication group.  If specified, 
+                 we try and pick a buddy why shares the same pool name (falling 
+                 back to other buddies if not available).  This allows the sysdmin 
+                 to hint at backup buddies are picked, so for example, nodes may 
+                 be hinted topick buddies on a different physical rack or power 
+                 supply for added fault tolerance.  
+            -->
+            <buddyPoolName>default</buddyPoolName>
+            <!-- communication timeout for inter-buddy group organisation 
+                 messages (such as assigning to and removing from groups -->
+            <buddyCommunicationTimeout>17500</buddyCommunicationTimeout>
+
+            <!-- Do not change these -->
+            <autoDataGravitation>false</autoDataGravitation>
+            <dataGravitationRemoveOnFind>true</dataGravitationRemoveOnFind>
+            <dataGravitationSearchBackupTrees>true</dataGravitationSearchBackupTrees>
+
+          </config>
+        </attribute>
+        
+        <!-- Cache Loader for session passivation --> 
+        <attribute name="CacheLoaderConfig">
+          <config>
+            <passivation>true</passivation>
+            <shared>false</shared>
+
+            <purgeOnStartup>true</purgeOnStartup>
+
+            <cacheloader>
+               <class>org.jboss.cache.loader.FileCacheLoader</class>
+               <properties>
+                  location=${java.io.tmpdir}${/}field-session
+               </properties>
+               <async>false</async>
+               <fetchPersistentState>true</fetchPersistentState>
+               <ignoreModifications>false</ignoreModifications>
+               <checkCharacterPortability>false</checkCharacterPortability>
+            </cacheloader>
+
+          </config>
+        </attribute>
+
+        <!-- FIELD granularity webapps use JBoss Cache eviction -->
+        <attribute name="EvictionPolicyConfig">
+          <config>
+            <attribute name="wakeUpIntervalSeconds">5</attribute>
+            <!-- Name of the DEFAULT eviction policy class. -->
+            <attribute name="policyClass">org.jboss.cache.eviction.NullEvictionPolicy</attribute>
+            <!--  Cache wide default region -->
+            <region name="/_default_"/>
+            <!-- JBossWeb integration code will programatically create
+                 other regions as webapps are deployed -->
+          </config>
+        </attribute>
+
+    </cache-config>
+
+    <!-- 
+      | A buddy replication enabled config appropriate for HttpSession caches.
+      | Not for use with FIELD replication granularity webapps.
+    -->
+    <cache-config name="br-standard-session-cache">
+
+        <attribute name="TransactionManagerLookupClass">org.jboss.cache.transaction.BatchModeTransactionManagerLookup</attribute>
+
+        <attribute name="NodeLockingScheme">PESSIMISTIC</attribute>
+        <attribute name="IsolationLevel">REPEATABLE_READ</attribute>
+
+        <!-- Valid modes are REPL_ASYNC and REPL_SYNC -->
+        <attribute name="CacheMode">REPL_ASYNC</attribute>
+
+        <!-- Name of cluster. Needs to be the same for all members, in order
+             to find each other -->
+        <attribute name="ClusterName">${jboss.partition.name:JBCTest}-SessionCache</attribute>
+        
+        <!-- Use a UDP (multicast) based stack. Need JGroups flow control (FC)
+             because we are using asynchronous replication.
+        -->
+        <attribute name="MultiplexerStack">${jboss.default.jgroups.stack:udp}</attribute>
+
+        <!-- Must fetch state. -->
+        <attribute name="FetchInMemoryState">true</attribute>
+
+        <!--
+          The max amount of time (in milliseconds) we wait until the
+          state (ie. the contents of the cache) are retrieved from
+          existing members at startup.
+        -->
+        <attribute name="StateRetrievalTimeout">60000</attribute>
+
+        <!--
+            Number of milliseconds to wait until all responses for a
+            synchronous call have been received.
+        -->
+        <attribute name="SyncReplTimeout">17500</attribute>
+
+        <!-- Max number of milliseconds to wait for a lock acquisition -->
+        <attribute name="LockAcquisitionTimeout">15000</attribute>
+
+        <!-- Region-based marshalling is not needed. -->
+        <attribute name="UseRegionBasedMarshalling">false</attribute>
+        <!-- Must match the value of "UseRegionBasedMarshalling" -->
+        <attribute name="InactiveOnStartup">false</attribute>
+
+        <!-- Disable asynchronous RPC marshalling/sending -->
+        <attribute name="SerializationExecutorPoolSize">0</attribute>
+        
+        <!-- We have no asynchronous notification listeners -->
+        <attribute name="ListenerAsyncPoolSize">0</attribute>
+           
+        <attribute name="ExposeManagementStatistics">true</attribute>
+
+        <!-- Buddy Replication config -->
+        <attribute name="BuddyReplicationConfig">
+          <config>
+            <buddyReplicationEnabled>true</buddyReplicationEnabled>
+            <buddyLocatorClass>org.jboss.cache.buddyreplication.NextMemberBuddyLocator</buddyLocatorClass>
+            <!-- numBuddies is the number of backup nodes each node maintains.  
+                 ignoreColocatedBuddies means that each node will *try* to 
+                 select a buddy on a different physical host. If not able to do 
+                 so though, it will fall back to colocated nodes. -->
+            <buddyLocatorProperties>
+               numBuddies = 1
+               ignoreColocatedBuddies = true
+            </buddyLocatorProperties>
+
+            <!-- A way to specify a preferred replication group.  If specified, 
+                 we try and pick a buddy why shares the same pool name (falling 
+                 back to other buddies if not available).  This allows the sysdmin 
+                 to hint at backup buddies are picked, so for example, nodes may 
+                 be hinted topick buddies on a different physical rack or power 
+                 supply for added fault tolerance.  
+            -->
+            <buddyPoolName>default</buddyPoolName>
+            <!-- communication timeout for inter-buddy group organisation 
+                 messages (such as assigning to and removing from groups -->
+            <buddyCommunicationTimeout>17500</buddyCommunicationTimeout>
+
+            <!-- Do not change these -->
+            <autoDataGravitation>false</autoDataGravitation>
+            <dataGravitationRemoveOnFind>true</dataGravitationRemoveOnFind>
+            <dataGravitationSearchBackupTrees>true</dataGravitationSearchBackupTrees>
+
+          </config>
+        </attribute>
+        
+        <!-- Cache Loader for session passivation --> 
+        <attribute name="CacheLoaderConfig">
+          <config>
+            <passivation>true</passivation>
+            <shared>false</shared>
+
+            <purgeOnStartup>true</purgeOnStartup>
+
+            <cacheloader>
+               <class>org.jboss.cache.loader.FileCacheLoader</class>
+               <properties>
+                  location=${java.io.tmpdir}${/}session
+               </properties>
+               <async>false</async>
+               <fetchPersistentState>true</fetchPersistentState>
+               <ignoreModifications>false</ignoreModifications>
+               <checkCharacterPortability>false</checkCharacterPortability>
+            </cacheloader>
+
+          </config>
+        </attribute>
+
+        <!-- 
+           JBoss Cache eviction is not needed; webapp or SFSB container 
+           manages eviction by itself.
+        -->
+
+    </cache-config>
+    
+</cache-configs>




More information about the jbosscache-commits mailing list