[hibernate-commits] Hibernate SVN: r17386 - in core/trunk/cache-infinispan: src and 45 other directories.

hibernate-commits at lists.jboss.org hibernate-commits at lists.jboss.org
Fri Aug 21 04:39:34 EDT 2009


Author: galder.zamarreno at jboss.com
Date: 2009-08-21 04:39:33 -0400 (Fri, 21 Aug 2009)
New Revision: 17386

Added:
   core/trunk/cache-infinispan/pom.xml
   core/trunk/cache-infinispan/src/
   core/trunk/cache-infinispan/src/main/
   core/trunk/cache-infinispan/src/main/java/
   core/trunk/cache-infinispan/src/main/java/org/
   core/trunk/cache-infinispan/src/main/java/org/hibernate/
   core/trunk/cache-infinispan/src/main/java/org/hibernate/cache/
   core/trunk/cache-infinispan/src/main/java/org/hibernate/cache/infinispan/
   core/trunk/cache-infinispan/src/main/java/org/hibernate/cache/infinispan/InfinispanRegionFactory.java
   core/trunk/cache-infinispan/src/main/java/org/hibernate/cache/infinispan/TypeOverrides.java
   core/trunk/cache-infinispan/src/main/java/org/hibernate/cache/infinispan/access/
   core/trunk/cache-infinispan/src/main/java/org/hibernate/cache/infinispan/access/TransactionalAccessDelegate.java
   core/trunk/cache-infinispan/src/main/java/org/hibernate/cache/infinispan/collection/
   core/trunk/cache-infinispan/src/main/java/org/hibernate/cache/infinispan/collection/CollectionRegionImpl.java
   core/trunk/cache-infinispan/src/main/java/org/hibernate/cache/infinispan/collection/ReadOnlyAccess.java
   core/trunk/cache-infinispan/src/main/java/org/hibernate/cache/infinispan/collection/TransactionalAccess.java
   core/trunk/cache-infinispan/src/main/java/org/hibernate/cache/infinispan/entity/
   core/trunk/cache-infinispan/src/main/java/org/hibernate/cache/infinispan/entity/EntityRegionImpl.java
   core/trunk/cache-infinispan/src/main/java/org/hibernate/cache/infinispan/entity/ReadOnlyAccess.java
   core/trunk/cache-infinispan/src/main/java/org/hibernate/cache/infinispan/entity/TransactionalAccess.java
   core/trunk/cache-infinispan/src/main/java/org/hibernate/cache/infinispan/impl/
   core/trunk/cache-infinispan/src/main/java/org/hibernate/cache/infinispan/impl/BaseGeneralDataRegion.java
   core/trunk/cache-infinispan/src/main/java/org/hibernate/cache/infinispan/impl/BaseRegion.java
   core/trunk/cache-infinispan/src/main/java/org/hibernate/cache/infinispan/impl/BaseTransactionalDataRegion.java
   core/trunk/cache-infinispan/src/main/java/org/hibernate/cache/infinispan/query/
   core/trunk/cache-infinispan/src/main/java/org/hibernate/cache/infinispan/query/QueryResultsRegionImpl.java
   core/trunk/cache-infinispan/src/main/java/org/hibernate/cache/infinispan/timestamp/
   core/trunk/cache-infinispan/src/main/java/org/hibernate/cache/infinispan/timestamp/TimestampTypeOverrides.java
   core/trunk/cache-infinispan/src/main/java/org/hibernate/cache/infinispan/timestamp/TimestampsRegionImpl.java
   core/trunk/cache-infinispan/src/main/java/org/hibernate/cache/infinispan/tm/
   core/trunk/cache-infinispan/src/main/java/org/hibernate/cache/infinispan/tm/HibernateTransactionManagerLookup.java
   core/trunk/cache-infinispan/src/main/java/org/hibernate/cache/infinispan/util/
   core/trunk/cache-infinispan/src/main/java/org/hibernate/cache/infinispan/util/CacheHelper.java
   core/trunk/cache-infinispan/src/main/resources/
   core/trunk/cache-infinispan/src/main/resources/org/
   core/trunk/cache-infinispan/src/main/resources/org/hibernate/
   core/trunk/cache-infinispan/src/main/resources/org/hibernate/cache/
   core/trunk/cache-infinispan/src/main/resources/org/hibernate/cache/infinispan/
   core/trunk/cache-infinispan/src/main/resources/org/hibernate/cache/infinispan/builder/
   core/trunk/cache-infinispan/src/main/resources/org/hibernate/cache/infinispan/builder/infinispan-configs.xml
   core/trunk/cache-infinispan/src/test/
   core/trunk/cache-infinispan/src/test/java/
   core/trunk/cache-infinispan/src/test/java/org/
   core/trunk/cache-infinispan/src/test/java/org/hibernate/
   core/trunk/cache-infinispan/src/test/java/org/hibernate/test/
   core/trunk/cache-infinispan/src/test/java/org/hibernate/test/cache/
   core/trunk/cache-infinispan/src/test/java/org/hibernate/test/cache/infinispan/
   core/trunk/cache-infinispan/src/test/java/org/hibernate/test/cache/infinispan/AbstractEntityCollectionRegionTestCase.java
   core/trunk/cache-infinispan/src/test/java/org/hibernate/test/cache/infinispan/AbstractGeneralDataRegionTestCase.java
   core/trunk/cache-infinispan/src/test/java/org/hibernate/test/cache/infinispan/AbstractNonFunctionalTestCase.java
   core/trunk/cache-infinispan/src/test/java/org/hibernate/test/cache/infinispan/AbstractRegionImplTestCase.java
   core/trunk/cache-infinispan/src/test/java/org/hibernate/test/cache/infinispan/InfinispanRegionFactoryTestCase.java
   core/trunk/cache-infinispan/src/test/java/org/hibernate/test/cache/infinispan/collection/
   core/trunk/cache-infinispan/src/test/java/org/hibernate/test/cache/infinispan/collection/AbstractCollectionRegionAccessStrategyTestCase.java
   core/trunk/cache-infinispan/src/test/java/org/hibernate/test/cache/infinispan/collection/AbstractReadOnlyAccessTestCase.java
   core/trunk/cache-infinispan/src/test/java/org/hibernate/test/cache/infinispan/collection/AbstractTransactionalAccessTestCase.java
   core/trunk/cache-infinispan/src/test/java/org/hibernate/test/cache/infinispan/collection/InvalidatedTransactionalTestCase.java
   core/trunk/cache-infinispan/src/test/java/org/hibernate/test/cache/infinispan/collection/ReadOnlyExtraAPITestCase.java
   core/trunk/cache-infinispan/src/test/java/org/hibernate/test/cache/infinispan/collection/ReadOnlyTestCase.java
   core/trunk/cache-infinispan/src/test/java/org/hibernate/test/cache/infinispan/collection/TransactionalExtraAPITestCase.java
   core/trunk/cache-infinispan/src/test/java/org/hibernate/test/cache/infinispan/entity/
   core/trunk/cache-infinispan/src/test/java/org/hibernate/test/cache/infinispan/entity/AbstractEntityRegionAccessStrategyTestCase.java
   core/trunk/cache-infinispan/src/test/java/org/hibernate/test/cache/infinispan/entity/AbstractReadOnlyAccessTestCase.java
   core/trunk/cache-infinispan/src/test/java/org/hibernate/test/cache/infinispan/entity/AbstractTransactionalAccessTestCase.java
   core/trunk/cache-infinispan/src/test/java/org/hibernate/test/cache/infinispan/entity/EntityRegionImplTestCase.java
   core/trunk/cache-infinispan/src/test/java/org/hibernate/test/cache/infinispan/entity/InvalidatedTransactionalTestCase.java
   core/trunk/cache-infinispan/src/test/java/org/hibernate/test/cache/infinispan/entity/ReadOnlyExtraAPITestCase.java
   core/trunk/cache-infinispan/src/test/java/org/hibernate/test/cache/infinispan/entity/ReadOnlyTestCase.java
   core/trunk/cache-infinispan/src/test/java/org/hibernate/test/cache/infinispan/entity/TransactionalExtraAPITestCase.java
   core/trunk/cache-infinispan/src/test/java/org/hibernate/test/cache/infinispan/functional/
   core/trunk/cache-infinispan/src/test/java/org/hibernate/test/cache/infinispan/functional/AbstractFunctionalTestCase.java
   core/trunk/cache-infinispan/src/test/java/org/hibernate/test/cache/infinispan/functional/BasicReadOnlyTestCase.java
   core/trunk/cache-infinispan/src/test/java/org/hibernate/test/cache/infinispan/functional/BasicTransactionalTestCase.java
   core/trunk/cache-infinispan/src/test/java/org/hibernate/test/cache/infinispan/functional/Contact.java
   core/trunk/cache-infinispan/src/test/java/org/hibernate/test/cache/infinispan/functional/Customer.java
   core/trunk/cache-infinispan/src/test/java/org/hibernate/test/cache/infinispan/functional/Item.java
   core/trunk/cache-infinispan/src/test/java/org/hibernate/test/cache/infinispan/functional/VersionedItem.java
   core/trunk/cache-infinispan/src/test/java/org/hibernate/test/cache/infinispan/functional/bulk/
   core/trunk/cache-infinispan/src/test/java/org/hibernate/test/cache/infinispan/functional/bulk/BulkOperationsTestCase.java
   core/trunk/cache-infinispan/src/test/java/org/hibernate/test/cache/infinispan/functional/classloader/
   core/trunk/cache-infinispan/src/test/java/org/hibernate/test/cache/infinispan/functional/classloader/Account.java
   core/trunk/cache-infinispan/src/test/java/org/hibernate/test/cache/infinispan/functional/classloader/AccountHolder.java
   core/trunk/cache-infinispan/src/test/java/org/hibernate/test/cache/infinispan/functional/classloader/CacheAccessListener.java
   core/trunk/cache-infinispan/src/test/java/org/hibernate/test/cache/infinispan/functional/classloader/ClassLoaderTestDAO.java
   core/trunk/cache-infinispan/src/test/java/org/hibernate/test/cache/infinispan/functional/classloader/IsolatedCacheTestSetup.java
   core/trunk/cache-infinispan/src/test/java/org/hibernate/test/cache/infinispan/functional/classloader/IsolatedClassLoaderTest.java
   core/trunk/cache-infinispan/src/test/java/org/hibernate/test/cache/infinispan/functional/classloader/SelectedClassnameClassLoader.java
   core/trunk/cache-infinispan/src/test/java/org/hibernate/test/cache/infinispan/functional/classloader/SelectedClassnameClassLoaderTestSetup.java
   core/trunk/cache-infinispan/src/test/java/org/hibernate/test/cache/infinispan/functional/cluster/
   core/trunk/cache-infinispan/src/test/java/org/hibernate/test/cache/infinispan/functional/cluster/AbstractDualNodeTestCase.java
   core/trunk/cache-infinispan/src/test/java/org/hibernate/test/cache/infinispan/functional/cluster/ClusterAwareRegionFactory.java
   core/trunk/cache-infinispan/src/test/java/org/hibernate/test/cache/infinispan/functional/cluster/DualNodeConnectionProviderImpl.java
   core/trunk/cache-infinispan/src/test/java/org/hibernate/test/cache/infinispan/functional/cluster/DualNodeJtaTransactionImpl.java
   core/trunk/cache-infinispan/src/test/java/org/hibernate/test/cache/infinispan/functional/cluster/DualNodeJtaTransactionManagerImpl.java
   core/trunk/cache-infinispan/src/test/java/org/hibernate/test/cache/infinispan/functional/cluster/DualNodeTransactionManagerLookup.java
   core/trunk/cache-infinispan/src/test/java/org/hibernate/test/cache/infinispan/functional/cluster/EntityCollectionInvalidationTestCase.java
   core/trunk/cache-infinispan/src/test/java/org/hibernate/test/cache/infinispan/functional/cluster/SessionRefreshTestCase.java
   core/trunk/cache-infinispan/src/test/java/org/hibernate/test/cache/infinispan/query/
   core/trunk/cache-infinispan/src/test/java/org/hibernate/test/cache/infinispan/query/QueryRegionImplTestCase.java
   core/trunk/cache-infinispan/src/test/java/org/hibernate/test/cache/infinispan/timestamp/
   core/trunk/cache-infinispan/src/test/java/org/hibernate/test/cache/infinispan/timestamp/TimestampsRegionImplTestCase.java
   core/trunk/cache-infinispan/src/test/java/org/hibernate/test/cache/infinispan/tm/
   core/trunk/cache-infinispan/src/test/java/org/hibernate/test/cache/infinispan/tm/XaConnectionProvider.java
   core/trunk/cache-infinispan/src/test/java/org/hibernate/test/cache/infinispan/tm/XaTransactionImpl.java
   core/trunk/cache-infinispan/src/test/java/org/hibernate/test/cache/infinispan/tm/XaTransactionManagerImpl.java
   core/trunk/cache-infinispan/src/test/java/org/hibernate/test/cache/infinispan/tm/XaTransactionManagerLookup.java
   core/trunk/cache-infinispan/src/test/java/org/hibernate/test/cache/infinispan/util/
   core/trunk/cache-infinispan/src/test/java/org/hibernate/test/cache/infinispan/util/BatchModeTransactionManagerLookup.java
   core/trunk/cache-infinispan/src/test/java/org/hibernate/test/cache/infinispan/util/CacheTestSupport.java
   core/trunk/cache-infinispan/src/test/java/org/hibernate/test/cache/infinispan/util/CacheTestUtil.java
   core/trunk/cache-infinispan/src/test/resources/
   core/trunk/cache-infinispan/src/test/resources/hibernate.properties
   core/trunk/cache-infinispan/src/test/resources/log4j.properties
   core/trunk/cache-infinispan/src/test/resources/org/
   core/trunk/cache-infinispan/src/test/resources/org/hibernate/
   core/trunk/cache-infinispan/src/test/resources/org/hibernate/test/
   core/trunk/cache-infinispan/src/test/resources/org/hibernate/test/cache/
   core/trunk/cache-infinispan/src/test/resources/org/hibernate/test/cache/infinispan/
   core/trunk/cache-infinispan/src/test/resources/org/hibernate/test/cache/infinispan/functional/
   core/trunk/cache-infinispan/src/test/resources/org/hibernate/test/cache/infinispan/functional/Contact.hbm.xml
   core/trunk/cache-infinispan/src/test/resources/org/hibernate/test/cache/infinispan/functional/Customer.hbm.xml
   core/trunk/cache-infinispan/src/test/resources/org/hibernate/test/cache/infinispan/functional/Item.hbm.xml
   core/trunk/cache-infinispan/src/test/resources/org/hibernate/test/cache/infinispan/functional/classloader/
   core/trunk/cache-infinispan/src/test/resources/org/hibernate/test/cache/infinispan/functional/classloader/Account.hbm.xml
Modified:
   core/trunk/cache-infinispan/
Log:
[HHH-4103] Initial commit.


Property changes on: core/trunk/cache-infinispan
___________________________________________________________________
Name: svn:ignore
   + target
.project
.classpath
.settings


Added: core/trunk/cache-infinispan/pom.xml
===================================================================
--- core/trunk/cache-infinispan/pom.xml	                        (rev 0)
+++ core/trunk/cache-infinispan/pom.xml	2009-08-21 08:39:33 UTC (rev 17386)
@@ -0,0 +1,160 @@
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.hibernate</groupId>
+        <artifactId>hibernate-parent</artifactId>
+        <version>3.5.0-SNAPSHOT</version>
+        <relativePath>../parent/pom.xml</relativePath>
+    </parent>
+    
+    <groupId>org.hibernate</groupId>
+    <artifactId>hibernate-infinispan</artifactId>
+    <packaging>jar</packaging>
+
+    <name>Hibernate Infinispan Integration</name>
+    <description>Integration of Hibernate with Infinispan</description>
+
+    <properties>
+      <version.infinispan>4.0.0-SNAPSHOT</version.infinispan>
+      <version.hsqldb>1.8.0.2</version.hsqldb>
+      <version.cglib>2.2</version.cglib>
+      <version.javassist>3.4.GA</version.javassist>
+      <skipUnitTests>true</skipUnitTests>
+      <!-- 
+         Following is the default jgroups mcast address.  If you find the testsuite runs very slowly, there
+         may be problems with multicast on the interface JGroups uses by default on your machine. You can
+         try to resolve setting 'jgroups.bind_addr' as a system-property to the jvm launching maven and
+         setting the value to an interface where you know multicast works
+      -->
+      <jgroups.bind_addr>127.0.0.1</jgroups.bind_addr>
+    </properties>
+
+    <dependencies>
+        <dependency>
+            <groupId>${groupId}</groupId>
+            <artifactId>hibernate-core</artifactId>
+            <version>${version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.infinispan</groupId>
+            <artifactId>infinispan-core</artifactId>
+            <version>${version.infinispan}</version> 
+        </dependency>
+        
+        <!-- test dependencies -->
+        <dependency>
+            <groupId>${groupId}</groupId>
+            <artifactId>hibernate-testing</artifactId>
+            <version>${version}</version>
+            <!-- <scope>test</scope> TODO fix this -->
+        </dependency>
+        <dependency>
+            <groupId>org.infinispan</groupId>
+            <artifactId>infinispan-core</artifactId>
+            <version>${version.infinispan}</version>
+            <type>test-jar</type>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>hsqldb</groupId>
+            <artifactId>hsqldb</artifactId>
+            <version>${version.hsqldb}</version>
+            <scope>test</scope>
+        </dependency>
+        <!-- this is optional on core :( and needed for testing -->
+        <dependency>
+            <groupId>cglib</groupId>
+            <artifactId>cglib</artifactId>
+            <version>${version.cglib}</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>javassist</groupId>
+            <artifactId>javassist</artifactId>
+            <version>${version.javassist}</version>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-compiler-plugin</artifactId>
+                <configuration>
+                    <source>1.5</source>
+                    <target>1.5</target>
+                </configuration>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-surefire-plugin</artifactId>
+                <configuration>
+                    <excludes>
+                        <!-- Skip a long-running test of a prototype class -->
+                        <exclude>**/ClusteredConcurrentTimestampRegionTestCase.java</exclude>
+                    </excludes>
+                    <systemProperties>
+                        <property>
+                            <name>hibernate.test.validatefailureexpected</name>
+                            <value>true</value>
+                        </property>
+                        <property>
+                            <name>jgroups.bind_addr</name>
+                            <value>${jgroups.bind_addr}</value>
+                        </property>
+                        <!-- There are problems with multicast and IPv6 on some
+                             OS/JDK combos, so we tell Java to use IPv4. If you
+                             have problems with multicast when running the tests
+                             you can try setting this to 'false', although typically
+                             that won't be helpful.
+                        -->
+                        <property>
+                            <name>java.net.preferIPv4Stack</name>
+                            <value>true</value>
+                        </property>
+                        <!-- Tell JGroups to only wait a short time for PING 
+                             responses before determining coordinator. Speeds cluster
+                             formation during integration tests. (This is too
+                             low a value for a real system; only use for tests.)
+                        -->
+                        <property>
+                            <name>jgroups.ping.timeout</name>
+                            <value>500</value>
+                        </property>
+                        <!-- Tell JGroups to only require one PING response
+                             before determining coordinator. Speeds cluster
+                             formation during integration tests. (This is too
+                             low a value for a real system; only use for tests.)
+                        -->
+                        <property>
+                            <name>jgroups.ping.num_initial_members</name>
+                            <value>1</value>
+                        </property>
+                        <!-- Disable the JGroups message bundling feature
+                             to speed tests and avoid FLUSH issue -->
+                        <property>
+                            <name>jgroups.udp.enable_bundling</name>
+                            <value>false</value>
+                        </property>
+                    </systemProperties>
+                    <skipExec>${skipUnitTests}</skipExec>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+
+    <profiles>
+        <profile>
+            <id>test</id>
+            <activation>
+                <activeByDefault>false</activeByDefault>
+            </activation>
+            <properties>
+                <skipUnitTests>false</skipUnitTests>
+            </properties>
+        </profile>
+     </profiles>
+</project>

Added: core/trunk/cache-infinispan/src/main/java/org/hibernate/cache/infinispan/InfinispanRegionFactory.java
===================================================================
--- core/trunk/cache-infinispan/src/main/java/org/hibernate/cache/infinispan/InfinispanRegionFactory.java	                        (rev 0)
+++ core/trunk/cache-infinispan/src/main/java/org/hibernate/cache/infinispan/InfinispanRegionFactory.java	2009-08-21 08:39:33 UTC (rev 17386)
@@ -0,0 +1,429 @@
+package org.hibernate.cache.infinispan;
+
+import java.io.IOException;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Properties;
+import java.util.Set;
+
+import javax.transaction.TransactionManager;
+
+import org.hibernate.cache.CacheDataDescription;
+import org.hibernate.cache.CacheException;
+import org.hibernate.cache.CollectionRegion;
+import org.hibernate.cache.EntityRegion;
+import org.hibernate.cache.QueryResultsRegion;
+import org.hibernate.cache.RegionFactory;
+import org.hibernate.cache.TimestampsRegion;
+import org.hibernate.cache.infinispan.collection.CollectionRegionImpl;
+import org.hibernate.cache.infinispan.entity.EntityRegionImpl;
+import org.hibernate.cache.infinispan.query.QueryResultsRegionImpl;
+import org.hibernate.cache.infinispan.timestamp.TimestampsRegionImpl;
+import org.hibernate.cache.infinispan.timestamp.TimestampTypeOverrides;
+import org.hibernate.cache.infinispan.tm.HibernateTransactionManagerLookup;
+import org.hibernate.cfg.Settings;
+import org.hibernate.util.PropertiesHelper;
+import org.infinispan.Cache;
+import org.infinispan.config.Configuration;
+import org.infinispan.manager.CacheManager;
+import org.infinispan.manager.DefaultCacheManager;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * A {@link RegionFactory} for <a href="http://www.jboss.org/infinispan">Infinispan</a>-backed cache
+ * regions.
+ * 
+ * @author Chris Bredesen
+ * @author Galder Zamarreño
+ * @since 3.5
+ */
+public class InfinispanRegionFactory implements RegionFactory {
+   
+   private static final Logger log = LoggerFactory.getLogger(InfinispanRegionFactory.class);
+   
+   private static final String PREFIX = "hibernate.cache.infinispan.";
+   
+   private static final String CONFIG_SUFFIX = ".cfg";
+   
+   private static final String STRATEGY_SUFFIX = ".eviction.strategy";
+
+   private static final String WAKE_UP_INTERVAL_SUFFIX = ".eviction.wake_up_interval";
+   
+   private static final String MAX_ENTRIES_SUFFIX = ".eviction.max_entries";
+   
+   private static final String LIFESPAN_SUFFIX = ".eviction.lifespan";
+   
+   private static final String MAX_IDLE_SUFFIX = ".eviction.max_idle";
+   
+   /** 
+    * Classpath or filesystem resource containing Infinispan configurations the factory should use.
+    * 
+    * @see #DEF_INFINISPAN_CONFIG_RESOURCE
+    */
+   public static final String INFINISPAN_CONFIG_RESOURCE_PROP = "hibernate.cache.infinispan.cfg";
+
+   private static final String ENTITY_KEY = "entity";
+   
+   /**
+    * Name of the configuration that should be used for entity caches.
+    * 
+    * @see #DEF_ENTITY_RESOURCE
+    */
+   public static final String ENTITY_CACHE_RESOURCE_PROP = PREFIX + ENTITY_KEY + CONFIG_SUFFIX;
+   
+   private static final String COLLECTION_KEY = "collection";
+   
+   /**
+    * Name of the configuration that should be used for collection caches.
+    * No default value, as by default we try to use the same Infinispan cache
+    * instance we use for entity caching.
+    * 
+    * @see #ENTITY_CACHE_RESOURCE_PROP
+    * @see #DEF_ENTITY_RESOURCE
+    */
+   public static final String COLLECTION_CACHE_RESOURCE_PROP = PREFIX + COLLECTION_KEY + CONFIG_SUFFIX;
+
+   private static final String TIMESTAMPS_KEY = "timestamps";
+
+   /**
+    * Name of the configuration that should be used for timestamp caches.
+    * 
+    * @see #DEF_TS_RESOURCE
+    */
+   public static final String TIMESTAMPS_CACHE_RESOURCE_PROP = PREFIX + TIMESTAMPS_KEY + CONFIG_SUFFIX;
+
+   private static final String QUERY_KEY = "query";
+
+   /**
+    * Name of the configuration that should be used for query caches.
+    * 
+    * @see #DEF_QUERY_RESOURCE
+    */
+   public static final String QUERY_CACHE_RESOURCE_PROP = PREFIX + QUERY_KEY + CONFIG_SUFFIX;
+   
+   /**
+    * Default value for {@link #INFINISPAN_RESOURCE_PROP}. Specifies the "infinispan-configs.xml" file in this package.
+    */
+   public static final String DEF_INFINISPAN_CONFIG_RESOURCE = "org/hibernate/cache/infinispan/builder/infinispan-configs.xml";
+   
+   /**
+    * Default value for {@link #ENTITY_CACHE_RESOURCE_PROP}.
+    */
+   public static final String DEF_ENTITY_RESOURCE = "entity";
+   
+   /**
+    * Default value for {@link #TIMESTAMPS_CACHE_RESOURCE_PROP}.
+    */
+   public static final String DEF_TIMESTAMPS_RESOURCE = "timestamps";
+   
+   /**
+    * Default value for {@link #QUERY_CACHE_RESOURCE_PROP}.
+    */
+   public static final String DEF_QUERY_RESOURCE = "local-query";
+   
+   private CacheManager manager;
+   
+   private final Map<String, TypeOverrides> typeOverrides = new HashMap<String, TypeOverrides>();
+   
+   private final Set<String> definedConfigurations = new HashSet<String>();
+   
+   private org.infinispan.transaction.lookup.TransactionManagerLookup transactionManagerlookup;
+   
+   private TransactionManager transactionManager;
+
+   /**
+    * Create a new instance using the default configuration.
+    */
+   public InfinispanRegionFactory() {
+   }
+
+   /**
+    * Create a new instance using conifguration properties in <code>props</code>.
+    * 
+    * @param props
+    *           Environmental properties; currently unused.
+    */
+   public InfinispanRegionFactory(Properties props) {
+   }
+
+   /** {@inheritDoc} */
+   public CollectionRegion buildCollectionRegion(String regionName, Properties properties, CacheDataDescription metadata) throws CacheException {
+      log.debug("Building collection cache region [" + regionName + "]");
+      Cache cache = getCache(regionName, COLLECTION_KEY, properties);
+      return new CollectionRegionImpl(cache, regionName, metadata, transactionManager);
+   }
+
+   /** {@inheritDoc} */
+   public EntityRegion buildEntityRegion(String regionName, Properties properties, CacheDataDescription metadata) throws CacheException {
+      if (log.isDebugEnabled()) log.debug("Building entity cache region [" + regionName + "]");
+      Cache cache = getCache(regionName, ENTITY_KEY, properties);
+      return new EntityRegionImpl(cache, regionName, metadata, transactionManager);
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   public QueryResultsRegion buildQueryResultsRegion(String regionName, Properties properties)
+            throws CacheException {
+      log.debug("Building query results cache region [" + regionName + "]");
+      String cacheName = typeOverrides.get(QUERY_KEY).getCacheName();
+      return new QueryResultsRegionImpl(manager.getCache(cacheName), regionName, properties, transactionManager);
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   public TimestampsRegion buildTimestampsRegion(String regionName, Properties properties)
+            throws CacheException {
+      log.debug("Building timestamps cache region [" + regionName + "]");
+      String cacheName = typeOverrides.get(TIMESTAMPS_KEY).getCacheName();
+      return new TimestampsRegionImpl(manager.getCache(cacheName), regionName, transactionManager);
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   public boolean isMinimalPutsEnabledByDefault() {
+      return false;
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   public long nextTimestamp() {
+      return 0;
+   }
+   
+   public void setCacheManager(CacheManager manager) {
+      this.manager = manager;
+   }
+
+   public CacheManager getCacheManager() {
+      return manager;
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   public void start(Settings settings, Properties properties) throws CacheException {
+      log.debug("Starting Infinispan CacheManager");
+      try {
+         transactionManagerlookup = new HibernateTransactionManagerLookup(settings, properties);
+         transactionManager = transactionManagerlookup.getTransactionManager();
+         
+         String configLoc = PropertiesHelper.getString(INFINISPAN_CONFIG_RESOURCE_PROP, properties, DEF_INFINISPAN_CONFIG_RESOURCE);
+         manager = createCacheManager(configLoc);
+         initGenericDataTypeOverrides();
+         Enumeration keys = properties.propertyNames();
+         while (keys.hasMoreElements()) {
+            String key = (String) keys.nextElement();
+            int prefixLoc = -1;
+            if ((prefixLoc = key.indexOf(PREFIX)) != -1) {
+               dissectProperty(prefixLoc, key, properties);
+            }
+         }
+         defineGenericDataTypeCacheConfigurations(settings, properties);
+      } catch (CacheException ce) {
+         throw ce;
+      } catch (Throwable t) {
+          throw new CacheException("Unable to start region factory", t);
+      }
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   public void stop() {
+      log.debug("Stopping Infinispan CacheManager");
+      manager.stop();
+   }
+   
+   /**
+    * Returns an unmodifiable map containing configured entity/collection type configuration overrides.
+    * This method should be used primarily for testing/checking purpouses.
+    * 
+    * @return an unmodifiable map.
+    */
+   public Map<String, TypeOverrides> getTypeOverrides() {
+      return Collections.unmodifiableMap(typeOverrides);
+   }
+   
+   public Set<String> getDefinedConfigurations() {
+      return Collections.unmodifiableSet(definedConfigurations);
+   }
+
+   protected CacheManager createCacheManager(String configLoc) throws CacheException {
+      try {
+         return new DefaultCacheManager(configLoc);
+      } catch (IOException e) {
+         throw new CacheException("Unable to create default cache manager", e);
+      }
+   }
+
+   private Map<String, TypeOverrides> initGenericDataTypeOverrides() {
+      TypeOverrides entityOverrides = new TypeOverrides();
+      entityOverrides.setCacheName(DEF_ENTITY_RESOURCE);
+      typeOverrides.put(ENTITY_KEY, entityOverrides);
+      TypeOverrides collectionOverrides = new TypeOverrides();
+      collectionOverrides.setCacheName(DEF_ENTITY_RESOURCE);
+      typeOverrides.put(COLLECTION_KEY, collectionOverrides);
+      TypeOverrides timestampOverrides = new TimestampTypeOverrides();
+      timestampOverrides.setCacheName(DEF_TIMESTAMPS_RESOURCE);
+      typeOverrides.put(TIMESTAMPS_KEY, timestampOverrides);
+      TypeOverrides queryOverrides = new TypeOverrides();
+      queryOverrides.setCacheName(DEF_QUERY_RESOURCE);
+      typeOverrides.put(QUERY_KEY, queryOverrides);
+      return typeOverrides;
+   }
+   
+//   private boolean isGenericDataTypeProperty(String property) {
+//      return property.startsWith(PREFIX + ENTITY_KEY) || property.startsWith(PREFIX + COLLECTION_KEY) 
+//            || property.startsWith(PREFIX + QUERY_KEY) || property.startsWith(PREFIX + TIMESTAMP_KEY);
+//   }
+   
+   private void dissectProperty(int prefixLoc, String key, Properties properties) {
+      TypeOverrides cfgOverride = null;
+      int suffixLoc = -1;
+      if (!key.equals(INFINISPAN_CONFIG_RESOURCE_PROP) && (suffixLoc = key.indexOf(CONFIG_SUFFIX)) != -1) {
+         cfgOverride = getOrCreateConfig(prefixLoc, key, suffixLoc);
+         cfgOverride.setCacheName(PropertiesHelper.extractPropertyValue(key, properties));
+      } else if ((suffixLoc = key.indexOf(STRATEGY_SUFFIX)) != -1) {
+         cfgOverride = getOrCreateConfig(prefixLoc, key, suffixLoc);
+         cfgOverride.setEvictionStrategy(PropertiesHelper.extractPropertyValue(key, properties));
+      } else if ((suffixLoc = key.indexOf(WAKE_UP_INTERVAL_SUFFIX)) != -1) {
+         cfgOverride = getOrCreateConfig(prefixLoc, key, suffixLoc);
+         cfgOverride.setEvictionWakeUpInterval(Long.parseLong(PropertiesHelper.extractPropertyValue(key, properties)));
+      } else if ((suffixLoc = key.indexOf(MAX_ENTRIES_SUFFIX)) != -1) {
+         cfgOverride = getOrCreateConfig(prefixLoc, key, suffixLoc);
+         cfgOverride.setEvictionMaxEntries(PropertiesHelper.getInt(key, properties, -1));
+      } else if ((suffixLoc = key.indexOf(LIFESPAN_SUFFIX)) != -1) {
+         cfgOverride = getOrCreateConfig(prefixLoc, key, suffixLoc);
+         cfgOverride.setExpirationLifespan(Long.parseLong(PropertiesHelper.extractPropertyValue(key, properties)));
+      } else if ((suffixLoc = key.indexOf(MAX_IDLE_SUFFIX)) != -1) {
+         cfgOverride = getOrCreateConfig(prefixLoc, key, suffixLoc);
+         cfgOverride.setExpirationMaxIdle(Long.parseLong(PropertiesHelper.extractPropertyValue(key, properties)));
+      }
+   }
+
+//   private Configuration overrideCacheConfig(TypeOverrides config, Configuration baseCacheCfg) {
+//      // If eviction strategy is different from null, an override has been defined
+//      EvictionStrategy strategy = config.getEvictionStrategy();
+//      if (strategy != null) baseCacheCfg.setEvictionStrategy(strategy);
+//      // If eviction wake up interval is different from min value, an override has been defined
+//      // Checking for -1 might not be enough because user might have defined -1 in the config.
+//      // Same applies to other configuration options.
+//      long wakeUpInterval = config.getEvictionWakeUpInterval(); 
+//      if (wakeUpInterval != Long.MIN_VALUE) baseCacheCfg.setEvictionWakeUpInterval(wakeUpInterval);
+//      int maxEntries = config.getEvictionMaxEntries();
+//      if (maxEntries != Integer.MIN_VALUE) baseCacheCfg.setEvictionMaxEntries(maxEntries);
+//      long lifespan = config.getExpirationLifespan();
+//      if (lifespan != Long.MIN_VALUE) baseCacheCfg.setExpirationLifespan(lifespan); 
+//      long maxIdle = config.getExpirationMaxIdle();
+//      if (maxIdle != Long.MIN_VALUE) baseCacheCfg.setExpirationMaxIdle(maxIdle);
+//      return baseCacheCfg;
+//   }
+   
+   private TypeOverrides getOrCreateConfig(int prefixLoc, String key, int suffixLoc) {
+      String name = key.substring(prefixLoc + PREFIX.length(), suffixLoc);
+      TypeOverrides cfgOverride = typeOverrides.get(name);
+      if (cfgOverride == null) {
+         cfgOverride = new TypeOverrides();
+         typeOverrides.put(name, cfgOverride);
+      }
+      return cfgOverride;
+   }
+   
+   private void defineGenericDataTypeCacheConfigurations(Settings settings, Properties properties) throws CacheException {
+      String[] defaultGenericDataTypes = new String[]{ENTITY_KEY, COLLECTION_KEY, TIMESTAMPS_KEY, QUERY_KEY};
+      for (String type : defaultGenericDataTypes) {
+         TypeOverrides override = typeOverrides.get(type);
+         String cacheName = override.getCacheName();
+         Configuration newCacheCfg = override.createInfinispanConfiguration();
+         // Apply overrides
+         Configuration cacheConfig = manager.defineConfiguration(cacheName, cacheName, newCacheCfg);
+         // Configure transaction manager
+         cacheConfig = configureTransactionManager(cacheConfig, cacheName, properties);
+         manager.defineConfiguration(cacheName, cacheName, cacheConfig);
+         definedConfigurations.add(cacheName);
+         override.validateInfinispanConfiguration(cacheConfig);
+      }
+   }
+   
+   private Cache getCache(String regionName, String typeKey, Properties properties) {
+      TypeOverrides regionOverride = typeOverrides.get(regionName);
+      if (!definedConfigurations.contains(regionName)) {
+         String templateCacheName = null;
+         Configuration regionCacheCfg = null;
+         if (regionOverride != null) {
+            if (log.isDebugEnabled()) log.debug("Entity cache region specific configuration exists: " + regionOverride);
+            regionCacheCfg = regionOverride.createInfinispanConfiguration();
+            String cacheName = regionOverride.getCacheName();
+            if (cacheName != null) // Region specific override with a given cache name
+               templateCacheName = cacheName; 
+            else // Region specific override without cache name, so template cache name is generic for data type.
+               templateCacheName = typeOverrides.get(typeKey).getCacheName(); 
+         } else {
+            // No region specific overrides, template cache name is generic for data type.
+            templateCacheName = typeOverrides.get(typeKey).getCacheName();
+            regionCacheCfg = typeOverrides.get(typeKey).createInfinispanConfiguration();
+         }
+         // Configure transaction manager
+         regionCacheCfg = configureTransactionManager(regionCacheCfg, templateCacheName, properties);
+         // Apply overrides
+         manager.defineConfiguration(regionName, templateCacheName, regionCacheCfg);
+         definedConfigurations.add(regionName);
+      }
+      return manager.getCache(regionName);
+
+//      if (regionOverride != null) {
+//         if (log.isDebugEnabled()) log.debug("Entity cache region specific configuration exists: " + regionOverride);
+//         String cacheName = regionOverride.getCacheName();
+//         if (cacheName != null) {
+//            // Region specific override with a given cache name
+//            if (!definedConfigurations.contains(regionName)) {
+//               templateCacheName = cacheName;
+//               regionCacheCfg = regionOverride.createInfinispanConfiguration();
+//               manager.defineConfiguration(regionName, templateCacheName, regionCacheCfg);
+//               definedConfigurations.add(regionName);
+//            }
+//            return manager.getCache(regionName);
+//         } else {
+//            // Region specific override without cache name, so template cache name is generic for data type.
+//            if (!definedConfigurations.contains(regionName)) {
+//               templateCacheName = typeOverrides.get(typeKey).getCacheName();
+//               regionCacheCfg = regionOverride.createInfinispanConfiguration();
+//               manager.defineConfiguration(regionName, templateCacheName, regionCacheCfg);
+//               definedConfigurations.add(regionName);
+//            }
+//            return manager.getCache(regionName);
+//         }
+//      }
+//      
+//      if (!definedConfigurations.contains(regionName)) {
+//         templateCacheName = typeOverrides.get(typeKey).getCacheName();
+//         regionCacheCfg = typeOverrides.get(typeKey).createInfinispanConfiguration();
+//         manager.defineConfiguration(regionName, templateCacheName, regionCacheCfg);
+//         definedConfigurations.add(regionName);
+//      }
+//      // No region specific overrides, get a cache instance for the generic entity data type region
+//      return manager.getCache(regionName);
+   }
+   
+   private Configuration configureTransactionManager(Configuration regionOverrides, String templateCacheName, Properties properties) {
+      // Get existing configuration to verify whether a tm was configured or not.
+      Configuration templateConfig = manager.defineConfiguration(templateCacheName, new Configuration());
+      String ispnTmLookupClassName = templateConfig.getTransactionManagerLookupClass();
+      String hbTmLookupClassName = org.hibernate.cache.infinispan.tm.HibernateTransactionManagerLookup.class.getName();
+      if (ispnTmLookupClassName != null && !ispnTmLookupClassName.equals(hbTmLookupClassName)) {
+         log.debug("Infinispan is configured [" + ispnTmLookupClassName + "] with a different transaction manager lookup " +
+               "class than Hibernate [" + hbTmLookupClassName + "]");
+      } else {
+         regionOverrides.setTransactionManagerLookup(transactionManagerlookup);
+      }
+      return regionOverrides;
+   }
+}
\ No newline at end of file

Added: core/trunk/cache-infinispan/src/main/java/org/hibernate/cache/infinispan/TypeOverrides.java
===================================================================
--- core/trunk/cache-infinispan/src/main/java/org/hibernate/cache/infinispan/TypeOverrides.java	                        (rev 0)
+++ core/trunk/cache-infinispan/src/main/java/org/hibernate/cache/infinispan/TypeOverrides.java	2009-08-21 08:39:33 UTC (rev 17386)
@@ -0,0 +1,138 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2009, Red Hat, Inc. and/or it's affiliates, 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.hibernate.cache.infinispan;
+
+import java.util.Locale;
+
+import org.hibernate.cache.CacheException;
+import org.infinispan.config.Configuration;
+import org.infinispan.eviction.EvictionStrategy;
+
+/**
+ * This class represents Infinispan cache parameters that can be configured via hibernate configuration properties 
+ * for either general entity/collection/query/timestamp data type caches and overrides for individual entity or 
+ * collection caches. Configuration these properties override previously defined properties in XML file.
+ * 
+ * @author Galder Zamarreño
+ * @since 3.5
+ */
+public class TypeOverrides {
+   
+   private String cacheName;
+   
+   private EvictionStrategy evictionStrategy;
+   
+   private long evictionWakeUpInterval = Long.MIN_VALUE;
+   
+   private int evictionMaxEntries = Integer.MIN_VALUE;
+   
+   private long expirationLifespan = Long.MIN_VALUE;
+   
+   private long expirationMaxIdle = Long.MIN_VALUE;
+
+   public String getCacheName() {
+      return cacheName;
+   }
+
+   public void setCacheName(String cacheName) {
+      this.cacheName = cacheName;
+   }
+
+   public EvictionStrategy getEvictionStrategy() {
+      return evictionStrategy;
+   }
+
+   public void setEvictionStrategy(String evictionStrategy) {
+      this.evictionStrategy = EvictionStrategy.valueOf(uc(evictionStrategy));
+   }
+
+   public long getEvictionWakeUpInterval() {
+      return evictionWakeUpInterval;
+   }
+
+   public void setEvictionWakeUpInterval(long evictionWakeUpInterval) {
+      this.evictionWakeUpInterval = evictionWakeUpInterval;
+   }
+
+   public int getEvictionMaxEntries() {
+      return evictionMaxEntries;
+   }
+
+   public void setEvictionMaxEntries(int evictionMaxEntries) {
+      this.evictionMaxEntries = evictionMaxEntries;
+   }
+
+   public long getExpirationLifespan() {
+      return expirationLifespan;
+   }
+
+   public void setExpirationLifespan(long expirationLifespan) {
+      this.expirationLifespan = expirationLifespan;
+   }
+
+   public long getExpirationMaxIdle() {
+      return expirationMaxIdle;
+   }
+
+   public void setExpirationMaxIdle(long expirationMaxIdle) {
+      this.expirationMaxIdle = expirationMaxIdle;
+   }
+   
+//   public boolean isConvertedToInfinispanConfiguration() {
+//      return convertedToInfinispanConfiguration;
+//   }
+   
+   public Configuration createInfinispanConfiguration() {
+      Configuration cacheCfg = new Configuration();
+      // If eviction strategy is different from null, an override has been defined
+      if (evictionStrategy != null) cacheCfg.setEvictionStrategy(evictionStrategy);
+      // If eviction wake up interval is different from min value, an override has been defined
+      // Checking for -1 might not be enough because user might have defined -1 in the config.
+      // Same applies to other configuration options.
+      if (evictionWakeUpInterval != Long.MIN_VALUE) cacheCfg.setEvictionWakeUpInterval(evictionWakeUpInterval);
+      if (evictionMaxEntries != Integer.MIN_VALUE) cacheCfg.setEvictionMaxEntries(evictionMaxEntries);
+      if (expirationLifespan != Long.MIN_VALUE) cacheCfg.setExpirationLifespan(expirationLifespan); 
+      if (expirationMaxIdle != Long.MIN_VALUE) cacheCfg.setExpirationMaxIdle(expirationMaxIdle);
+//      convertedToInfinispanConfiguration = true;
+      return cacheCfg;
+   }
+   
+   public void validateInfinispanConfiguration(Configuration configuration) throws CacheException {
+      // no-op
+   }
+   
+   @Override
+   public String toString() {
+      return new StringBuilder().append(getClass().getSimpleName()).append('{')
+         .append("cache=").append(cacheName)
+         .append(", strategy=").append(evictionStrategy)
+         .append(", wakeUpInterval=").append(evictionWakeUpInterval)
+         .append(", maxEntries=").append(evictionMaxEntries)
+         .append(", lifespan=").append(expirationLifespan)
+         .append(", maxIdle=").append(expirationMaxIdle)
+         .append('}').toString();
+   }
+   
+   private String uc(String s) {
+      return s == null ? null : s.toUpperCase(Locale.ENGLISH);
+   }
+}

Added: core/trunk/cache-infinispan/src/main/java/org/hibernate/cache/infinispan/access/TransactionalAccessDelegate.java
===================================================================
--- core/trunk/cache-infinispan/src/main/java/org/hibernate/cache/infinispan/access/TransactionalAccessDelegate.java	                        (rev 0)
+++ core/trunk/cache-infinispan/src/main/java/org/hibernate/cache/infinispan/access/TransactionalAccessDelegate.java	2009-08-21 08:39:33 UTC (rev 17386)
@@ -0,0 +1,116 @@
+/*
+ * Hibernate, Relational Persistence for Idiomatic Java
+ *
+ * Copyright (c) 2007, Red Hat, Inc. and/or it's affiliates or third-party contributors as
+ * indicated by the @author tags or express copyright attribution
+ * statements applied by the authors.  All third-party contributions are
+ * distributed under license by Red Hat, Inc. and/or it's affiliates.
+ *
+ * This copyrighted material is made available to anyone wishing to use, modify,
+ * copy, or redistribute it subject to the terms and conditions of the GNU
+ * Lesser General Public License, as published by the Free Software Foundation.
+ *
+ * This program 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 distribution; if not, write to:
+ * Free Software Foundation, Inc.
+ * 51 Franklin Street, Fifth Floor
+ * Boston, MA  02110-1301  USA
+ */
+package org.hibernate.cache.infinispan.access;
+
+import org.hibernate.cache.CacheException;
+import org.hibernate.cache.access.CollectionRegionAccessStrategy;
+import org.hibernate.cache.access.EntityRegionAccessStrategy;
+import org.hibernate.cache.access.SoftLock;
+import org.infinispan.Cache;
+
+/**
+ * Defines the strategy for transactional access to entity or collection data in a Infinispan instance.
+ * <p>
+ * The intent of this class is to encapsulate common code and serve as a delegate for
+ * {@link EntityRegionAccessStrategy} and {@link CollectionRegionAccessStrategy} implementations.
+ * 
+ * @author Brian Stansberry
+ * @author Galder Zamarreño
+ * @since 3.5
+ */
+public class TransactionalAccessDelegate {
+
+   protected final Cache cache;
+
+   public TransactionalAccessDelegate(Cache cache) {
+      this.cache = cache;
+   }
+
+   public Object get(Object key, long txTimestamp) throws CacheException {
+      return cache.get(key);
+   }
+
+   public boolean putFromLoad(Object key, Object value, long txTimestamp, Object version) throws CacheException {
+      cache.putForExternalRead(key, value);
+      return true;
+   }
+
+   public boolean putFromLoad(Object key, Object value, long txTimestamp, Object version, boolean minimalPutOverride)
+            throws CacheException {
+      return putFromLoad(key, value, txTimestamp, version);
+   }
+
+   public SoftLock lockItem(Object key, Object version) throws CacheException {
+      return null;
+   }
+
+   public SoftLock lockRegion() throws CacheException {
+      return null;
+   }
+
+   public void unlockItem(Object key, SoftLock lock) throws CacheException {
+   }
+
+   public void unlockRegion(SoftLock lock) throws CacheException {
+   }
+
+   public boolean insert(Object key, Object value, Object version) throws CacheException {
+      cache.put(key, value);
+      return true;
+   }
+
+   public boolean afterInsert(Object key, Object value, Object version) throws CacheException {
+      return false;
+   }
+
+   public boolean update(Object key, Object value, Object currentVersion, Object previousVersion) throws CacheException {
+      cache.put(key, value);
+      return true;
+   }
+
+   public boolean afterUpdate(Object key, Object value, Object currentVersion, Object previousVersion, SoftLock lock)
+            throws CacheException {
+      return false;
+   }
+
+   public void remove(Object key) throws CacheException {
+      cache.remove(key);
+   }
+
+   public void removeAll() throws CacheException {
+      cache.clear();
+   }
+
+//   public void evict(Object key) throws CacheException {
+//      cache.evict(key);
+//   }
+
+   public void evictAll() throws CacheException {
+      evictOrRemoveAll();
+   }
+
+   private void evictOrRemoveAll() throws CacheException {
+      cache.clear();
+   }
+}


Property changes on: core/trunk/cache-infinispan/src/main/java/org/hibernate/cache/infinispan/access/TransactionalAccessDelegate.java
___________________________________________________________________
Name: svn:executable
   + *

Added: core/trunk/cache-infinispan/src/main/java/org/hibernate/cache/infinispan/collection/CollectionRegionImpl.java
===================================================================
--- core/trunk/cache-infinispan/src/main/java/org/hibernate/cache/infinispan/collection/CollectionRegionImpl.java	                        (rev 0)
+++ core/trunk/cache-infinispan/src/main/java/org/hibernate/cache/infinispan/collection/CollectionRegionImpl.java	2009-08-21 08:39:33 UTC (rev 17386)
@@ -0,0 +1,33 @@
+package org.hibernate.cache.infinispan.collection;
+
+import javax.transaction.TransactionManager;
+
+import org.hibernate.cache.CacheDataDescription;
+import org.hibernate.cache.CacheException;
+import org.hibernate.cache.CollectionRegion;
+import org.hibernate.cache.access.AccessType;
+import org.hibernate.cache.access.CollectionRegionAccessStrategy;
+import org.hibernate.cache.infinispan.impl.BaseTransactionalDataRegion;
+import org.infinispan.Cache;
+
+/**
+ * @author Chris Bredesen
+ * @author Galder Zamarreño
+ * @since 3.5
+ */
+public class CollectionRegionImpl extends BaseTransactionalDataRegion implements CollectionRegion {
+
+   public CollectionRegionImpl(Cache<Object, Object> cache, String name, CacheDataDescription metadata, TransactionManager transactionManager) {
+      super(cache, name, metadata, transactionManager);
+   }
+
+   public CollectionRegionAccessStrategy buildAccessStrategy(AccessType accessType) throws CacheException {
+      if (AccessType.READ_ONLY.equals(accessType)) {
+         return new ReadOnlyAccess(this);
+      } else if (AccessType.TRANSACTIONAL.equals(accessType)) {
+         return new TransactionalAccess(this);
+      }
+      throw new CacheException("Unsupported access type [" + accessType.getName() + "]");
+   }
+
+}

Added: core/trunk/cache-infinispan/src/main/java/org/hibernate/cache/infinispan/collection/ReadOnlyAccess.java
===================================================================
--- core/trunk/cache-infinispan/src/main/java/org/hibernate/cache/infinispan/collection/ReadOnlyAccess.java	                        (rev 0)
+++ core/trunk/cache-infinispan/src/main/java/org/hibernate/cache/infinispan/collection/ReadOnlyAccess.java	2009-08-21 08:39:33 UTC (rev 17386)
@@ -0,0 +1,41 @@
+package org.hibernate.cache.infinispan.collection;
+
+import org.hibernate.cache.CacheException;
+import org.hibernate.cache.access.SoftLock;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * This defines the strategy for transactional access to collection data in a
+ * Infinispan instance.
+ * <p/>
+ * The read-only access to a Infinispan really is still transactional, just with 
+ * the extra semantic or guarantee that we will not update data.
+ *
+ * @author Chris Bredesen
+ * @author Galder Zamarreño
+ * @since 3.5
+ */
+class ReadOnlyAccess extends TransactionalAccess {
+   private static final Logger log = LoggerFactory.getLogger(ReadOnlyAccess.class);
+
+   ReadOnlyAccess(CollectionRegionImpl region) {
+      super(region);
+   }
+   public SoftLock lockItem(Object key, Object version) throws CacheException {
+      throw new UnsupportedOperationException("Illegal attempt to edit read only item");
+   }
+
+   public SoftLock lockRegion() throws CacheException {
+      throw new UnsupportedOperationException("Illegal attempt to edit read only region");
+   }
+
+   public void unlockItem(Object key, SoftLock lock) throws CacheException {
+      log.error("Illegal attempt to edit read only item");
+   }
+
+   public void unlockRegion(SoftLock lock) throws CacheException {
+      log.error("Illegal attempt to edit read only item");
+   }
+
+}

Added: core/trunk/cache-infinispan/src/main/java/org/hibernate/cache/infinispan/collection/TransactionalAccess.java
===================================================================
--- core/trunk/cache-infinispan/src/main/java/org/hibernate/cache/infinispan/collection/TransactionalAccess.java	                        (rev 0)
+++ core/trunk/cache-infinispan/src/main/java/org/hibernate/cache/infinispan/collection/TransactionalAccess.java	2009-08-21 08:39:33 UTC (rev 17386)
@@ -0,0 +1,74 @@
+package org.hibernate.cache.infinispan.collection;
+
+import org.hibernate.cache.CacheException;
+import org.hibernate.cache.CollectionRegion;
+import org.hibernate.cache.access.CollectionRegionAccessStrategy;
+import org.hibernate.cache.access.SoftLock;
+import org.hibernate.cache.infinispan.access.TransactionalAccessDelegate;
+
+/**
+ * Transactional collection region access for Infinispan.
+ * 
+ * @author Chris Bredesen
+ * @author Galder Zamarreño
+ * @since 3.5
+ */
+class TransactionalAccess implements CollectionRegionAccessStrategy {
+
+   private final CollectionRegionImpl region;
+   
+   private final TransactionalAccessDelegate delegate;
+
+   TransactionalAccess(CollectionRegionImpl region) {
+      this.region = region;
+      this.delegate = new TransactionalAccessDelegate(region.getCache());
+   }
+
+   public void evict(Object key) throws CacheException {
+//      delegate.evict(key);
+      delegate.remove(key);
+   }
+
+   public void evictAll() throws CacheException {
+      delegate.evictAll();
+   }
+
+   public Object get(Object key, long txTimestamp) throws CacheException {
+      return delegate.get(key, txTimestamp);
+   }
+
+   public boolean putFromLoad(Object key, Object value, long txTimestamp, Object version) throws CacheException {
+      return delegate.putFromLoad(key, value, txTimestamp, version);
+   }
+
+   public boolean putFromLoad(Object key, Object value, long txTimestamp, Object version, boolean minimalPutOverride) throws CacheException {
+      return delegate.putFromLoad(key, value, txTimestamp, version, minimalPutOverride);
+   }
+
+   public void remove(Object key) throws CacheException {
+      delegate.remove(key);
+   }
+
+   public void removeAll() throws CacheException {
+      delegate.removeAll();
+   }
+
+   public CollectionRegion getRegion() {
+      return region;
+   }
+
+   public SoftLock lockItem(Object key, Object version) throws CacheException {
+      return null;
+   }
+
+   public SoftLock lockRegion() throws CacheException {
+      return null;
+   }
+
+   public void unlockItem(Object key, SoftLock lock) throws CacheException {
+   }
+
+   public void unlockRegion(SoftLock lock) throws CacheException {
+   }
+
+}
\ No newline at end of file

Added: core/trunk/cache-infinispan/src/main/java/org/hibernate/cache/infinispan/entity/EntityRegionImpl.java
===================================================================
--- core/trunk/cache-infinispan/src/main/java/org/hibernate/cache/infinispan/entity/EntityRegionImpl.java	                        (rev 0)
+++ core/trunk/cache-infinispan/src/main/java/org/hibernate/cache/infinispan/entity/EntityRegionImpl.java	2009-08-21 08:39:33 UTC (rev 17386)
@@ -0,0 +1,33 @@
+package org.hibernate.cache.infinispan.entity;
+
+import javax.transaction.TransactionManager;
+
+import org.hibernate.cache.CacheDataDescription;
+import org.hibernate.cache.CacheException;
+import org.hibernate.cache.EntityRegion;
+import org.hibernate.cache.access.AccessType;
+import org.hibernate.cache.access.EntityRegionAccessStrategy;
+import org.hibernate.cache.infinispan.impl.BaseTransactionalDataRegion;
+import org.infinispan.Cache;
+
+/**
+ * @author Chris Bredesen
+ * @author Galder Zamarreño
+ * @since 3.5
+ */
+public class EntityRegionImpl extends BaseTransactionalDataRegion implements EntityRegion {
+
+   public EntityRegionImpl(Cache<Object, Object> cache, String name, CacheDataDescription metadata, TransactionManager transactionManager) {
+      super(cache, name, metadata, transactionManager);
+   }
+
+   public EntityRegionAccessStrategy buildAccessStrategy(AccessType accessType) throws CacheException {
+      if (AccessType.READ_ONLY.equals(accessType)) {
+         return new ReadOnlyAccess(this);
+      } else if (AccessType.TRANSACTIONAL.equals(accessType)) {
+         return new TransactionalAccess(this);
+      }
+      throw new CacheException("Unsupported access type [" + accessType.getName() + "]");
+   }
+
+}
\ No newline at end of file

Added: core/trunk/cache-infinispan/src/main/java/org/hibernate/cache/infinispan/entity/ReadOnlyAccess.java
===================================================================
--- core/trunk/cache-infinispan/src/main/java/org/hibernate/cache/infinispan/entity/ReadOnlyAccess.java	                        (rev 0)
+++ core/trunk/cache-infinispan/src/main/java/org/hibernate/cache/infinispan/entity/ReadOnlyAccess.java	2009-08-21 08:39:33 UTC (rev 17386)
@@ -0,0 +1,49 @@
+package org.hibernate.cache.infinispan.entity;
+
+import org.hibernate.cache.CacheException;
+import org.hibernate.cache.access.SoftLock;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * A specialization of {@link TransactionalAccess} that ensures we never update data. Infinispan
+ * access is always transactional.
+ * 
+ * @author Chris Bredesen
+ * @author Galder Zamarreño
+ * @since 3.5
+ */
+class ReadOnlyAccess extends TransactionalAccess {
+   private static final Logger log = LoggerFactory.getLogger(ReadOnlyAccess.class);
+
+   ReadOnlyAccess(EntityRegionImpl region) {
+      super(region);
+   }
+
+   public SoftLock lockItem(Object key, Object version) throws CacheException {
+      throw new UnsupportedOperationException("Illegal attempt to edit read only item");
+   }
+
+   public SoftLock lockRegion() throws CacheException {
+      throw new UnsupportedOperationException("Illegal attempt to edit read only item");
+   }
+
+   public void unlockItem(Object key, SoftLock lock) throws CacheException {
+      log.error("Illegal attempt to edit read only item");
+   }
+
+   public void unlockRegion(SoftLock lock) throws CacheException {
+      log.error("Illegal attempt to edit read only item");
+   }
+
+   @Override
+   public boolean update(Object key, Object value, Object currentVersion, Object previousVersion) throws CacheException {
+      throw new UnsupportedOperationException("Illegal attempt to edit read only item");
+   }
+
+   @Override
+   public boolean afterUpdate(Object key, Object value, Object currentVersion, Object previousVersion, SoftLock lock)
+            throws CacheException {
+      throw new UnsupportedOperationException("Illegal attempt to edit read only item");
+   }
+}
\ No newline at end of file

Added: core/trunk/cache-infinispan/src/main/java/org/hibernate/cache/infinispan/entity/TransactionalAccess.java
===================================================================
--- core/trunk/cache-infinispan/src/main/java/org/hibernate/cache/infinispan/entity/TransactionalAccess.java	                        (rev 0)
+++ core/trunk/cache-infinispan/src/main/java/org/hibernate/cache/infinispan/entity/TransactionalAccess.java	2009-08-21 08:39:33 UTC (rev 17386)
@@ -0,0 +1,89 @@
+package org.hibernate.cache.infinispan.entity;
+
+import org.hibernate.cache.CacheException;
+import org.hibernate.cache.EntityRegion;
+import org.hibernate.cache.access.EntityRegionAccessStrategy;
+import org.hibernate.cache.access.SoftLock;
+import org.hibernate.cache.infinispan.access.TransactionalAccessDelegate;
+
+/**
+ * Transactional entity region access for Infinispan.
+ * 
+ * @author Chris Bredesen
+ * @author Galder Zamarreño
+ * @since 3.5
+ */
+class TransactionalAccess implements EntityRegionAccessStrategy {
+ 
+   private final EntityRegionImpl region;
+   
+   private final TransactionalAccessDelegate delegate;
+
+   TransactionalAccess(EntityRegionImpl region) {
+      this.region = region;
+      this.delegate = new TransactionalAccessDelegate(region.getCache());
+   }
+
+   public void evict(Object key) throws CacheException {
+      delegate.remove(key);
+   }
+
+   public void evictAll() throws CacheException {
+      delegate.evictAll();
+   }
+
+   public Object get(Object key, long txTimestamp) throws CacheException {
+      return delegate.get(key, txTimestamp);
+   }
+
+   public EntityRegion getRegion() {
+      return this.region;
+   }
+
+   public boolean insert(Object key, Object value, Object version) throws CacheException {
+      region.getCache().put(key, value);
+      return true; // TODO this is suspect
+   }
+
+   public boolean putFromLoad(Object key, Object value, long txTimestamp, Object version) throws CacheException {
+      return delegate.putFromLoad(key, value, txTimestamp, version);
+   }
+
+   public boolean putFromLoad(Object key, Object value, long txTimestamp, Object version, boolean minimalPutOverride) throws CacheException {
+      return delegate.putFromLoad(key, value, txTimestamp, version, minimalPutOverride);
+   }
+
+   public void remove(Object key) throws CacheException {
+      delegate.remove(key);
+   }
+
+   public void removeAll() throws CacheException {
+      delegate.removeAll();
+   }
+
+   public boolean update(Object key, Object value, Object currentVersion, Object previousVersion) throws CacheException {
+      return delegate.update(key, value, currentVersion, previousVersion);
+   }
+
+   public SoftLock lockItem(Object key, Object version) throws CacheException {
+      return null;
+   }
+
+   public SoftLock lockRegion() throws CacheException {
+      return null;
+   }
+
+   public void unlockItem(Object key, SoftLock lock) throws CacheException {
+   }
+
+   public void unlockRegion(SoftLock lock) throws CacheException {
+   }
+
+   public boolean afterInsert(Object key, Object value, Object version) throws CacheException {
+      return false;
+   }
+
+   public boolean afterUpdate(Object key, Object value, Object currentVersion, Object previousVersion, SoftLock lock) throws CacheException {
+      return false;
+   }
+}
\ No newline at end of file

Added: core/trunk/cache-infinispan/src/main/java/org/hibernate/cache/infinispan/impl/BaseGeneralDataRegion.java
===================================================================
--- core/trunk/cache-infinispan/src/main/java/org/hibernate/cache/infinispan/impl/BaseGeneralDataRegion.java	                        (rev 0)
+++ core/trunk/cache-infinispan/src/main/java/org/hibernate/cache/infinispan/impl/BaseGeneralDataRegion.java	2009-08-21 08:39:33 UTC (rev 17386)
@@ -0,0 +1,38 @@
+package org.hibernate.cache.infinispan.impl;
+
+import javax.transaction.TransactionManager;
+
+import org.hibernate.cache.CacheException;
+import org.hibernate.cache.GeneralDataRegion;
+import org.infinispan.Cache;
+
+/**
+ * Support for Infinispan {@link GeneralDataRegion} implementors.
+ * 
+ * @author Chris Bredesen
+ * @author Galder Zamarreño
+ * @since 3.5
+ */
+public abstract class BaseGeneralDataRegion extends BaseRegion implements GeneralDataRegion {
+
+   public BaseGeneralDataRegion(Cache<Object, Object> cache, String name, TransactionManager transactionManager) {
+      super(cache, name, transactionManager);
+   }
+
+   public void evict(Object key) throws CacheException {
+      getCache().evict(key);
+   }
+
+   public void evictAll() throws CacheException {
+      getCache().clear();
+   }
+
+   public Object get(Object key) throws CacheException {
+      return getCache().get(key);
+   }
+
+   public void put(Object key, Object value) throws CacheException {
+      getCache().put(key, value);
+   }
+
+}
\ No newline at end of file

Added: core/trunk/cache-infinispan/src/main/java/org/hibernate/cache/infinispan/impl/BaseRegion.java
===================================================================
--- core/trunk/cache-infinispan/src/main/java/org/hibernate/cache/infinispan/impl/BaseRegion.java	                        (rev 0)
+++ core/trunk/cache-infinispan/src/main/java/org/hibernate/cache/infinispan/impl/BaseRegion.java	2009-08-21 08:39:33 UTC (rev 17386)
@@ -0,0 +1,182 @@
+package org.hibernate.cache.infinispan.impl;
+
+import java.util.Map;
+
+import javax.transaction.SystemException;
+import javax.transaction.Transaction;
+import javax.transaction.TransactionManager;
+
+import org.hibernate.cache.CacheException;
+import org.hibernate.cache.Region;
+import org.hibernate.cache.infinispan.util.CacheHelper;
+import org.infinispan.Cache;
+import org.infinispan.context.Flag;
+
+/**
+ * Support for Infinispan {@link Region}s. Handles common "utility" methods for an underlying named
+ * Cache. In other words, this implementation doesn't actually read or write data. Subclasses are
+ * expected to provide core cache interaction appropriate to the semantics needed.
+ * 
+ * @author Chris Bredesen
+ * @author Galder Zamarreño
+ * @since 3.5
+ */
+public abstract class BaseRegion implements Region {
+   private final Cache cache;
+   private final String name;
+   protected final TransactionManager transactionManager;
+
+   public BaseRegion(Cache cache, String name, TransactionManager transactionManager) {
+      this.cache = cache;
+      this.name = name;
+      this.transactionManager = transactionManager;
+   }
+
+   public Cache getCache() {
+      return cache;
+   }
+
+   public String getName() {
+      return name;
+   }
+
+   public long getElementCountInMemory() {
+      return cache.size();
+   }
+
+   /**
+    * Not supported.
+    * 
+    * @return -1
+    */
+   public long getElementCountOnDisk() {
+      return -1;
+   }
+
+   /**
+    * Not supported.
+    * 
+    * @return -1
+    */
+   public long getSizeInMemory() {
+      return -1;
+   }
+
+   public int getTimeout() {
+      // TODO Auto-generated method stub
+      return 0;
+   }
+
+   public long nextTimestamp() {
+      // TODO Auto-generated method stub
+      return 0;
+   }
+
+   public Map toMap() {
+      return cache;
+   }
+
+   public void destroy() throws CacheException {
+      // TODO see if we need to do this even in spite of RF.shutdown()
+   }
+   
+   public boolean contains(Object key) {
+      return CacheHelper.containsKey(cache, key, Flag.ZERO_LOCK_ACQUISITION_TIMEOUT);
+   }
+   
+   /**
+    * Performs a JBoss Cache <code>get(Fqn, Object)</code> after first
+    * {@link #suspend suspending any ongoing transaction}. Wraps any exception
+    * in a {@link CacheException}. Ensures any ongoing transaction is resumed.
+    * 
+    * @param key The key of the item to get
+    * @param opt any option to add to the get invocation. May be <code>null</code>
+    * @param suppressTimeout should any TimeoutException be suppressed?
+    * @return The retrieved object
+      * @throws CacheException issue managing transaction or talking to cache
+    */
+   protected Object suspendAndGet(Object key, Flag opt, boolean suppressTimeout) throws CacheException {
+       Transaction tx = suspend();
+       try {
+           if (suppressTimeout)
+               return CacheHelper.getAllowingTimeout(cache, key);
+           else
+               return CacheHelper.get(cache, key);
+       } finally {
+           resume(tx);
+       }
+   }
+   
+   /**
+    * Tell the TransactionManager to suspend any ongoing transaction.
+    * 
+    * @return the transaction that was suspended, or <code>null</code> if
+    *         there wasn't one
+    */
+   protected Transaction suspend() {
+       Transaction tx = null;
+       try {
+           if (transactionManager != null) {
+               tx = transactionManager.suspend();
+           }
+       } catch (SystemException se) {
+           throw new CacheException("Could not suspend transaction", se);
+       }
+       return tx;
+   }
+   
+   /**
+    * Tell the TransactionManager to resume the given transaction
+    * 
+    * @param tx
+    *            the transaction to suspend. May be <code>null</code>.
+    */
+   protected void resume(Transaction tx) {
+       try {
+           if (tx != null)
+               transactionManager.resume(tx);
+       } catch (Exception e) {
+           throw new CacheException("Could not resume transaction", e);
+       }
+   }
+   
+//   /**
+//    * HACKY WAY TO GET THE TRANSACTION MANAGER, TODO: resolve it!
+//    */
+//   private static TransactionManager getTransactionManager(Properties properties) {
+////      return cache == null ? null : extractComponent(cache, TransactionManager.class);
+//      return TransactionManagerLookupFactory.getTransactionManager(properties);
+//   }
+//   
+//   public static <T> T extractComponent(Cache cache, Class<T> componentType) {
+//      ComponentRegistry cr = extractComponentRegistry(cache);
+//      return cr.getComponent(componentType);
+//   }
+//   
+//   public static ComponentRegistry extractComponentRegistry(Cache cache) {
+//      return (ComponentRegistry) extractField(cache, "componentRegistry");
+//   }
+//   
+//   public static Object extractField(Object target, String fieldName) {
+//      return extractField(target.getClass(), target, fieldName);
+//   }
+//   
+//   public static Object extractField(Class type, Object target, String fieldName) {
+//      Field field;
+//      try {
+//         field = type.getDeclaredField(fieldName);
+//         field.setAccessible(true);
+//         return field.get(target);
+//      }
+//      catch (Exception e) {
+//         if (type.equals(Object.class)) {
+//            e.printStackTrace();
+//            return null;
+//         } else {
+//            // try with superclass!!
+//            return extractField(type.getSuperclass(), target, fieldName);
+//         }
+//      }
+//   }
+
+}
\ No newline at end of file

Added: core/trunk/cache-infinispan/src/main/java/org/hibernate/cache/infinispan/impl/BaseTransactionalDataRegion.java
===================================================================
--- core/trunk/cache-infinispan/src/main/java/org/hibernate/cache/infinispan/impl/BaseTransactionalDataRegion.java	                        (rev 0)
+++ core/trunk/cache-infinispan/src/main/java/org/hibernate/cache/infinispan/impl/BaseTransactionalDataRegion.java	2009-08-21 08:39:33 UTC (rev 17386)
@@ -0,0 +1,33 @@
+package org.hibernate.cache.infinispan.impl;
+
+import javax.transaction.TransactionManager;
+
+import org.hibernate.cache.CacheDataDescription;
+import org.hibernate.cache.TransactionalDataRegion;
+import org.infinispan.Cache;
+
+/**
+ * Support for Inifinispan {@link TransactionalDataRegion} implementors.
+ * 
+ * @author Chris Bredesen
+ * @author Galder Zamarreño
+ * @since 3.5
+ */
+public abstract class BaseTransactionalDataRegion extends BaseRegion implements TransactionalDataRegion {
+
+   private final CacheDataDescription metadata;
+
+   public BaseTransactionalDataRegion(Cache<Object, Object> cache, String name, CacheDataDescription metadata, TransactionManager transactionManager) {
+      super(cache, name, transactionManager);
+      this.metadata = metadata;
+   }
+
+   public CacheDataDescription getCacheDataDescription() {
+      return metadata;
+   }
+
+   public boolean isTransactionAware() {
+      return transactionManager != null;
+   }
+
+}
\ No newline at end of file

Added: core/trunk/cache-infinispan/src/main/java/org/hibernate/cache/infinispan/query/QueryResultsRegionImpl.java
===================================================================
--- core/trunk/cache-infinispan/src/main/java/org/hibernate/cache/infinispan/query/QueryResultsRegionImpl.java	                        (rev 0)
+++ core/trunk/cache-infinispan/src/main/java/org/hibernate/cache/infinispan/query/QueryResultsRegionImpl.java	2009-08-21 08:39:33 UTC (rev 17386)
@@ -0,0 +1,85 @@
+package org.hibernate.cache.infinispan.query;
+
+import java.util.Properties;
+
+import javax.transaction.TransactionManager;
+
+import org.hibernate.cache.CacheException;
+import org.hibernate.cache.QueryResultsRegion;
+import org.hibernate.cache.infinispan.impl.BaseTransactionalDataRegion;
+import org.hibernate.cache.infinispan.util.CacheHelper;
+import org.hibernate.util.PropertiesHelper;
+import org.infinispan.Cache;
+import org.infinispan.context.Flag;
+
+/**
+ * @author Chris Bredesen
+ * @author Galder Zamarreño
+ * @since 3.5
+ */
+public class QueryResultsRegionImpl extends BaseTransactionalDataRegion implements QueryResultsRegion {
+   public static final String QUERY_CACHE_LOCAL_ONLY_PROP = "hibernate.cache.infinispan.query.localonly";
+
+   private boolean localOnly;
+
+   public QueryResultsRegionImpl(Cache<Object, Object> cache, String name, Properties properties, TransactionManager transactionManager) {
+      super(cache, name, null, transactionManager);
+      
+      // If JBC is using INVALIDATION, we don't want to propagate changes.
+      // We use the Timestamps cache to manage invalidation
+      localOnly = CacheHelper.isClusteredInvalidation(cache);
+      if (!localOnly) {
+          // We don't want to waste effort setting an option if JBC is
+          // already in LOCAL mode. If JBC is REPL_(A)SYNC then check
+          // if they passed an config option to disable query replication
+          localOnly = CacheHelper.isClusteredReplication(cache)
+                  && PropertiesHelper.getBoolean(QUERY_CACHE_LOCAL_ONLY_PROP, properties, false);
+      }
+   }
+
+   public void evict(Object key) throws CacheException {
+      if (localOnly)
+         CacheHelper.removeKey(getCache(), key, Flag.CACHE_MODE_LOCAL);
+      else 
+         CacheHelper.removeKey(getCache(), key);
+   }
+
+   public void evictAll() throws CacheException {
+      if (localOnly)
+         CacheHelper.removeAll(getCache(), Flag.CACHE_MODE_LOCAL);
+      else 
+         CacheHelper.removeAll(getCache());
+   }
+
+   public Object get(Object key) throws CacheException {
+      // Don't hold the JBC node lock throughout the tx, as that
+      // prevents updates
+      // Add a zero (or low) timeout option so we don't block
+      // waiting for tx's that did a put to commit
+      return suspendAndGet(key, Flag.ZERO_LOCK_ACQUISITION_TIMEOUT, true);
+   }
+
+   public void put(Object key, Object value) throws CacheException {
+      // Here we don't want to suspend the tx. If we do:
+      // 1) We might be caching query results that reflect uncommitted
+      // changes. No tx == no WL on cache node, so other threads
+      // can prematurely see those query results
+      // 2) No tx == immediate replication. More overhead, plus we
+      // spread issue #1 above around the cluster
+
+      // Add a zero (or quite low) timeout option so we don't block.
+      // Ignore any TimeoutException. Basically we forego caching the
+      // query result in order to avoid blocking.
+      // Reads are done with suspended tx, so they should not hold the
+      // lock for long.  Not caching the query result is OK, since
+      // any subsequent read will just see the old result with its
+      // out-of-date timestamp; that result will be discarded and the
+      // db query performed again.
+      if (localOnly)
+         CacheHelper.putAllowingTimeout(getCache(), key, value, Flag.ZERO_LOCK_ACQUISITION_TIMEOUT, Flag.CACHE_MODE_LOCAL);
+      else 
+         CacheHelper.putAllowingTimeout(getCache(), key, value, Flag.ZERO_LOCK_ACQUISITION_TIMEOUT);
+      
+   }
+
+}
\ No newline at end of file

Added: core/trunk/cache-infinispan/src/main/java/org/hibernate/cache/infinispan/timestamp/TimestampTypeOverrides.java
===================================================================
--- core/trunk/cache-infinispan/src/main/java/org/hibernate/cache/infinispan/timestamp/TimestampTypeOverrides.java	                        (rev 0)
+++ core/trunk/cache-infinispan/src/main/java/org/hibernate/cache/infinispan/timestamp/TimestampTypeOverrides.java	2009-08-21 08:39:33 UTC (rev 17386)
@@ -0,0 +1,48 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2009, Red Hat, Inc. and/or it's affiliates, 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.hibernate.cache.infinispan.timestamp;
+
+import org.hibernate.cache.CacheException;
+import org.hibernate.cache.infinispan.TypeOverrides;
+import org.infinispan.config.Configuration;
+import org.infinispan.config.Configuration.CacheMode;
+import org.infinispan.eviction.EvictionStrategy;
+
+/**
+ * TimestampTypeOverrides.
+ * 
+ * @author Galder Zamarreño
+ * @since 3.5
+ */
+public class TimestampTypeOverrides extends TypeOverrides {
+   @Override
+   public void validateInfinispanConfiguration(Configuration configuration) throws CacheException {
+      CacheMode cacheMode = configuration.getCacheMode();
+      if (cacheMode.equals(CacheMode.INVALIDATION_ASYNC) || cacheMode.equals(CacheMode.INVALIDATION_SYNC)) {
+         throw new CacheException("Timestamp cache cannot be configured with invalidation");
+      }
+      EvictionStrategy strategy = configuration.getEvictionStrategy();
+      if (!strategy.equals(EvictionStrategy.NONE)) {
+         throw new CacheException("Timestamp cache cannot be configured with eviction");
+      }
+   }
+}

Added: core/trunk/cache-infinispan/src/main/java/org/hibernate/cache/infinispan/timestamp/TimestampsRegionImpl.java
===================================================================
--- core/trunk/cache-infinispan/src/main/java/org/hibernate/cache/infinispan/timestamp/TimestampsRegionImpl.java	                        (rev 0)
+++ core/trunk/cache-infinispan/src/main/java/org/hibernate/cache/infinispan/timestamp/TimestampsRegionImpl.java	2009-08-21 08:39:33 UTC (rev 17386)
@@ -0,0 +1,121 @@
+package org.hibernate.cache.infinispan.timestamp;
+
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+
+import javax.transaction.Transaction;
+import javax.transaction.TransactionManager;
+
+import org.hibernate.cache.CacheException;
+import org.hibernate.cache.TimestampsRegion;
+import org.hibernate.cache.infinispan.impl.BaseGeneralDataRegion;
+import org.hibernate.cache.infinispan.util.CacheHelper;
+import org.infinispan.Cache;
+import org.infinispan.context.Flag;
+import org.infinispan.notifications.Listener;
+import org.infinispan.notifications.cachelistener.annotation.CacheEntryModified;
+import org.infinispan.notifications.cachelistener.annotation.CacheEntryRemoved;
+import org.infinispan.notifications.cachelistener.event.CacheEntryModifiedEvent;
+import org.infinispan.notifications.cachelistener.event.CacheEntryRemovedEvent;
+
+/**
+ * Defines the behavior of the timestamps cache region for Infinispan.
+ * 
+ * @author Chris Bredesen
+ * @author Galder Zamarreño
+ * @since 3.5
+ */
+ at Listener
+public class TimestampsRegionImpl extends BaseGeneralDataRegion implements TimestampsRegion {
+
+   private Map localCache = new ConcurrentHashMap();
+
+   public TimestampsRegionImpl(Cache<Object, Object> cache, String name, TransactionManager transactionManager) {
+      super(cache, name, transactionManager);
+      cache.addListener(this);
+      populateLocalCache();
+   }
+
+   @Override
+   public void evict(Object key) throws CacheException {
+      // TODO Is this a valid operation on a timestamps cache?
+      CacheHelper.removeKey(getCache(), key);
+   }
+
+   public void evictAll() throws CacheException {
+      // TODO Is this a valid operation on a timestamps cache?
+      CacheHelper.removeAll(getCache());
+   }
+
+   public Object get(Object key) throws CacheException {
+      Object value = localCache.get(key);
+      if (value == null) {
+         value = suspendAndGet(key, null, false);
+         if (value != null)
+            localCache.put(key, value);
+      }
+      return value;
+   }
+
+   public void put(Object key, Object value) throws CacheException {
+      // Don't hold the JBC node lock throughout the tx, as that
+      // prevents reads and other updates
+      Transaction tx = suspend();
+      try {
+         // We ensure ASYNC semantics (JBCACHE-1175)
+         CacheHelper.put(getCache(), key, value, Flag.FORCE_ASYNCHRONOUS);
+      } catch (Exception e) {
+         throw new CacheException(e);
+      } finally {
+         resume(tx);
+      }
+   }
+
+   @Override
+   public void destroy() throws CacheException {
+      localCache.clear();
+      getCache().removeListener(this);
+      super.destroy();
+   }
+
+   /**
+    * Monitors cache events and updates the local cache
+    * 
+    * @param event
+    */
+   @CacheEntryModified
+   public void nodeModified(CacheEntryModifiedEvent event) {
+      if (event.isPre()) return;
+      localCache.put(event.getKey(), event.getValue());
+   }
+
+   /**
+    * Monitors cache events and updates the local cache
+    * 
+    * @param event
+    */
+   @CacheEntryRemoved
+   public void nodeRemoved(CacheEntryRemovedEvent event) {
+      if (event.isPre()) return;
+      localCache.remove(event.getKey());
+//      Fqn fqn = event.getFqn();
+//      Fqn regFqn = getRegionFqn();
+//      if (fqn.size() == regFqn.size() + 1 && fqn.isChildOf(regFqn)) {
+//         Object key = fqn.get(regFqn.size());
+//         localCache.remove(key);
+//      } else if (fqn.equals(regFqn)) {
+//         localCache.clear();
+//      }
+   }
+
+   /**
+    * Brings all data from the distributed cache into our local cache.
+    */
+   private void populateLocalCache() {
+      Set children = CacheHelper.getKeySet(getCache());
+      for (Object key : children)
+         get(key);
+   }
+
+}
\ No newline at end of file

Added: core/trunk/cache-infinispan/src/main/java/org/hibernate/cache/infinispan/tm/HibernateTransactionManagerLookup.java
===================================================================
--- core/trunk/cache-infinispan/src/main/java/org/hibernate/cache/infinispan/tm/HibernateTransactionManagerLookup.java	                        (rev 0)
+++ core/trunk/cache-infinispan/src/main/java/org/hibernate/cache/infinispan/tm/HibernateTransactionManagerLookup.java	2009-08-21 08:39:33 UTC (rev 17386)
@@ -0,0 +1,54 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2009, Red Hat, Inc. and/or it's affiliates, 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.hibernate.cache.infinispan.tm;
+
+import java.util.Properties;
+
+import javax.transaction.TransactionManager;
+
+import org.hibernate.cfg.Settings;
+import org.hibernate.transaction.TransactionManagerLookup;
+
+/**
+ * HibernateTransactionManagerLookup.
+ * 
+ * @author Galder Zamarreño
+ * @since 3.5
+ */
+public class HibernateTransactionManagerLookup implements org.infinispan.transaction.lookup.TransactionManagerLookup {
+   private final TransactionManagerLookup hibernateLookup;
+   
+   private final Properties properties;
+   
+   public HibernateTransactionManagerLookup(Settings settings, Properties properties) {
+      if (settings != null)
+         this.hibernateLookup = settings.getTransactionManagerLookup();
+      else
+         this.hibernateLookup = null;
+      this.properties = properties;
+   }
+   
+   public TransactionManager getTransactionManager() throws Exception {
+      return hibernateLookup == null ? null : hibernateLookup.getTransactionManager(properties);
+   }
+   
+}

Added: core/trunk/cache-infinispan/src/main/java/org/hibernate/cache/infinispan/util/CacheHelper.java
===================================================================
--- core/trunk/cache-infinispan/src/main/java/org/hibernate/cache/infinispan/util/CacheHelper.java	                        (rev 0)
+++ core/trunk/cache-infinispan/src/main/java/org/hibernate/cache/infinispan/util/CacheHelper.java	2009-08-21 08:39:33 UTC (rev 17386)
@@ -0,0 +1,447 @@
+/*
+ * Hibernate, Relational Persistence for Idiomatic Java
+ *
+ * Copyright (c) 2007, Red Hat, Inc. and/or it's affiliates or third-party contributors as
+ * indicated by the @author tags or express copyright attribution
+ * statements applied by the authors.  All third-party contributions are
+ * distributed under license by Red Hat, Inc. and/or it's affiliates.
+ *
+ * This copyrighted material is made available to anyone wishing to use, modify,
+ * copy, or redistribute it subject to the terms and conditions of the GNU
+ * Lesser General Public License, as published by the Free Software Foundation.
+ *
+ * This program 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 distribution; if not, write to:
+ * Free Software Foundation, Inc.
+ * 51 Franklin Street, Fifth Floor
+ * Boston, MA  02110-1301  USA
+ */
+package org.hibernate.cache.infinispan.util;
+
+import java.util.Set;
+
+import org.hibernate.cache.CacheException;
+import org.infinispan.Cache;
+import org.infinispan.config.Configuration;
+import org.infinispan.context.Flag;
+import org.infinispan.util.concurrent.TimeoutException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Helper for dealing with Infinisan cache instances.
+ * 
+ * @author Steve Ebersole
+ * @author Brian Stansberry
+ * @author Galder Zamarreño
+ * @since 3.5
+ */
+public class CacheHelper {
+
+   /** Key under which items are cached */
+   public static final String ITEM = "item";
+   /** Key and value used in a hack to create region root nodes */
+   public static final String DUMMY = "dummy";
+
+   private static final Logger log = LoggerFactory.getLogger(CacheHelper.class);
+
+   /**
+    * Disallow external instantiation of CacheHelper.
+    */
+   private CacheHelper() {
+   }
+
+   /**
+    * Is this cache participating in a cluster with invalidation?
+    * 
+    * @param cache
+    *           The cache to check.
+    * @return True if the cache is configured for synchronous/asynchronous invalidation; false
+    *         otherwise.
+    */
+   public static boolean isClusteredInvalidation(Cache cache) {
+      return isClusteredInvalidation(cache.getConfiguration().getCacheMode());
+   }
+
+   /**
+    * Does this cache mode indicate clustered invalidation?
+    * 
+    * @param cacheMode
+    *           The cache to check
+    * @return True if the cache mode is confiogured for synchronous/asynchronous invalidation; false
+    *         otherwise.
+    */
+   public static boolean isClusteredInvalidation(Configuration.CacheMode cacheMode) {
+      return cacheMode == Configuration.CacheMode.INVALIDATION_ASYNC
+               || cacheMode == Configuration.CacheMode.INVALIDATION_SYNC;
+   }
+
+   /**
+    * Is this cache participating in a cluster with replication?
+    * 
+    * @param cache
+    *           The cache to check.
+    * @return True if the cache is configured for synchronous/asynchronous invalidation; false
+    *         otherwise.
+    */
+   public static boolean isClusteredReplication(Cache cache) {
+      return isClusteredReplication(cache.getConfiguration().getCacheMode());
+   }
+
+   /**
+    * Does this cache mode indicate clustered replication?
+    * 
+    * @param cacheMode
+    *           The cache to check
+    * @return True if the cache mode is confiogured for synchronous/asynchronous invalidation; false
+    *         otherwise.
+    */
+   public static boolean isClusteredReplication(Configuration.CacheMode cacheMode) {
+      return cacheMode == Configuration.CacheMode.REPL_ASYNC || cacheMode == Configuration.CacheMode.REPL_SYNC;
+   }
+
+   public static boolean isSynchronous(Cache cache) {
+      return isSynchronous(cache.getConfiguration().getCacheMode());
+   }
+
+   public static boolean isSynchronous(Configuration.CacheMode cacheMode) {
+      return cacheMode == Configuration.CacheMode.REPL_SYNC || cacheMode == Configuration.CacheMode.INVALIDATION_SYNC;
+   }
+
+   public static Set getKeySet(Cache cache) {
+      return cache.keySet();
+   }
+
+   /**
+    * Builds an {@link Fqn} from <code>region</code> and <code>key</code> and performs a JBoss Cache
+    * <code>get(Fqn, Object)</code>, wrapping any exception in a {@link CacheException}.
+    * 
+    * @param cache
+    *           the cache to invoke on
+    * @param region
+    *           base Fqn for the cache region
+    * @param key
+    *           specific key to append to the <code>region</code> to form the full Fqn
+    */
+   public static Object get(Cache cache, Object key) throws CacheException {
+      try {
+         return cache.get(key);
+      } catch (Exception e) {
+         throw new CacheException(e);
+      }
+   }
+
+   /**
+    * Builds an {@link Fqn} from <code>region</code> and <code>key</code> and performs a JBoss Cache
+    * <code>get(Fqn, Object)</code>, wrapping any exception in a {@link CacheException}.
+    * 
+    * @param cache
+    *           the cache to invoke on
+    * @param key
+    *           specific key to append to the <code>region</code> to form the full Fqn
+    */
+   public static Object getAllowingTimeout(Cache cache, Object key) throws CacheException {
+      try {
+         return cache.get(key);
+      } catch (TimeoutException ignored) {
+         // ignore it
+         return null;
+      } catch (Exception e) {
+         throw new CacheException(e);
+      }
+   }
+
+   /**
+    * Builds an {@link Fqn} from <code>region</code> and <code>key</code> and performs a JBoss Cache
+    * <code>put(Object, Object)</code>, wrapping any exception in a {@link CacheException}.
+    * 
+    * @param cache
+    *           the cache to invoke on
+    * @param region
+    *           base Fqn for the cache region
+    * @param key
+    *           specific key to append to the <code>region</code> to form the full Fqn
+    * @param value
+    *           data to store in the cache node
+    */
+   public static void put(Cache cache, Object key, Object value) throws CacheException {
+      put(cache, key, value, null);
+   }
+
+   /**
+    * Builds an {@link Fqn} from <code>region</code> and <code>key</code> and performs a JBoss Cache
+    * <code>put(Object, Object)</code>, wrapping any exception in a {@link CacheException}.
+    * 
+    * @param cache
+    *           the cache to invoke on
+    * @param region
+    *           base Fqn for the cache region
+    * @param key
+    *           specific key to append to the <code>region</code> to form the full Fqn
+    * @param value
+    *           data to store in the cache node
+    * @param option
+    *           invocation Option to set for this invocation. May be <code>null</code>.
+    */
+   public static void put(Cache cache, Object key, Object value, Flag option) throws CacheException {
+      try {
+         cache.getAdvancedCache().put(key, value, option);
+      } catch (Exception e) {
+         throw new CacheException(e);
+      }
+   }
+
+   /**
+    * Builds an {@link Fqn} from <code>region</code> and <code>key</code> and performs a JBoss Cache
+    * <code>put(Object, Object)</code>, ignoring any {@link TimeoutException} and wrapping any other
+    * exception in a {@link CacheException}.
+    * 
+    * @param cache
+    *           the cache to invoke on
+    * @param region
+    *           base Fqn for the cache region
+    * @param key
+    *           specific key to append to the <code>region</code> to form the full Fqn
+    * @param value
+    *           data to store in the cache node
+    * @param option
+    *           invocation Option to set for this invocation. May be <code>null</code>.
+    */
+   public static void putAllowingTimeout(Cache cache, Object key, Object value, Flag... option) throws CacheException {
+      try {
+         cache.getAdvancedCache().put(key, value, option);
+      } catch (TimeoutException allowed) {
+         // ignore it
+      } catch (Exception e) {
+         throw new CacheException(e);
+      }
+   }
+
+   /**
+    * Builds an {@link Fqn} from <code>region</code> and <code>key</code> and performs a JBoss Cache
+    * <code>putForExternalRead(Object, Object)</code>, wrapping any exception in a
+    * {@link CacheException}. Ignores any JBoss Cache {@link TimeoutException}.
+    * 
+    * @param cache
+    *           the cache to invoke on
+    * @param region
+    *           base Fqn for the cache region
+    * @param key
+    *           specific key to append to the <code>region</code> to form the full Fqn
+    * @param value
+    *           data to store in the cache node
+    */
+   public static boolean putForExternalRead(Cache cache, Object key, Object value) throws CacheException {
+      return putForExternalRead(cache, key, value, (Flag[])null);
+   }
+
+   /**
+    * Builds an {@link Fqn} from <code>region</code> and <code>key</code> and performs a JBoss Cache
+    * <code>putForExternalRead(Object, Object)</code>, wrapping any exception in a
+    * {@link CacheException}. Ignores any JBoss Cache {@link TimeoutException}.
+    * 
+    * @param cache
+    *           the cache to invoke on
+    * @param region
+    *           base Fqn for the cache region
+    * @param key
+    *           specific key to append to the <code>region</code> to form the full Fqn
+    * @param value
+    *           data to store in the cache node
+    * @param option
+    *           invocation Option to set for this invocation. May be <code>null</code>.
+    */
+   public static boolean putForExternalRead(Cache cache, Object key, Object value, Flag... option) throws CacheException {
+      try {
+         cache.getAdvancedCache().putForExternalRead(key, value, option);
+         return true;
+      } catch (TimeoutException te) {
+         // ignore!
+         log.debug("ignoring write lock acquisition failure");
+         return false;
+      } catch (Throwable t) {
+         throw new CacheException(t);
+      }
+   }
+
+   /**
+    * Builds an {@link Fqn} from <code>region</code> and <code>key</code> and performs a JBoss Cache
+    * <code>removeNode(Fqn)</code>, wrapping any exception in a {@link CacheException}.
+    * 
+    * @param cache
+    *           the cache to invoke on
+    * @param region
+    *           base Fqn for the cache region
+    * @param key
+    *           specific key to append to the <code>region</code> to form the full Fqn
+    */
+   public static void remove(Cache cache, Object key) throws CacheException {
+      remove(cache, key, null);
+   }
+
+   /**
+    * Builds an {@link Fqn} from <code>region</code> and <code>key</code> and performs a JBoss Cache
+    * <code>removeNode(Fqn)</code>, wrapping any exception in a {@link CacheException}.
+    * 
+    * @param cache
+    *           the cache to invoke on
+    * @param region
+    *           base Fqn for the cache region
+    * @param key
+    *           specific key to append to the <code>region</code> to form the full Fqn
+    * @param option
+    *           invocation Option to set for this invocation. May be <code>null</code>.
+    */
+   public static void remove(Cache cache, Object key, Flag option) throws CacheException {
+      try {
+         cache.getAdvancedCache().remove(key, option);
+      } catch (Exception e) {
+         throw new CacheException(e);
+      }
+   }
+
+   /**
+    * Performs a JBoss Cache <code>removeNode(Fqn)</code>, wrapping any exception in a
+    * {@link CacheException}.
+    * 
+    * @param cache
+    *           the cache to invoke on
+    * @param region
+    *           base Fqn for the cache region
+    */
+   public static void removeAll(Cache cache) throws CacheException {
+      try {
+         cache.clear();
+      } catch (Exception e) {
+         throw new CacheException(e);
+      }
+   }
+
+   /**
+    * Performs a JBoss Cache <code>removeNode(Fqn)</code>, wrapping any exception in a
+    * {@link CacheException}.
+    * 
+    * @param cache
+    *           the cache to invoke on
+    * @param region
+    *           base Fqn for the cache region
+    * @param option
+    *           invocation Option to set for this invocation. May be <code>null</code>.
+    */
+   public static void removeAll(Cache cache, Flag option) throws CacheException {
+      try {
+         cache.getAdvancedCache().clear(option);
+      } catch (Exception e) {
+         throw new CacheException(e);
+      }
+   }
+
+   /**
+    * Performs a JBoss Cache <code>removeNode(Fqn)</code>, wrapping any exception in a
+    * {@link CacheException}.
+    * 
+    * @param cache
+    *           the cache to invoke on
+    * @param region
+    *           base Fqn for the cache region
+    * @param option
+    *           invocation Option to set for this invocation. May be <code>null</code>.
+    */
+   public static void removeKey(Cache cache, Object key, Flag option) throws CacheException {
+      try {
+         cache.getAdvancedCache().remove(key, option);
+      } catch (Exception e) {
+         throw new CacheException(e);
+      }
+   }
+   
+   public static void removeKey(Cache cache, Object key) throws CacheException {
+      try {
+         cache.remove(key);
+      } catch (Exception e) {
+         throw new CacheException(e);
+      }
+   }
+   
+//   public static void evict(Cache cache, Object key) throws CacheException {
+//      try {
+//         cache.evict(key);
+//      } catch (Exception e) {
+//         throw new CacheException(e);
+//      }
+//   }
+   
+   public static boolean containsKey(Cache cache, Object key, Flag... flags) {
+      try {
+         return cache.getAdvancedCache().containsKey(key, flags);
+      } catch (Exception e) {
+         throw new CacheException(e);
+      }
+   }
+
+//   public static Node addNode(Cache cache, Fqn fqn, boolean localOnly, boolean resident) throws CacheException {
+//      try {
+//         Option option = null;
+//         if (localOnly) {
+//            option = new Option();
+//            option.setCacheModeLocal(localOnly);
+//         }
+//
+//         Node root = cache.getRoot();
+//         setInvocationOption(cache, option);
+//         // FIXME hack to work around fact that calling
+//         // Node added = root.addChild( fqn ); doesn't
+//         // properly set the version on the node
+//         Node added = null;
+//         if (version == null) {
+//            added = root.addChild(fqn);
+//         } else {
+//            cache.put(fqn, DUMMY, DUMMY);
+//            added = root.getChild(fqn);
+//         }
+//         if (resident)
+//            added.setResident(true);
+//         return added;
+//      } catch (Exception e) {
+//         throw new CacheException(e);
+//      }
+//   }
+
+   /**
+//    * Assigns the given Option to the cache's {@link InvocationContext}. Does nothing if
+//    * <code>option</code> is <code>null</code>.
+//    * 
+//    * @param cache
+//    *           the cache. Cannot be <code>null</code>.
+//    * @param option
+//    *           the option. May be <code>null</code>.
+//    * 
+//    * @see {@link Cache#getInvocationContext()}
+//    * @see {@link InvocationContext#setOptionOverrides(Option)}
+//    */
+//   public static void setInvocationOption(Cache cache, Option option) {
+//      if (option != null) {
+//         cache.getInvocationContext().setOptionOverrides(option);
+//      }
+//   }
+
+//   /**
+//    * Creates an {@link Option} using the given {@link DataVersion} and passes it to
+//    * {@link #setInvocationOption(Cache, Option)}.
+//    * 
+//    * @param cache
+//    *           the cache to set the Option on. Cannot be <code>null</code>.
+//    * @param version
+//    *           the DataVersion to set. Cannot be <code>null</code>.
+//    */
+//   public static void setDataVersionOption(Cache cache, DataVersion version) {
+//      Option option = new Option();
+//      option.setDataVersion(version);
+//      setInvocationOption(cache, option);
+//   }
+}

Added: core/trunk/cache-infinispan/src/main/resources/org/hibernate/cache/infinispan/builder/infinispan-configs.xml
===================================================================
--- core/trunk/cache-infinispan/src/main/resources/org/hibernate/cache/infinispan/builder/infinispan-configs.xml	                        (rev 0)
+++ core/trunk/cache-infinispan/src/main/resources/org/hibernate/cache/infinispan/builder/infinispan-configs.xml	2009-08-21 08:39:33 UTC (rev 17386)
@@ -0,0 +1,77 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<infinispan xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="urn:infinispan:config:4.0">
+   <global>
+      <transport transportClass = "org.infinispan.remoting.transport.jgroups.JGroupsTransport" 
+            clusterName="infinispan-hibernate-cluster" distributedSyncTimeout="50000">
+         <!-- Note that the JGroups transport uses sensible defaults if no configuration property is defined. -->
+         <properties>
+            <!-- TODO: Change to udp.xml once streaming transfer requirement has been removed.  -->
+            <property name="configurationFile" value="flush-udp.xml"/>
+         </properties>
+         <!-- See the JGroupsTransport javadocs for more flags -->
+      </transport>
+      <serialization marshallerClass="org.infinispan.marshall.VersionAwareMarshaller" version="4.0"/>
+   </global>
+
+   <default>
+      <!-- Used to register JMX statistics in any available MBean server -->
+      <jmxStatistics enabled="false"/>
+   </default>
+
+   <!-- Default configuration is appropriate for entity/collection caching. -->
+   <namedCache name="entity">
+      <clustering mode="invalidation">
+         <stateRetrieval fetchInMemoryState="false" timeout="20000"/>
+         <sync replTimeout="20000"/>
+      </clustering>
+      <!-- Note: 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.
+       -->
+      <locking isolationLevel="READ_COMMITTED" concurrencyLevel="1000" lockAcquisitionTimeout="15000"/>
+      <!-- Eviction configuration.  WakeupInterval defines how often the eviction thread runs, in milliseconds.  
+           0 means the eviction thread will never run.  A separate executor is used for eviction in each cache. -->
+      <eviction wakeUpInterval="5000" maxEntries="10000" strategy="LRU"/>
+      <expiration maxIdle="100000"/>
+      <lazyDeserialization enabled="true"/>
+   </namedCache>
+   
+   <!-- A config appropriate for query caching. Does not replicate queries. -->
+   <namedCache name="local-query">
+      <locking isolationLevel="READ_COMMITTED" concurrencyLevel="1000" lockAcquisitionTimeout="15000"/>
+      <!--Eviction configuration.  WakeupInterval defines how often the eviction thread runs, in milliseconds.  0 means
+         the eviction thread will never run.  A separate executor is used for eviction in each cache. -->
+      <eviction wakeUpInterval="5000" maxEntries="10000" strategy="LRU"/>
+      <expiration maxIdle="100000"/>
+   </namedCache>
+
+   <!-- A query cache that replicates queries. Replication is asynchronous. -->
+   <namedCache name="replicated-query">
+      <clustering mode="replication">
+         <stateRetrieval fetchInMemoryState="false"/>
+         <async/>
+      </clustering>
+      <locking isolationLevel="READ_COMMITTED" concurrencyLevel="1000" lockAcquisitionTimeout="15000"/>
+      <!--Eviction configuration.  WakeupInterval defines how often the eviction thread runs, in milliseconds.  0 means
+         the eviction thread will never run.  A separate executor is used for eviction in each cache. -->
+      <eviction wakeUpInterval="5000" maxEntries="10000" strategy="LRU"/>
+      <expiration maxIdle="100000"/>
+   </namedCache>
+
+   <!-- 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. -->
+   <namedCache name="timestamps">
+      <clustering mode="replication">
+         <stateRetrieval fetchInMemoryState="true" timeout="20000"/>
+         <async/>
+      </clustering>
+      <locking isolationLevel="READ_COMMITTED" concurrencyLevel="1000" lockAcquisitionTimeout="15000"/>
+      <lazyDeserialization enabled="true"/>
+      <!--  Don't ever evict modification timestamps -->
+      <eviction wakeUpInterval="0" strategy="NONE"/>
+   </namedCache>
+
+</infinispan>
\ No newline at end of file

Added: core/trunk/cache-infinispan/src/test/java/org/hibernate/test/cache/infinispan/AbstractEntityCollectionRegionTestCase.java
===================================================================
--- core/trunk/cache-infinispan/src/test/java/org/hibernate/test/cache/infinispan/AbstractEntityCollectionRegionTestCase.java	                        (rev 0)
+++ core/trunk/cache-infinispan/src/test/java/org/hibernate/test/cache/infinispan/AbstractEntityCollectionRegionTestCase.java	2009-08-21 08:39:33 UTC (rev 17386)
@@ -0,0 +1,128 @@
+/*
+ * Hibernate, Relational Persistence for Idiomatic Java
+ *
+ * Copyright (c) 2007, Red Hat, Inc. and/or it's affiliates or third-party contributors as
+ * indicated by the @author tags or express copyright attribution
+ * statements applied by the authors.  All third-party contributions are
+ * distributed under license by Red Hat, Inc. and/or it's affiliates.
+ *
+ * This copyrighted material is made available to anyone wishing to use, modify,
+ * copy, or redistribute it subject to the terms and conditions of the GNU
+ * Lesser General Public License, as published by the Free Software Foundation.
+ *
+ * This program 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 distribution; if not, write to:
+ * Free Software Foundation, Inc.
+ * 51 Franklin Street, Fifth Floor
+ * Boston, MA  02110-1301  USA
+ */
+package org.hibernate.test.cache.infinispan;
+
+import java.util.Properties;
+
+import org.hibernate.cache.CacheDataDescription;
+import org.hibernate.cache.RegionFactory;
+import org.hibernate.cache.TransactionalDataRegion;
+import org.hibernate.cache.access.AccessType;
+import org.hibernate.cache.infinispan.InfinispanRegionFactory;
+import org.hibernate.cfg.Configuration;
+import org.hibernate.cfg.Environment;
+import org.hibernate.test.cache.infinispan.util.CacheTestUtil;
+
+/**
+ * Base class for tests of EntityRegion and CollectionRegion implementations.
+ * 
+ * @author Galder Zamarreño
+ * @since 3.5
+ */
+public abstract class AbstractEntityCollectionRegionTestCase extends AbstractRegionImplTestCase {
+
+    /**
+     * Create a new EntityCollectionRegionTestCaseBase.
+     * 
+     * @param name
+     */
+    public AbstractEntityCollectionRegionTestCase(String name) {
+        super(name);
+    }
+   
+    /** 
+     * Creates a Region backed by an PESSIMISTIC locking JBoss Cache, and then 
+     * ensures that it handles calls to buildAccessStrategy as expected when 
+     * all the various {@link AccessType}s are passed as arguments.
+     */
+    public void testSupportedAccessTypes() throws Exception {
+        supportedAccessTypeTest();
+    }
+    
+    private void supportedAccessTypeTest() throws Exception {
+        Configuration cfg = CacheTestUtil.buildConfiguration("test", InfinispanRegionFactory.class, true, false);
+        String entityCfg = "entity";
+        cfg.setProperty(InfinispanRegionFactory.ENTITY_CACHE_RESOURCE_PROP, entityCfg);
+        InfinispanRegionFactory regionFactory = CacheTestUtil.startRegionFactory(cfg, getCacheTestSupport());
+        supportedAccessTypeTest(regionFactory, cfg.getProperties());
+    }
+    
+    /** 
+     * Creates a Region using the given factory, and then ensure that it
+     * handles calls to buildAccessStrategy as expected when all the
+     * various {@link AccessType}s are passed as arguments.
+     */
+    protected abstract void supportedAccessTypeTest(RegionFactory regionFactory, Properties properties);
+    
+    /**
+     * Test that the Region properly implements 
+     * {@link TransactionalDataRegion#isTransactionAware()}.
+     * 
+     * @throws Exception
+     */
+    public void testIsTransactionAware() throws Exception {
+        Configuration cfg = CacheTestUtil.buildConfiguration("test", InfinispanRegionFactory.class, true, false);
+//        cfg.setProperty(SharedCacheInstanceManager.CACHE_RESOURCE_PROP, CacheTestUtil.LOCAL_PESSIMISTIC_CACHE);
+
+        InfinispanRegionFactory regionFactory = CacheTestUtil.startRegionFactory(cfg, getCacheTestSupport());
+        
+        TransactionalDataRegion region = (TransactionalDataRegion) createRegion(regionFactory, "test/test", cfg.getProperties(), getCacheDataDescription());
+        
+        assertTrue("Region is transaction-aware", region.isTransactionAware());
+        
+        CacheTestUtil.stopRegionFactory(regionFactory, getCacheTestSupport());
+        
+        cfg = CacheTestUtil.buildConfiguration("test", InfinispanRegionFactory.class, true, false);
+//        cfg.setProperty(SharedCacheInstanceManager.CACHE_RESOURCE_PROP, CacheTestUtil.LOCAL_PESSIMISTIC_CACHE);
+        // Make it non-transactional
+        cfg.getProperties().remove(Environment.TRANSACTION_MANAGER_STRATEGY);
+        
+        regionFactory = CacheTestUtil.startRegionFactory(cfg, getCacheTestSupport());
+        
+        region = (TransactionalDataRegion) createRegion(regionFactory, "test/test", cfg.getProperties(), getCacheDataDescription());
+        
+        assertFalse("Region is not transaction-aware", region.isTransactionAware());
+        
+        CacheTestUtil.stopRegionFactory(regionFactory, getCacheTestSupport());
+    }
+    
+    public void testGetCacheDataDescription() throws Exception {
+        Configuration cfg = CacheTestUtil.buildConfiguration("test", InfinispanRegionFactory.class, true, false);
+//        cfg.setProperty(SharedCacheInstanceManager.CACHE_RESOURCE_PROP, CacheTestUtil.LOCAL_PESSIMISTIC_CACHE);
+        
+        InfinispanRegionFactory regionFactory = CacheTestUtil.startRegionFactory(cfg, getCacheTestSupport());
+        
+        TransactionalDataRegion region = (TransactionalDataRegion) createRegion(regionFactory, "test/test", cfg.getProperties(), getCacheDataDescription());
+        
+        CacheDataDescription cdd = region.getCacheDataDescription();
+        
+        assertNotNull(cdd);
+        
+        CacheDataDescription expected = getCacheDataDescription();
+        assertEquals(expected.isMutable(), cdd.isMutable());
+        assertEquals(expected.isVersioned(), cdd.isVersioned());
+        assertEquals(expected.getVersionComparator(), cdd.getVersionComparator());
+        
+    }
+}

Added: core/trunk/cache-infinispan/src/test/java/org/hibernate/test/cache/infinispan/AbstractGeneralDataRegionTestCase.java
===================================================================
--- core/trunk/cache-infinispan/src/test/java/org/hibernate/test/cache/infinispan/AbstractGeneralDataRegionTestCase.java	                        (rev 0)
+++ core/trunk/cache-infinispan/src/test/java/org/hibernate/test/cache/infinispan/AbstractGeneralDataRegionTestCase.java	2009-08-21 08:39:33 UTC (rev 17386)
@@ -0,0 +1,195 @@
+/*
+ * Hibernate, Relational Persistence for Idiomatic Java
+ *
+ * Copyright (c) 2007, Red Hat, Inc. and/or it's affiliates or third-party contributors as
+ * indicated by the @author tags or express copyright attribution
+ * statements applied by the authors.  All third-party contributions are
+ * distributed under license by Red Hat, Inc. and/or it's affiliates.
+ *
+ * This copyrighted material is made available to anyone wishing to use, modify,
+ * copy, or redistribute it subject to the terms and conditions of the GNU
+ * Lesser General Public License, as published by the Free Software Foundation.
+ *
+ * This program 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 distribution; if not, write to:
+ * Free Software Foundation, Inc.
+ * 51 Franklin Street, Fifth Floor
+ * Boston, MA  02110-1301  USA
+ */
+package org.hibernate.test.cache.infinispan;
+
+import java.util.Set;
+
+import org.hibernate.cache.GeneralDataRegion;
+import org.hibernate.cache.QueryResultsRegion;
+import org.hibernate.cache.Region;
+import org.hibernate.cache.infinispan.InfinispanRegionFactory;
+import org.hibernate.cache.infinispan.util.CacheHelper;
+import org.hibernate.cfg.Configuration;
+import org.hibernate.test.cache.infinispan.util.CacheTestUtil;
+import org.infinispan.Cache;
+import org.infinispan.transaction.tm.BatchModeTransactionManager;
+
+/**
+ * Base class for tests of QueryResultsRegion and TimestampsRegion.
+ * 
+ * @author Galder Zamarreño
+ * @since 3.5
+ */
+public abstract class AbstractGeneralDataRegionTestCase extends AbstractRegionImplTestCase {
+   protected static final String KEY = "Key";
+
+   protected static final String VALUE1 = "value1";
+   protected static final String VALUE2 = "value2";
+
+   public AbstractGeneralDataRegionTestCase(String name) {
+      super(name);
+   }
+
+   @Override
+   protected void putInRegion(Region region, Object key, Object value) {
+      ((GeneralDataRegion) region).put(key, value);
+   }
+
+   @Override
+   protected void removeFromRegion(Region region, Object key) {
+      ((GeneralDataRegion) region).evict(key);
+   }
+
+   /**
+    * Test method for {@link QueryResultsRegion#evict(java.lang.Object)}.
+    * 
+    * FIXME add testing of the "immediately without regard for transaction isolation" bit in the
+    * CollectionRegionAccessStrategy API.
+    */
+   public void testEvict() throws Exception {
+      evictOrRemoveTest();
+   }
+
+   private void evictOrRemoveTest() throws Exception {
+      Configuration cfg = createConfiguration();
+      InfinispanRegionFactory regionFactory = CacheTestUtil.startRegionFactory(cfg, getCacheTestSupport());
+      Cache localCache = getInfinispanCache(regionFactory);
+      boolean invalidation = CacheHelper.isClusteredInvalidation(localCache);
+
+      // Sleep a bit to avoid concurrent FLUSH problem
+      avoidConcurrentFlush();
+
+      GeneralDataRegion localRegion = (GeneralDataRegion) createRegion(regionFactory,
+               getStandardRegionName(REGION_PREFIX), cfg.getProperties(), null);
+
+      cfg = createConfiguration();
+      regionFactory = CacheTestUtil.startRegionFactory(cfg, getCacheTestSupport());
+
+      GeneralDataRegion remoteRegion = (GeneralDataRegion) createRegion(regionFactory,
+               getStandardRegionName(REGION_PREFIX), cfg.getProperties(), null);
+
+      assertNull("local is clean", localRegion.get(KEY));
+      assertNull("remote is clean", remoteRegion.get(KEY));
+
+      localRegion.put(KEY, VALUE1);
+      assertEquals(VALUE1, localRegion.get(KEY));
+
+      // allow async propagation
+      sleep(250);
+      Object expected = invalidation ? null : VALUE1;
+      assertEquals(expected, remoteRegion.get(KEY));
+
+      localRegion.evict(KEY);
+
+      // allow async propagation
+      sleep(250);
+      assertEquals(null, localRegion.get(KEY));
+      assertEquals(null, remoteRegion.get(KEY));
+   }
+
+   protected abstract String getStandardRegionName(String regionPrefix);
+
+   /**
+    * Test method for {@link QueryResultsRegion#evictAll()}.
+    * 
+    * FIXME add testing of the "immediately without regard for transaction isolation" bit in the
+    * CollectionRegionAccessStrategy API.
+    */
+   public void testEvictAll() throws Exception {
+      evictOrRemoveAllTest("entity");
+   }
+
+   private void evictOrRemoveAllTest(String configName) throws Exception {
+      Configuration cfg = createConfiguration();
+      InfinispanRegionFactory regionFactory = CacheTestUtil.startRegionFactory(cfg, getCacheTestSupport());
+      Cache localCache = getInfinispanCache(regionFactory);
+//      boolean invalidation = CacheHelper.isClusteredInvalidation(localCache);
+
+      // Sleep a bit to avoid concurrent FLUSH problem
+      avoidConcurrentFlush();
+
+      GeneralDataRegion localRegion = (GeneralDataRegion) createRegion(regionFactory,
+               getStandardRegionName(REGION_PREFIX), cfg.getProperties(), null);
+
+      cfg = createConfiguration();
+      regionFactory = CacheTestUtil.startRegionFactory(cfg, getCacheTestSupport());
+      Cache remoteCache = getInfinispanCache(regionFactory);
+
+      // Sleep a bit to avoid concurrent FLUSH problem
+      avoidConcurrentFlush();
+
+      GeneralDataRegion remoteRegion = (GeneralDataRegion) createRegion(regionFactory,
+               getStandardRegionName(REGION_PREFIX), cfg.getProperties(), null);
+//      String regionName = REGION_PREFIX;
+
+      Set children = CacheHelper.getKeySet(localCache);
+      assertEquals("No children in " + children, 0, children.size());
+
+      children = CacheHelper.getKeySet(remoteCache);
+      assertEquals("No children in " + children, 0, children.size());
+
+      assertNull("local is clean", localRegion.get(KEY));
+      assertNull("remote is clean", remoteRegion.get(KEY));
+
+      localRegion.put(KEY, VALUE1);
+      assertEquals(VALUE1, localRegion.get(KEY));
+
+      // Allow async propagation
+      sleep(250);
+
+      remoteRegion.put(KEY, VALUE1);
+      assertEquals(VALUE1, remoteRegion.get(KEY));
+
+      // Allow async propagation
+      sleep(250);
+
+      localRegion.evictAll();
+
+      // allow async propagation
+      sleep(250);
+      // This should re-establish the region root node in the optimistic case
+      assertNull(localRegion.get(KEY));
+
+      // Re-establishing the region root on the local node doesn't
+      // propagate it to other nodes. Do a get on the remote node to re-establish
+      // This only adds a node in the case of optimistic locking
+      assertEquals(null, remoteRegion.get(KEY));
+
+      assertEquals("local is clean", null, localRegion.get(KEY));
+      assertEquals("remote is clean", null, remoteRegion.get(KEY));
+   }
+
+   protected Configuration createConfiguration() {
+      Configuration cfg = CacheTestUtil.buildConfiguration("test", InfinispanRegionFactory.class, false, true);
+      return cfg;
+   }
+
+   protected void rollback() {
+      try {
+         BatchModeTransactionManager.getInstance().rollback();
+      } catch (Exception e) {
+         log.error(e.getMessage(), e);
+      }
+   }
+}
\ No newline at end of file

Added: core/trunk/cache-infinispan/src/test/java/org/hibernate/test/cache/infinispan/AbstractNonFunctionalTestCase.java
===================================================================
--- core/trunk/cache-infinispan/src/test/java/org/hibernate/test/cache/infinispan/AbstractNonFunctionalTestCase.java	                        (rev 0)
+++ core/trunk/cache-infinispan/src/test/java/org/hibernate/test/cache/infinispan/AbstractNonFunctionalTestCase.java	2009-08-21 08:39:33 UTC (rev 17386)
@@ -0,0 +1,97 @@
+/*
+ * Hibernate, Relational Persistence for Idiomatic Java
+ *
+ * Copyright (c) 2007, Red Hat, Inc. and/or it's affiliates or third-party contributors as
+ * indicated by the @author tags or express copyright attribution
+ * statements applied by the authors.  All third-party contributions are
+ * distributed under license by Red Hat, Inc. and/or it's affiliates.
+ *
+ * This copyrighted material is made available to anyone wishing to use, modify,
+ * copy, or redistribute it subject to the terms and conditions of the GNU
+ * Lesser General Public License, as published by the Free Software Foundation.
+ *
+ * This program 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 distribution; if not, write to:
+ * Free Software Foundation, Inc.
+ * 51 Franklin Street, Fifth Floor
+ * Boston, MA  02110-1301  USA
+ */
+package org.hibernate.test.cache.infinispan;
+
+import org.hibernate.cache.RegionFactory;
+import org.hibernate.junit.UnitTestCase;
+import org.hibernate.test.cache.infinispan.util.CacheTestSupport;
+import org.infinispan.Cache;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Base class for all non-functional tests of Infinispan integration.
+ * 
+ * @author Galder Zamarreño
+ * @since 3.5
+ */
+public abstract class AbstractNonFunctionalTestCase extends UnitTestCase {
+
+    public static final String REGION_PREFIX = "test";
+    
+    private CacheTestSupport testSupport;
+    protected final Logger log = LoggerFactory.getLogger(getClass());
+    
+    public AbstractNonFunctionalTestCase(String name) {
+        super(name);
+        testSupport = new CacheTestSupport(log);
+    }
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        
+        testSupport.setUp();
+    }
+
+    @Override
+    protected void tearDown() throws Exception {
+        super.tearDown();
+        
+        testSupport.tearDown();
+    }
+
+    protected void registerCache(Cache cache) {
+        testSupport.registerCache(cache);
+    }
+
+    protected void unregisterCache(Cache cache) {
+        testSupport.unregisterCache(cache);
+    }
+
+    protected void registerFactory(RegionFactory factory) {
+        testSupport.registerFactory(factory);
+    }
+
+    protected void unregisterFactory(RegionFactory factory) {
+        testSupport.unregisterFactory(factory);
+    }
+
+    protected CacheTestSupport getCacheTestSupport() {
+        return testSupport;
+    }
+    
+    protected void sleep(long ms) {
+        try {
+            Thread.sleep(ms);
+        }
+        catch (InterruptedException e) {
+            log.warn("Interrupted during sleep", e);
+        }
+    }
+    
+    protected void avoidConcurrentFlush() {
+        testSupport.avoidConcurrentFlush();
+    }
+}
\ No newline at end of file

Added: core/trunk/cache-infinispan/src/test/java/org/hibernate/test/cache/infinispan/AbstractRegionImplTestCase.java
===================================================================
--- core/trunk/cache-infinispan/src/test/java/org/hibernate/test/cache/infinispan/AbstractRegionImplTestCase.java	                        (rev 0)
+++ core/trunk/cache-infinispan/src/test/java/org/hibernate/test/cache/infinispan/AbstractRegionImplTestCase.java	2009-08-21 08:39:33 UTC (rev 17386)
@@ -0,0 +1,59 @@
+/*
+ * Hibernate, Relational Persistence for Idiomatic Java
+ *
+ * Copyright (c) 2007, Red Hat, Inc. and/or it's affiliates or third-party contributors as
+ * indicated by the @author tags or express copyright attribution
+ * statements applied by the authors.  All third-party contributions are
+ * distributed under license by Red Hat, Inc. and/or it's affiliates.
+ *
+ * This copyrighted material is made available to anyone wishing to use, modify,
+ * copy, or redistribute it subject to the terms and conditions of the GNU
+ * Lesser General Public License, as published by the Free Software Foundation.
+ *
+ * This program 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 distribution; if not, write to:
+ * Free Software Foundation, Inc.
+ * 51 Franklin Street, Fifth Floor
+ * Boston, MA  02110-1301  USA
+ */
+package org.hibernate.test.cache.infinispan;
+
+import java.util.Properties;
+
+import org.hibernate.cache.CacheDataDescription;
+import org.hibernate.cache.Region;
+import org.hibernate.cache.impl.CacheDataDescriptionImpl;
+import org.hibernate.cache.infinispan.InfinispanRegionFactory;
+import org.hibernate.util.ComparableComparator;
+import org.infinispan.Cache;
+
+/**
+ * Base class for tests of Region implementations.
+ * 
+ * @author Galder Zamarreño
+ * @since 3.5
+ */
+public abstract class AbstractRegionImplTestCase extends AbstractNonFunctionalTestCase {
+
+   public AbstractRegionImplTestCase(String name) {
+      super(name);
+   }
+
+   protected abstract Cache getInfinispanCache(InfinispanRegionFactory regionFactory);
+
+   protected abstract Region createRegion(InfinispanRegionFactory regionFactory, String regionName, Properties properties, CacheDataDescription cdd);
+
+   protected abstract void putInRegion(Region region, Object key, Object value);
+
+   protected abstract void removeFromRegion(Region region, Object key);
+
+   protected CacheDataDescription getCacheDataDescription() {
+      return new CacheDataDescriptionImpl(true, true, ComparableComparator.INSTANCE);
+   }
+
+}

Added: core/trunk/cache-infinispan/src/test/java/org/hibernate/test/cache/infinispan/InfinispanRegionFactoryTestCase.java
===================================================================
--- core/trunk/cache-infinispan/src/test/java/org/hibernate/test/cache/infinispan/InfinispanRegionFactoryTestCase.java	                        (rev 0)
+++ core/trunk/cache-infinispan/src/test/java/org/hibernate/test/cache/infinispan/InfinispanRegionFactoryTestCase.java	2009-08-21 08:39:33 UTC (rev 17386)
@@ -0,0 +1,418 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2009, Red Hat, Inc. and/or it's affiliates, 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.hibernate.test.cache.infinispan;
+
+import java.util.Properties;
+
+import org.hibernate.cache.CacheException;
+import org.hibernate.cache.infinispan.InfinispanRegionFactory;
+import org.hibernate.cache.infinispan.collection.CollectionRegionImpl;
+import org.hibernate.cache.infinispan.entity.EntityRegionImpl;
+import org.hibernate.cache.infinispan.query.QueryResultsRegionImpl;
+import org.hibernate.cache.infinispan.timestamp.TimestampsRegionImpl;
+import org.infinispan.Cache;
+import org.infinispan.config.Configuration;
+import org.infinispan.config.Configuration.CacheMode;
+import org.infinispan.eviction.EvictionStrategy;
+import org.infinispan.manager.CacheManager;
+import org.infinispan.manager.DefaultCacheManager;
+
+import junit.framework.TestCase;
+
+/**
+ * InfinispanRegionFactoryTestCase.
+ * 
+ * @author Galder Zamarreño
+ * @since 3.5
+ */
+public class InfinispanRegionFactoryTestCase extends TestCase {
+
+   public void testConfigurationProcessing() {
+      final String person = "com.acme.Person";
+      final String addresses = "com.acme.Person.addresses";
+      Properties p = new Properties();
+      p.setProperty("hibernate.cache.infinispan.com.acme.Person.cfg", "person-cache");
+      p.setProperty("hibernate.cache.infinispan.com.acme.Person.eviction.strategy", "LRU");
+      p.setProperty("hibernate.cache.infinispan.com.acme.Person.eviction.wake_up_interval", "2000");
+      p.setProperty("hibernate.cache.infinispan.com.acme.Person.eviction.max_entries", "5000 ");
+      p.setProperty("hibernate.cache.infinispan.com.acme.Person.eviction.lifespan", "60000");
+      p.setProperty("hibernate.cache.infinispan.com.acme.Person.eviction.max_idle", "30000");
+      p.setProperty("hibernate.cache.infinispan.com.acme.Person.addresses.cfg", "person-addresses-cache");
+      p.setProperty("hibernate.cache.infinispan.com.acme.Person.addresses.eviction.lifespan", "120000");      
+      p.setProperty("hibernate.cache.infinispan.com.acme.Person.addresses.eviction.max_idle", "60000");
+      p.setProperty("hibernate.cache.infinispan.query.cfg", "my-query-cache");
+      p.setProperty("hibernate.cache.infinispan.query.eviction.strategy", "FIFO");
+      p.setProperty("hibernate.cache.infinispan.query.eviction.wake_up_interval", "3000");
+      p.setProperty("hibernate.cache.infinispan.query.eviction.max_entries", "10000");
+
+      InfinispanRegionFactory factory = new InfinispanRegionFactory();
+      factory.start(null, p);
+
+      assertEquals("entity", factory.getTypeOverrides().get("entity").getCacheName());
+      assertEquals("entity", factory.getTypeOverrides().get("collection").getCacheName());
+      assertEquals("timestamps", factory.getTypeOverrides().get("timestamps").getCacheName());
+
+      assertEquals("person-cache", factory.getTypeOverrides().get(person).getCacheName());
+      assertEquals(EvictionStrategy.LRU, factory.getTypeOverrides().get(person).getEvictionStrategy());
+      assertEquals(2000, factory.getTypeOverrides().get(person).getEvictionWakeUpInterval());
+      assertEquals(5000, factory.getTypeOverrides().get(person).getEvictionMaxEntries());
+      assertEquals(60000, factory.getTypeOverrides().get(person).getExpirationLifespan());
+      assertEquals(30000, factory.getTypeOverrides().get(person).getExpirationMaxIdle());
+
+      assertEquals("person-addresses-cache", factory.getTypeOverrides().get(addresses).getCacheName());
+      assertEquals(120000, factory.getTypeOverrides().get(addresses).getExpirationLifespan());
+      assertEquals(60000, factory.getTypeOverrides().get(addresses).getExpirationMaxIdle());
+
+      assertEquals("my-query-cache", factory.getTypeOverrides().get("query").getCacheName());
+      assertEquals(EvictionStrategy.FIFO, factory.getTypeOverrides().get("query").getEvictionStrategy());
+      assertEquals(3000, factory.getTypeOverrides().get("query").getEvictionWakeUpInterval());
+      assertEquals(10000, factory.getTypeOverrides().get("query").getEvictionMaxEntries());
+   }
+   
+   public void testBuildEntityCollectionRegionsPersonPlusEntityCollectionOverrides() {
+      final String person = "com.acme.Person";
+      final String address = "com.acme.Address";
+      final String car = "com.acme.Car";
+      final String addresses = "com.acme.Person.addresses";
+      final String parts = "com.acme.Car.parts";
+      Properties p = new Properties();
+      // First option, cache defined for entity and overrides for generic entity data type and entity itself.
+      p.setProperty("hibernate.cache.infinispan.com.acme.Person.cfg", "person-cache");
+      p.setProperty("hibernate.cache.infinispan.com.acme.Person.eviction.strategy", "LRU");
+      p.setProperty("hibernate.cache.infinispan.com.acme.Person.eviction.wake_up_interval", "2000");
+      p.setProperty("hibernate.cache.infinispan.com.acme.Person.eviction.max_entries", "5000 ");
+      p.setProperty("hibernate.cache.infinispan.com.acme.Person.eviction.lifespan", "60000");
+      p.setProperty("hibernate.cache.infinispan.com.acme.Person.eviction.max_idle", "30000");
+      p.setProperty("hibernate.cache.infinispan.entity.cfg", "myentity-cache");
+      p.setProperty("hibernate.cache.infinispan.entity.eviction.strategy", "FIFO");
+      p.setProperty("hibernate.cache.infinispan.entity.eviction.wake_up_interval", "3000");
+      p.setProperty("hibernate.cache.infinispan.entity.eviction.max_entries", "20000");
+      p.setProperty("hibernate.cache.infinispan.com.acme.Person.addresses.cfg", "addresses-cache");
+      p.setProperty("hibernate.cache.infinispan.com.acme.Person.addresses.eviction.strategy", "FIFO");
+      p.setProperty("hibernate.cache.infinispan.com.acme.Person.addresses.eviction.wake_up_interval", "2500");
+      p.setProperty("hibernate.cache.infinispan.com.acme.Person.addresses.eviction.max_entries", "5500 ");
+      p.setProperty("hibernate.cache.infinispan.com.acme.Person.addresses.eviction.lifespan", "65000");
+      p.setProperty("hibernate.cache.infinispan.com.acme.Person.addresses.eviction.max_idle", "35000");
+      p.setProperty("hibernate.cache.infinispan.collection.cfg", "mycollection-cache");
+      p.setProperty("hibernate.cache.infinispan.collection.eviction.strategy", "LRU");
+      p.setProperty("hibernate.cache.infinispan.collection.eviction.wake_up_interval", "3500");
+      p.setProperty("hibernate.cache.infinispan.collection.eviction.max_entries", "25000");
+      InfinispanRegionFactory factory = new InfinispanRegionFactory();
+      factory.start(null, p);
+      CacheManager manager = factory.getCacheManager();
+      manager.getGlobalConfiguration().setTransportClass(null);
+//      Configuration config = new Configuration();
+//      // Set to local to avoid creating a JGroups channel
+//      config.setCacheMode(CacheMode.LOCAL);
+//      manager.defineConfiguration("entity", config);
+//      manager.defineConfiguration("timestamps", config);
+//      manager.defineConfiguration("myentity-cache", config);
+//      manager.defineConfiguration("mycollection-cache", config);
+      try {
+         assertNotNull(factory.getTypeOverrides().get(person));
+         assertFalse(factory.getDefinedConfigurations().contains(person));
+         assertNotNull(factory.getTypeOverrides().get(addresses));
+         assertFalse(factory.getDefinedConfigurations().contains(addresses));
+         Cache cache = null;
+
+         EntityRegionImpl region = (EntityRegionImpl) factory.buildEntityRegion(person, p, null);
+         assertNotNull(factory.getTypeOverrides().get(person));
+         assertTrue(factory.getDefinedConfigurations().contains(person));
+         assertNull(factory.getTypeOverrides().get(address));
+         cache = region.getCache();
+         Configuration cacheCfg = cache.getConfiguration();
+         assertEquals(EvictionStrategy.LRU, cacheCfg.getEvictionStrategy());
+         assertEquals(2000, cacheCfg.getEvictionWakeUpInterval());
+         assertEquals(5000, cacheCfg.getEvictionMaxEntries());
+         assertEquals(60000, cacheCfg.getExpirationLifespan());
+         assertEquals(30000, cacheCfg.getExpirationMaxIdle());
+
+         region = (EntityRegionImpl) factory.buildEntityRegion(address, p, null);
+         assertNotNull(factory.getTypeOverrides().get(person));
+         assertTrue(factory.getDefinedConfigurations().contains(person));
+         assertNull(factory.getTypeOverrides().get(address));
+         cache = region.getCache();
+         cacheCfg = cache.getConfiguration();
+         assertEquals(EvictionStrategy.FIFO, cacheCfg.getEvictionStrategy());
+         assertEquals(3000, cacheCfg.getEvictionWakeUpInterval());
+         assertEquals(20000, cacheCfg.getEvictionMaxEntries());
+
+         region = (EntityRegionImpl) factory.buildEntityRegion(car, p, null);
+         assertNotNull(factory.getTypeOverrides().get(person));
+         assertTrue(factory.getDefinedConfigurations().contains(person));
+         assertNull(factory.getTypeOverrides().get(address));
+         cache = region.getCache();
+         cacheCfg = cache.getConfiguration();
+         assertEquals(EvictionStrategy.FIFO, cacheCfg.getEvictionStrategy());
+         assertEquals(3000, cacheCfg.getEvictionWakeUpInterval());
+         assertEquals(20000, cacheCfg.getEvictionMaxEntries());
+
+         CollectionRegionImpl collectionRegion = (CollectionRegionImpl) factory.buildCollectionRegion(addresses, p, null);
+         assertNotNull(factory.getTypeOverrides().get(addresses));
+         assertTrue(factory.getDefinedConfigurations().contains(person));
+         assertNull(factory.getTypeOverrides().get(parts));
+         cache = collectionRegion .getCache();
+         cacheCfg = cache.getConfiguration();
+         assertEquals(EvictionStrategy.FIFO, cacheCfg.getEvictionStrategy());
+         assertEquals(2500, cacheCfg.getEvictionWakeUpInterval());
+         assertEquals(5500, cacheCfg.getEvictionMaxEntries());
+         assertEquals(65000, cacheCfg.getExpirationLifespan());
+         assertEquals(35000, cacheCfg.getExpirationMaxIdle());
+
+         collectionRegion = (CollectionRegionImpl) factory.buildCollectionRegion(parts, p, null);
+         assertNotNull(factory.getTypeOverrides().get(addresses));
+         assertTrue(factory.getDefinedConfigurations().contains(addresses));
+         assertNull(factory.getTypeOverrides().get(parts));
+         cache = collectionRegion.getCache();
+         cacheCfg = cache.getConfiguration();
+         assertEquals(EvictionStrategy.LRU, cacheCfg.getEvictionStrategy());
+         assertEquals(3500, cacheCfg.getEvictionWakeUpInterval());
+         assertEquals(25000, cacheCfg.getEvictionMaxEntries());
+
+         collectionRegion = (CollectionRegionImpl) factory.buildCollectionRegion(parts, p, null);
+         assertNotNull(factory.getTypeOverrides().get(addresses));
+         assertTrue(factory.getDefinedConfigurations().contains(addresses));
+         assertNull(factory.getTypeOverrides().get(parts));
+         cache = collectionRegion.getCache();
+         cacheCfg = cache.getConfiguration();
+         assertEquals(EvictionStrategy.LRU, cacheCfg.getEvictionStrategy());
+         assertEquals(3500, cacheCfg.getEvictionWakeUpInterval());
+         assertEquals(25000, cacheCfg.getEvictionMaxEntries());
+      } finally {
+         factory.stop();
+      }
+   }
+
+   public void testBuildEntityCollectionRegionOverridesOnly() {
+      Cache cache = null;
+      Properties p = new Properties();
+      p.setProperty("hibernate.cache.infinispan.entity.eviction.strategy", "FIFO");
+      p.setProperty("hibernate.cache.infinispan.entity.eviction.wake_up_interval", "3000");
+      p.setProperty("hibernate.cache.infinispan.entity.eviction.max_entries", "30000");
+      p.setProperty("hibernate.cache.infinispan.collection.eviction.strategy", "LRU");
+      p.setProperty("hibernate.cache.infinispan.collection.eviction.wake_up_interval", "3500");
+      p.setProperty("hibernate.cache.infinispan.collection.eviction.max_entries", "35000");
+      InfinispanRegionFactory factory = new InfinispanRegionFactory();
+      factory.start(null, p);
+      CacheManager manager = factory.getCacheManager();
+      manager.getGlobalConfiguration().setTransportClass(null);
+      try {
+         EntityRegionImpl region = (EntityRegionImpl) factory.buildEntityRegion("com.acme.Address", p, null);
+         assertNull(factory.getTypeOverrides().get("com.acme.Address"));
+         cache = region.getCache();
+         Configuration cacheCfg = cache.getConfiguration();
+         assertEquals(EvictionStrategy.FIFO, cacheCfg.getEvictionStrategy());
+         assertEquals(3000, cacheCfg.getEvictionWakeUpInterval());
+         assertEquals(30000, cacheCfg.getEvictionMaxEntries());
+         assertEquals(100000, cacheCfg.getExpirationMaxIdle());
+
+         CollectionRegionImpl collectionRegion = (CollectionRegionImpl) factory.buildCollectionRegion("com.acme.Person.addresses", p, null);
+         assertNull(factory.getTypeOverrides().get("com.acme.Person.addresses"));
+         cache = collectionRegion.getCache();
+         cacheCfg = cache.getConfiguration();
+         assertEquals(EvictionStrategy.LRU, cacheCfg.getEvictionStrategy());
+         assertEquals(3500, cacheCfg.getEvictionWakeUpInterval());
+         assertEquals(35000, cacheCfg.getEvictionMaxEntries());
+         assertEquals(100000, cacheCfg.getExpirationMaxIdle());
+      } finally {
+         factory.stop();
+      }
+   }
+
+   public void testBuildEntityRegionPersonPlusEntityOverridesWithoutCfg() {
+      final String person = "com.acme.Person";
+      Properties p = new Properties();
+      // Third option, no cache defined for entity and overrides for generic entity data type and entity itself.
+      p.setProperty("hibernate.cache.infinispan.com.acme.Person.eviction.strategy", "LRU");
+      p.setProperty("hibernate.cache.infinispan.com.acme.Person.eviction.lifespan", "60000");
+      p.setProperty("hibernate.cache.infinispan.com.acme.Person.eviction.max_idle", "30000");
+      p.setProperty("hibernate.cache.infinispan.entity.cfg", "myentity-cache");
+      p.setProperty("hibernate.cache.infinispan.entity.eviction.strategy", "FIFO");
+      p.setProperty("hibernate.cache.infinispan.entity.eviction.wake_up_interval", "3000");
+      p.setProperty("hibernate.cache.infinispan.entity.eviction.max_entries", "10000");
+      InfinispanRegionFactory factory = new InfinispanRegionFactory();
+      factory.start(null, p);
+      CacheManager manager = factory.getCacheManager();
+      manager.getGlobalConfiguration().setTransportClass(null);
+      try {
+         assertNotNull(factory.getTypeOverrides().get(person));
+         assertFalse(factory.getDefinedConfigurations().contains(person));
+         EntityRegionImpl region = (EntityRegionImpl) factory.buildEntityRegion(person, p, null);
+         assertNotNull(factory.getTypeOverrides().get(person));
+         assertTrue(factory.getDefinedConfigurations().contains(person));
+         Cache cache = region.getCache();
+         Configuration cacheCfg = cache.getConfiguration();
+         assertEquals(EvictionStrategy.LRU, cacheCfg.getEvictionStrategy());
+         assertEquals(3000, cacheCfg.getEvictionWakeUpInterval());
+         assertEquals(10000, cacheCfg.getEvictionMaxEntries());
+         assertEquals(60000, cacheCfg.getExpirationLifespan());
+         assertEquals(30000, cacheCfg.getExpirationMaxIdle());
+      } finally {
+         factory.stop();
+      }
+   }
+
+   public void testTimestampValidation() {
+      Properties p = new Properties();
+      final DefaultCacheManager manager = new DefaultCacheManager();
+      InfinispanRegionFactory factory = new InfinispanRegionFactory() {
+         @Override
+         protected CacheManager createCacheManager(String configLoc) throws CacheException {
+            return manager;
+         }
+      };
+      Configuration config = new Configuration();
+      config.setCacheMode(CacheMode.INVALIDATION_SYNC);
+      manager.defineConfiguration("timestamps", config);
+      try {
+         factory.start(null, p);
+         fail("Should have failed saying that invalidation is not allowed for timestamp caches.");
+      } catch(CacheException ce) {
+      }
+   }
+   
+   public void testBuildDefaultTimestampsRegion() {
+      final String timestamps = "org.hibernate.cache.UpdateTimestampsCache";
+      Properties p = new Properties();
+      InfinispanRegionFactory factory = new InfinispanRegionFactory();
+      factory.start(null, p);
+      CacheManager manager = factory.getCacheManager();
+      manager.getGlobalConfiguration().setTransportClass(null);
+      try {
+         assertTrue(factory.getDefinedConfigurations().contains("timestamps"));
+         assertTrue(factory.getTypeOverrides().get("timestamps").getCacheName().equals("timestamps"));
+         Configuration config = new Configuration();
+         config.setFetchInMemoryState(false);
+         manager.defineConfiguration("timestamps", config);
+         TimestampsRegionImpl region = (TimestampsRegionImpl) factory.buildTimestampsRegion(timestamps, p);
+         Cache cache = region.getCache();
+         Configuration cacheCfg = cache.getConfiguration();
+         assertEquals(EvictionStrategy.NONE, cacheCfg.getEvictionStrategy());
+         assertEquals(CacheMode.REPL_ASYNC, cacheCfg.getCacheMode());
+         assertTrue(cacheCfg.isUseLazyDeserialization());
+      } finally {
+         factory.stop();
+      }
+   }
+   
+   public void testBuildDiffCacheNameTimestampsRegion() {
+      final String timestamps = "org.hibernate.cache.UpdateTimestampsCache";
+      Properties p = new Properties();
+      p.setProperty("hibernate.cache.infinispan.timestamps.cfg", "unrecommended-timestamps");
+      InfinispanRegionFactory factory = new InfinispanRegionFactory();
+      factory.start(null, p);
+      CacheManager manager = factory.getCacheManager();
+      manager.getGlobalConfiguration().setTransportClass(null);
+      try {
+         assertFalse(factory.getDefinedConfigurations().contains("timestamp"));
+         assertTrue(factory.getDefinedConfigurations().contains("unrecommended-timestamps"));
+         assertTrue(factory.getTypeOverrides().get("timestamps").getCacheName().equals("unrecommended-timestamps"));
+         Configuration config = new Configuration();
+         config.setFetchInMemoryState(false);
+         config.setCacheMode(CacheMode.REPL_SYNC);
+         manager.defineConfiguration("unrecommended-timestamps", config);
+         TimestampsRegionImpl region = (TimestampsRegionImpl) factory.buildTimestampsRegion(timestamps, p);
+         Cache cache = region.getCache();
+         Configuration cacheCfg = cache.getConfiguration();
+         assertEquals(EvictionStrategy.NONE, cacheCfg.getEvictionStrategy());
+         assertEquals(CacheMode.REPL_SYNC, cacheCfg.getCacheMode());
+         assertFalse(cacheCfg.isUseLazyDeserialization());
+      } finally {
+         factory.stop();
+      }
+   }
+   
+   public void testBuildTimestamRegionWithCacheNameOverride() {
+      final String timestamps = "org.hibernate.cache.UpdateTimestampsCache";
+      Properties p = new Properties();
+      InfinispanRegionFactory factory = new InfinispanRegionFactory();
+      p.setProperty("hibernate.cache.infinispan.timestamps.cfg", "mytimestamps-cache");
+      factory.start(null, p);
+      CacheManager manager = factory.getCacheManager();
+      manager.getGlobalConfiguration().setTransportClass(null);
+      try {
+         factory.buildTimestampsRegion(timestamps, p);
+         assertTrue(factory.getDefinedConfigurations().contains("mytimestamps-cache"));
+      } finally {
+         factory.stop();
+      }
+   }
+   
+   public void testBuildTimestamRegionWithFifoEvictionOverride() {
+      final String timestamps = "org.hibernate.cache.UpdateTimestampsCache";
+      Properties p = new Properties();
+      InfinispanRegionFactory factory = new InfinispanRegionFactory();
+      p.setProperty("hibernate.cache.infinispan.timestamps.cfg", "mytimestamps-cache");
+      p.setProperty("hibernate.cache.infinispan.timestamps.eviction.strategy", "FIFO");
+      p.setProperty("hibernate.cache.infinispan.timestamps.eviction.wake_up_interval", "3000");
+      p.setProperty("hibernate.cache.infinispan.timestamps.eviction.max_entries", "10000");
+      try {
+         factory.start(null, p);
+         CacheManager manager = factory.getCacheManager();
+         manager.getGlobalConfiguration().setTransportClass(null);
+         factory.buildTimestampsRegion(timestamps, p);
+         assertTrue(factory.getDefinedConfigurations().contains("mytimestamps-cache"));
+         fail("Should fail cos no eviction configurations are allowed for timestamp caches");
+      } catch(CacheException ce) {
+      } finally {
+         factory.stop();
+      }
+   }
+   
+   public void testBuildTimestamRegionWithNoneEvictionOverride() {
+      final String timestamps = "org.hibernate.cache.UpdateTimestampsCache";
+      Properties p = new Properties();
+      InfinispanRegionFactory factory = new InfinispanRegionFactory();
+      p.setProperty("hibernate.cache.infinispan.timestamps.cfg", "timestamps-none-eviction");
+      p.setProperty("hibernate.cache.infinispan.timestamps.eviction.strategy", "NONE");
+      p.setProperty("hibernate.cache.infinispan.timestamps.eviction.wake_up_interval", "3000");
+      p.setProperty("hibernate.cache.infinispan.timestamps.eviction.max_entries", "10000");
+      factory.start(null, p);
+      CacheManager manager = factory.getCacheManager();
+      manager.getGlobalConfiguration().setTransportClass(null);
+      try {
+         factory.buildTimestampsRegion(timestamps, p);
+         assertTrue(factory.getDefinedConfigurations().contains("timestamps-none-eviction"));
+      } finally {
+         factory.stop();
+      }
+   }
+   
+   public void testBuildQueryRegion() {
+      final String query = "org.hibernate.cache.StandardQueryCache";
+      Properties p = new Properties();
+      InfinispanRegionFactory factory = new InfinispanRegionFactory();
+      factory.start(null, p);
+      CacheManager manager = factory.getCacheManager();
+      manager.getGlobalConfiguration().setTransportClass(null);
+      try {
+         assertTrue(factory.getDefinedConfigurations().contains("local-query"));
+         QueryResultsRegionImpl region = (QueryResultsRegionImpl) factory.buildQueryResultsRegion(query, p);
+         Cache cache = region.getCache();
+         Configuration cacheCfg = cache.getConfiguration();
+         assertEquals(CacheMode.LOCAL, cacheCfg.getCacheMode());
+      } finally {
+         factory.stop();
+      }
+   }
+
+}

Added: core/trunk/cache-infinispan/src/test/java/org/hibernate/test/cache/infinispan/collection/AbstractCollectionRegionAccessStrategyTestCase.java
===================================================================
--- core/trunk/cache-infinispan/src/test/java/org/hibernate/test/cache/infinispan/collection/AbstractCollectionRegionAccessStrategyTestCase.java	                        (rev 0)
+++ core/trunk/cache-infinispan/src/test/java/org/hibernate/test/cache/infinispan/collection/AbstractCollectionRegionAccessStrategyTestCase.java	2009-08-21 08:39:33 UTC (rev 17386)
@@ -0,0 +1,541 @@
+/*
+ * Hibernate, Relational Persistence for Idiomatic Java
+ *
+ * Copyright (c) 2007, Red Hat, Inc. and/or it's affiliates or third-party contributors as
+ * indicated by the @author tags or express copyright attribution
+ * statements applied by the authors.  All third-party contributions are
+ * distributed under license by Red Hat, Inc. and/or it's affiliates.
+ *
+ * This copyrighted material is made available to anyone wishing to use, modify,
+ * copy, or redistribute it subject to the terms and conditions of the GNU
+ * Lesser General Public License, as published by the Free Software Foundation.
+ *
+ * This program 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 distribution; if not, write to:
+ * Free Software Foundation, Inc.
+ * 51 Franklin Street, Fifth Floor
+ * Boston, MA  02110-1301  USA
+ */
+package org.hibernate.test.cache.infinispan.collection;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+import junit.extensions.TestSetup;
+import junit.framework.AssertionFailedError;
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+import org.hibernate.cache.CacheDataDescription;
+import org.hibernate.cache.CollectionRegion;
+import org.hibernate.cache.access.AccessType;
+import org.hibernate.cache.access.CollectionRegionAccessStrategy;
+import org.hibernate.cache.impl.CacheDataDescriptionImpl;
+import org.hibernate.cache.infinispan.InfinispanRegionFactory;
+import org.hibernate.cache.infinispan.impl.BaseRegion;
+import org.hibernate.cache.infinispan.util.CacheHelper;
+import org.hibernate.cfg.Configuration;
+import org.hibernate.test.cache.infinispan.AbstractNonFunctionalTestCase;
+import org.hibernate.test.cache.infinispan.util.CacheTestUtil;
+import org.hibernate.util.ComparableComparator;
+import org.infinispan.Cache;
+import org.infinispan.context.Flag;
+import org.infinispan.transaction.tm.BatchModeTransactionManager;
+
+/**
+ * Base class for tests of CollectionRegionAccessStrategy impls.
+ * 
+ * @author Galder Zamarreño
+ * @since 3.5
+ */
+public abstract class AbstractCollectionRegionAccessStrategyTestCase extends AbstractNonFunctionalTestCase {
+
+   public static final String REGION_NAME = "test/com.foo.test";
+   public static final String KEY_BASE = "KEY";
+   public static final String VALUE1 = "VALUE1";
+   public static final String VALUE2 = "VALUE2";
+
+   protected static int testCount;
+
+   protected static Configuration localCfg;
+   protected static InfinispanRegionFactory localRegionFactory;
+   protected Cache localCache;
+   protected static Configuration remoteCfg;
+   protected static InfinispanRegionFactory remoteRegionFactory;
+   protected Cache remoteCache;
+
+   protected CollectionRegion localCollectionRegion;
+   protected CollectionRegionAccessStrategy localAccessStrategy;
+
+   protected CollectionRegion remoteCollectionRegion;
+   protected CollectionRegionAccessStrategy remoteAccessStrategy;
+
+   protected boolean invalidation;
+   protected boolean synchronous;
+
+   protected Exception node1Exception;
+   protected Exception node2Exception;
+
+   protected AssertionFailedError node1Failure;
+   protected AssertionFailedError node2Failure;
+
+   public static Test getTestSetup(Class testClass, String configName) {
+      TestSuite suite = new TestSuite(testClass);
+      return new AccessStrategyTestSetup(suite, configName);
+   }
+
+   public static Test getTestSetup(Test test, String configName) {
+      return new AccessStrategyTestSetup(test, configName);
+   }
+
+   /**
+    * Create a new TransactionalAccessTestCase.
+    * 
+    * @param name
+    */
+   public AbstractCollectionRegionAccessStrategyTestCase(String name) {
+      super(name);
+   }
+
+   protected abstract AccessType getAccessType();
+
+   protected void setUp() throws Exception {
+      super.setUp();
+
+      // Sleep a bit to avoid concurrent FLUSH problem
+      avoidConcurrentFlush();
+
+      localCollectionRegion = localRegionFactory.buildCollectionRegion(REGION_NAME, localCfg.getProperties(),
+               getCacheDataDescription());
+      localCache = ((BaseRegion) localCollectionRegion).getCache();
+      localAccessStrategy = localCollectionRegion.buildAccessStrategy(getAccessType());
+      invalidation = CacheHelper.isClusteredInvalidation(localCache);
+      synchronous = CacheHelper.isSynchronous(localCache);
+
+      // Sleep a bit to avoid concurrent FLUSH problem
+      avoidConcurrentFlush();
+
+      remoteCollectionRegion = remoteRegionFactory.buildCollectionRegion(REGION_NAME, remoteCfg.getProperties(),
+               getCacheDataDescription());
+      remoteCache = ((BaseRegion) remoteCollectionRegion).getCache();
+      remoteAccessStrategy = remoteCollectionRegion.buildAccessStrategy(getAccessType());
+
+      node1Exception = null;
+      node2Exception = null;
+
+      node1Failure = null;
+      node2Failure = null;
+   }
+
+   protected void tearDown() throws Exception {
+
+      super.tearDown();
+
+      if (localCollectionRegion != null)
+         localCollectionRegion.destroy();
+      if (remoteCollectionRegion != null)
+         remoteCollectionRegion.destroy();
+
+      try {
+         localCache.getAdvancedCache().clear(Flag.CACHE_MODE_LOCAL);
+      } catch (Exception e) {
+         log.error("Problem purging local cache", e);
+      }
+
+      try {
+         remoteCache.getAdvancedCache().clear(Flag.CACHE_MODE_LOCAL);
+      } catch (Exception e) {
+         log.error("Problem purging remote cache", e);
+      }
+
+      node1Exception = null;
+      node2Exception = null;
+
+      node1Failure = null;
+      node2Failure = null;
+   }
+
+   protected static Configuration createConfiguration(String configName, String configResource) {
+      Configuration cfg = CacheTestUtil.buildConfiguration(REGION_PREFIX, InfinispanRegionFactory.class, true, false);
+      cfg.setProperty(InfinispanRegionFactory.ENTITY_CACHE_RESOURCE_PROP, configName);
+      return cfg;
+   }
+
+   protected CacheDataDescription getCacheDataDescription() {
+      return new CacheDataDescriptionImpl(true, true, ComparableComparator.INSTANCE);
+   }
+
+   protected boolean isUsingInvalidation() {
+      return invalidation;
+   }
+
+   protected boolean isSynchronous() {
+      return synchronous;
+   }
+
+   /**
+    * This is just a setup test where we assert that the cache config is as we expected.
+    */
+   public abstract void testCacheConfiguration();
+
+   /**
+    * Test method for {@link TransactionalAccess#getRegion()}.
+    */
+   public void testGetRegion() {
+      assertEquals("Correct region", localCollectionRegion, localAccessStrategy.getRegion());
+   }
+
+   /**
+    * Test method for
+    * {@link TransactionalAccess#putFromLoad(java.lang.Object, java.lang.Object, long, java.lang.Object)}
+    * .
+    */
+   public void testPutFromLoad() throws Exception {
+      putFromLoadTest(false);
+   }
+
+   /**
+    * Test method for
+    * {@link TransactionalAccess#putFromLoad(java.lang.Object, java.lang.Object, long, java.lang.Object, boolean)}
+    * .
+    */
+   public void testPutFromLoadMinimal() throws Exception {
+      putFromLoadTest(true);
+   }
+
+   /**
+    * Simulate 2 nodes, both start, tx do a get, experience a cache miss, then 'read from db.' First
+    * does a putFromLoad, then an evict (to represent a change). Second tries to do a putFromLoad
+    * with stale data (i.e. it took longer to read from the db). Both commit their tx. Then both
+    * start a new tx and get. First should see the updated data; second should either see the
+    * updated data (isInvalidation()( == false) or null (isInvalidation() == true).
+    * 
+    * @param useMinimalAPI
+    * @throws Exception
+    */
+   private void putFromLoadTest(final boolean useMinimalAPI) throws Exception {
+
+      final String KEY = KEY_BASE + testCount++;
+
+      final CountDownLatch writeLatch1 = new CountDownLatch(1);
+      final CountDownLatch writeLatch2 = new CountDownLatch(1);
+      final CountDownLatch completionLatch = new CountDownLatch(2);
+
+      Thread node1 = new Thread() {
+
+         public void run() {
+
+            try {
+               long txTimestamp = System.currentTimeMillis();
+               BatchModeTransactionManager.getInstance().begin();
+
+               assertEquals("node1 starts clean", null, localAccessStrategy.get(KEY, txTimestamp));
+
+               writeLatch1.await();
+
+               if (useMinimalAPI) {
+                  localAccessStrategy.putFromLoad(KEY, VALUE2, txTimestamp, new Integer(2), true);
+               } else {
+                  localAccessStrategy.putFromLoad(KEY, VALUE2, txTimestamp, new Integer(2));
+               }
+
+               BatchModeTransactionManager.getInstance().commit();
+            } catch (Exception e) {
+               log.error("node1 caught exception", e);
+               node1Exception = e;
+               rollback();
+            } catch (AssertionFailedError e) {
+               node1Failure = e;
+               rollback();
+            } finally {
+               // Let node2 write
+               writeLatch2.countDown();
+               completionLatch.countDown();
+            }
+         }
+      };
+
+      Thread node2 = new Thread() {
+
+         public void run() {
+
+            try {
+               long txTimestamp = System.currentTimeMillis();
+               BatchModeTransactionManager.getInstance().begin();
+
+               assertNull("node2 starts clean", remoteAccessStrategy.get(KEY, txTimestamp));
+
+               // Let node1 write
+               writeLatch1.countDown();
+               // Wait for node1 to finish
+               writeLatch2.await();
+
+               // Let the first PFER propagate
+               sleep(200);
+
+               if (useMinimalAPI) {
+                  remoteAccessStrategy.putFromLoad(KEY, VALUE1, txTimestamp, new Integer(1), true);
+               } else {
+                  remoteAccessStrategy.putFromLoad(KEY, VALUE1, txTimestamp, new Integer(1));
+               }
+
+               BatchModeTransactionManager.getInstance().commit();
+            } catch (Exception e) {
+               log.error("node2 caught exception", e);
+               node2Exception = e;
+               rollback();
+            } catch (AssertionFailedError e) {
+               node2Failure = e;
+               rollback();
+            } finally {
+               completionLatch.countDown();
+            }
+         }
+      };
+
+      node1.setDaemon(true);
+      node2.setDaemon(true);
+
+      node1.start();
+      node2.start();
+
+      assertTrue("Threads completed", completionLatch.await(2, TimeUnit.SECONDS));
+
+      if (node1Failure != null)
+         throw node1Failure;
+      if (node2Failure != null)
+         throw node2Failure;
+
+      assertEquals("node1 saw no exceptions", null, node1Exception);
+      assertEquals("node2 saw no exceptions", null, node2Exception);
+
+      // let the final PFER propagate
+      sleep(100);
+
+      long txTimestamp = System.currentTimeMillis();
+      String msg1 = "Correct node1 value";
+      String msg2 = "Correct node2 value";
+      Object expected1 = null;
+      Object expected2 = null;
+      if (isUsingInvalidation()) {
+         // PFER does not generate any invalidation, so each node should
+         // succeed. We count on database locking and Hibernate removing
+         // the collection on any update to prevent the situation we have
+         // here where the caches have inconsistent data
+         expected1 = VALUE2;
+         expected2 = VALUE1;
+      } else {
+         // the initial VALUE2 should prevent the node2 put
+         expected1 = VALUE2;
+         expected2 = VALUE2;
+      }
+
+      assertEquals(msg1, expected1, localAccessStrategy.get(KEY, txTimestamp));
+      assertEquals(msg2, expected2, remoteAccessStrategy.get(KEY, txTimestamp));
+   }
+
+   /**
+    * Test method for {@link TransactionalAccess#remove(java.lang.Object)}.
+    */
+   public void testRemove() {
+      evictOrRemoveTest(false);
+   }
+
+   /**
+    * Test method for {@link TransactionalAccess#removeAll()}.
+    */
+   public void testRemoveAll() {
+      evictOrRemoveAllTest(false);
+   }
+
+   /**
+    * Test method for {@link TransactionalAccess#evict(java.lang.Object)}.
+    * 
+    * FIXME add testing of the "immediately without regard for transaction isolation" bit in the
+    * CollectionRegionAccessStrategy API.
+    */
+   public void testEvict() {
+      evictOrRemoveTest(true);
+   }
+
+   /**
+    * Test method for {@link TransactionalAccess#evictAll()}.
+    * 
+    * FIXME add testing of the "immediately without regard for transaction isolation" bit in the
+    * CollectionRegionAccessStrategy API.
+    */
+   public void testEvictAll() {
+      evictOrRemoveAllTest(true);
+   }
+
+   private void evictOrRemoveTest(boolean evict) {
+
+      final String KEY = KEY_BASE + testCount++;
+
+      assertNull("local is clean", localAccessStrategy.get(KEY, System.currentTimeMillis()));
+      assertNull("remote is clean", remoteAccessStrategy.get(KEY, System.currentTimeMillis()));
+
+      localAccessStrategy.putFromLoad(KEY, VALUE1, System.currentTimeMillis(), new Integer(1));
+      assertEquals(VALUE1, localAccessStrategy.get(KEY, System.currentTimeMillis()));
+      remoteAccessStrategy.putFromLoad(KEY, VALUE1, System.currentTimeMillis(), new Integer(1));
+      assertEquals(VALUE1, remoteAccessStrategy.get(KEY, System.currentTimeMillis()));
+
+      // Wait for async propagation
+      sleep(250);
+
+      if (evict)
+         localAccessStrategy.evict(KEY);
+      else
+         localAccessStrategy.remove(KEY);
+
+      assertEquals(null, localAccessStrategy.get(KEY, System.currentTimeMillis()));
+
+      assertEquals(null, remoteAccessStrategy.get(KEY, System.currentTimeMillis()));
+   }
+
+   private void evictOrRemoveAllTest(boolean evict) {
+
+      final String KEY = KEY_BASE + testCount++;
+
+      // Fqn regionFqn = getRegionFqn(REGION_NAME, REGION_PREFIX);
+      //
+      // Node regionRoot = localCache.getRoot().getChild(regionFqn);
+      // assertFalse(regionRoot == null);
+      // assertEquals(0, getValidChildrenCount(regionRoot));
+      // assertTrue(regionRoot.isResident());
+      assertEquals(0, localCache.keySet().size());
+
+      // regionRoot = remoteCache.getRoot().getChild(regionFqn);
+      // assertFalse(regionRoot == null);
+      // assertEquals(0, getValidChildrenCount(regionRoot));
+      // assertTrue(regionRoot.isResident());
+      assertEquals(0, remoteCache.keySet().size());
+
+      assertNull("local is clean", localAccessStrategy.get(KEY, System.currentTimeMillis()));
+      assertNull("remote is clean", remoteAccessStrategy.get(KEY, System.currentTimeMillis()));
+
+      localAccessStrategy.putFromLoad(KEY, VALUE1, System.currentTimeMillis(), new Integer(1));
+      assertEquals(VALUE1, localAccessStrategy.get(KEY, System.currentTimeMillis()));
+      remoteAccessStrategy.putFromLoad(KEY, VALUE1, System.currentTimeMillis(), new Integer(1));
+      assertEquals(VALUE1, remoteAccessStrategy.get(KEY, System.currentTimeMillis()));
+
+      // Wait for async propagation
+      sleep(250);
+
+      if (evict)
+         localAccessStrategy.evictAll();
+      else
+         localAccessStrategy.removeAll();
+
+      // This should re-establish the region root node
+      assertNull(localAccessStrategy.get(KEY, System.currentTimeMillis()));
+
+      // regionRoot = localCache.getRoot().getChild(regionFqn);
+      // assertFalse(regionRoot == null);
+      // assertEquals(0, getValidChildrenCount(regionRoot));
+      // assertTrue(regionRoot.isValid());
+      // assertTrue(regionRoot.isResident());
+      assertEquals(0, localCache.keySet().size());
+
+      // Re-establishing the region root on the local node doesn't
+      // propagate it to other nodes. Do a get on the remote node to re-establish
+      assertEquals(null, remoteAccessStrategy.get(KEY, System.currentTimeMillis()));
+
+      // regionRoot = remoteCache.getRoot().getChild(regionFqn);
+      // assertFalse(regionRoot == null);
+      // assertTrue(regionRoot.isValid());
+      // assertTrue(regionRoot.isResident());
+      //        
+      // assertEquals(0, getValidChildrenCount(regionRoot));
+      // Not invalidation, so we didn't insert a child above
+      assertEquals(0, remoteCache.keySet().size());
+
+      // Test whether the get above messes up the optimistic version
+      remoteAccessStrategy.putFromLoad(KEY, VALUE1, System.currentTimeMillis(), new Integer(1));
+      assertEquals(VALUE1, remoteAccessStrategy.get(KEY, System.currentTimeMillis()));
+
+      // regionRoot = remoteCache.getRoot().getChild(regionFqn);
+      // assertFalse(regionRoot == null);
+      // assertTrue(regionRoot.isValid());
+      // assertTrue(regionRoot.isResident());
+      // // Region root should have 1 child -- the one we added above
+      // assertEquals(1, getValidChildrenCount(regionRoot));
+      // Revalidate the region root
+      assertEquals(1, remoteCache.keySet().size());
+
+      // Wait for async propagation of the putFromLoad
+      sleep(250);
+
+      assertEquals("local is correct", (isUsingInvalidation() ? null : VALUE1), localAccessStrategy.get(KEY, System
+               .currentTimeMillis()));
+      assertEquals("remote is correct", VALUE1, remoteAccessStrategy.get(KEY, System.currentTimeMillis()));
+   }
+
+   private void rollback() {
+      try {
+         BatchModeTransactionManager.getInstance().rollback();
+      } catch (Exception e) {
+         log.error(e.getMessage(), e);
+      }
+
+   }
+
+   private static class AccessStrategyTestSetup extends TestSetup {
+
+      private static final String PREFER_IPV4STACK = "java.net.preferIPv4Stack";
+
+      private final String configResource;
+      private final String configName;
+      private String preferIPv4Stack;
+
+      public AccessStrategyTestSetup(Test test, String configName) {
+         this(test, configName, null);
+      }
+
+      public AccessStrategyTestSetup(Test test, String configName, String configResource) {
+         super(test);
+         this.configName = configName;
+         this.configResource = configResource;
+      }
+
+      @Override
+      protected void setUp() throws Exception {
+         super.setUp();
+
+         // Try to ensure we use IPv4; otherwise cluster formation is very slow
+         preferIPv4Stack = System.getProperty(PREFER_IPV4STACK);
+         System.setProperty(PREFER_IPV4STACK, "true");
+
+         localCfg = createConfiguration(configName, configResource);
+         localRegionFactory = CacheTestUtil.startRegionFactory(localCfg);
+
+         remoteCfg = createConfiguration(configName, configResource);
+         remoteRegionFactory = CacheTestUtil.startRegionFactory(remoteCfg);
+      }
+
+      @Override
+      protected void tearDown() throws Exception {
+         try {
+            super.tearDown();
+         } finally {
+            if (preferIPv4Stack == null)
+               System.clearProperty(PREFER_IPV4STACK);
+            else
+               System.setProperty(PREFER_IPV4STACK, preferIPv4Stack);
+         }
+
+         if (localRegionFactory != null)
+            localRegionFactory.stop();
+
+         if (remoteRegionFactory != null)
+            remoteRegionFactory.stop();
+      }
+
+   }
+
+}

Added: core/trunk/cache-infinispan/src/test/java/org/hibernate/test/cache/infinispan/collection/AbstractReadOnlyAccessTestCase.java
===================================================================
--- core/trunk/cache-infinispan/src/test/java/org/hibernate/test/cache/infinispan/collection/AbstractReadOnlyAccessTestCase.java	                        (rev 0)
+++ core/trunk/cache-infinispan/src/test/java/org/hibernate/test/cache/infinispan/collection/AbstractReadOnlyAccessTestCase.java	2009-08-21 08:39:33 UTC (rev 17386)
@@ -0,0 +1,44 @@
+/*
+ * Hibernate, Relational Persistence for Idiomatic Java
+ *
+ * Copyright (c) 2007, Red Hat, Inc. and/or it's affiliates or third-party contributors as
+ * indicated by the @author tags or express copyright attribution
+ * statements applied by the authors.  All third-party contributions are
+ * distributed under license by Red Hat, Inc. and/or it's affiliates.
+ *
+ * This copyrighted material is made available to anyone wishing to use, modify,
+ * copy, or redistribute it subject to the terms and conditions of the GNU
+ * Lesser General Public License, as published by the Free Software Foundation.
+ *
+ * This program 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 distribution; if not, write to:
+ * Free Software Foundation, Inc.
+ * 51 Franklin Street, Fifth Floor
+ * Boston, MA  02110-1301  USA
+ */
+package org.hibernate.test.cache.infinispan.collection;
+
+import org.hibernate.cache.access.AccessType;
+
+/**
+ * Base class for tests of TRANSACTIONAL access.
+ * 
+ * @author <a href="brian.stansberry at jboss.com">Brian Stansberry</a>
+ */
+public abstract class AbstractReadOnlyAccessTestCase extends AbstractCollectionRegionAccessStrategyTestCase {
+
+    public AbstractReadOnlyAccessTestCase(String name) {
+        super(name);
+    }
+
+    @Override
+    protected AccessType getAccessType() {
+        return AccessType.READ_ONLY;
+    }
+
+}

Added: core/trunk/cache-infinispan/src/test/java/org/hibernate/test/cache/infinispan/collection/AbstractTransactionalAccessTestCase.java
===================================================================
--- core/trunk/cache-infinispan/src/test/java/org/hibernate/test/cache/infinispan/collection/AbstractTransactionalAccessTestCase.java	                        (rev 0)
+++ core/trunk/cache-infinispan/src/test/java/org/hibernate/test/cache/infinispan/collection/AbstractTransactionalAccessTestCase.java	2009-08-21 08:39:33 UTC (rev 17386)
@@ -0,0 +1,48 @@
+/*
+ * Hibernate, Relational Persistence for Idiomatic Java
+ *
+ * Copyright (c) 2007, Red Hat, Inc. and/or it's affiliates or third-party contributors as
+ * indicated by the @author tags or express copyright attribution
+ * statements applied by the authors.  All third-party contributions are
+ * distributed under license by Red Hat, Inc. and/or it's affiliates.
+ *
+ * This copyrighted material is made available to anyone wishing to use, modify,
+ * copy, or redistribute it subject to the terms and conditions of the GNU
+ * Lesser General Public License, as published by the Free Software Foundation.
+ *
+ * This program 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 distribution; if not, write to:
+ * Free Software Foundation, Inc.
+ * 51 Franklin Street, Fifth Floor
+ * Boston, MA  02110-1301  USA
+ */
+package org.hibernate.test.cache.infinispan.collection;
+
+import org.hibernate.cache.access.AccessType;
+
+/**
+ * Base class for tests of TRANSACTIONAL access.
+ * 
+ * @author <a href="brian.stansberry at jboss.com">Brian Stansberry</a>
+ */
+public abstract class AbstractTransactionalAccessTestCase extends AbstractCollectionRegionAccessStrategyTestCase {
+
+    /**
+     * Create a new AbstractTransactionalAccessTestCase.
+     * 
+     */
+    public AbstractTransactionalAccessTestCase(String name) {
+        super(name);
+    }
+
+    @Override
+    protected AccessType getAccessType() {
+        return AccessType.TRANSACTIONAL;
+    }
+
+}

Added: core/trunk/cache-infinispan/src/test/java/org/hibernate/test/cache/infinispan/collection/InvalidatedTransactionalTestCase.java
===================================================================
--- core/trunk/cache-infinispan/src/test/java/org/hibernate/test/cache/infinispan/collection/InvalidatedTransactionalTestCase.java	                        (rev 0)
+++ core/trunk/cache-infinispan/src/test/java/org/hibernate/test/cache/infinispan/collection/InvalidatedTransactionalTestCase.java	2009-08-21 08:39:33 UTC (rev 17386)
@@ -0,0 +1,51 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2009, Red Hat, Inc. and/or it's affiliates, 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.hibernate.test.cache.infinispan.collection;
+
+import org.hibernate.test.cache.infinispan.util.CacheTestUtil;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+/**
+ * InvalidatedTransactionalTestCase.
+ * 
+ * @author Galder Zamarreño
+ * @since 3.5
+ */
+public class InvalidatedTransactionalTestCase extends AbstractTransactionalAccessTestCase {
+
+   public InvalidatedTransactionalTestCase(String name) {
+      super(name);
+   }
+
+   @Override
+   public void testCacheConfiguration() {
+      assertTrue("Using Invalidation", isUsingInvalidation());
+      assertTrue("Synchronous mode", isSynchronous());
+   }
+
+   public static Test suite() throws Exception {
+      TestSuite suite = CacheTestUtil.createFailureExpectedSuite(InvalidatedTransactionalTestCase.class);
+      return getTestSetup(suite, "entity");
+  }
+}

Added: core/trunk/cache-infinispan/src/test/java/org/hibernate/test/cache/infinispan/collection/ReadOnlyExtraAPITestCase.java
===================================================================
--- core/trunk/cache-infinispan/src/test/java/org/hibernate/test/cache/infinispan/collection/ReadOnlyExtraAPITestCase.java	                        (rev 0)
+++ core/trunk/cache-infinispan/src/test/java/org/hibernate/test/cache/infinispan/collection/ReadOnlyExtraAPITestCase.java	2009-08-21 08:39:33 UTC (rev 17386)
@@ -0,0 +1,80 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2009, Red Hat, Inc. and/or it's affiliates, 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.hibernate.test.cache.infinispan.collection;
+
+import org.hibernate.cache.access.AccessType;
+import org.hibernate.cache.access.CollectionRegionAccessStrategy;
+
+/**
+ * ReadOnlyExtraAPITestCase.
+ * 
+ * @author Galder Zamarreño
+ * @since 3.5
+ */
+public class ReadOnlyExtraAPITestCase extends TransactionalExtraAPITestCase {
+
+   public ReadOnlyExtraAPITestCase(String name) {
+      super(name);
+   }
+
+   private static CollectionRegionAccessStrategy localAccessStrategy;
+   
+   @Override
+   protected AccessType getAccessType() {
+       return AccessType.READ_ONLY;
+   }
+   
+   @Override
+   protected CollectionRegionAccessStrategy getCollectionAccessStrategy() {
+       return localAccessStrategy;
+   }
+   
+   @Override
+   protected void setCollectionAccessStrategy(CollectionRegionAccessStrategy strategy) {
+       localAccessStrategy = strategy;
+   }
+   
+   /**
+    * Test method for {@link TransactionalAccess#lockItem(java.lang.Object, java.lang.Object)}.
+    */
+   @Override
+   public void testLockItem() {
+       try {
+           getCollectionAccessStrategy().lockItem(KEY, new Integer(1));
+           fail("Call to lockItem did not throw exception");
+       }
+       catch (UnsupportedOperationException expected) {}
+   }
+
+   /**
+    * Test method for {@link TransactionalAccess#lockRegion()}.
+    */
+   @Override
+   public void testLockRegion() {
+       try {
+           getCollectionAccessStrategy().lockRegion();
+           fail("Call to lockRegion did not throw exception");
+       }
+       catch (UnsupportedOperationException expected) {}
+   }
+
+}

Added: core/trunk/cache-infinispan/src/test/java/org/hibernate/test/cache/infinispan/collection/ReadOnlyTestCase.java
===================================================================
--- core/trunk/cache-infinispan/src/test/java/org/hibernate/test/cache/infinispan/collection/ReadOnlyTestCase.java	                        (rev 0)
+++ core/trunk/cache-infinispan/src/test/java/org/hibernate/test/cache/infinispan/collection/ReadOnlyTestCase.java	2009-08-21 08:39:33 UTC (rev 17386)
@@ -0,0 +1,51 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2009, Red Hat, Inc. and/or it's affiliates, 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.hibernate.test.cache.infinispan.collection;
+
+import org.hibernate.test.cache.infinispan.util.CacheTestUtil;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+/**
+ * Tests READ_ONLY access when invalidation is used.
+ * 
+ * @author Galder Zamarreño
+ * @since 3.5
+ */
+public class ReadOnlyTestCase extends AbstractReadOnlyAccessTestCase {
+
+   public ReadOnlyTestCase(String name) {
+      super(name);
+   }
+
+   public static Test suite() throws Exception {
+      TestSuite suite = CacheTestUtil.createFailureExpectedSuite(ReadOnlyTestCase.class);
+      return getTestSetup(suite, "entity");
+  }
+
+  @Override
+  public void testCacheConfiguration() {
+      assertTrue("Using Invalidation", isUsingInvalidation());
+  }
+
+}

Added: core/trunk/cache-infinispan/src/test/java/org/hibernate/test/cache/infinispan/collection/TransactionalExtraAPITestCase.java
===================================================================
--- core/trunk/cache-infinispan/src/test/java/org/hibernate/test/cache/infinispan/collection/TransactionalExtraAPITestCase.java	                        (rev 0)
+++ core/trunk/cache-infinispan/src/test/java/org/hibernate/test/cache/infinispan/collection/TransactionalExtraAPITestCase.java	2009-08-21 08:39:33 UTC (rev 17386)
@@ -0,0 +1,125 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2009, Red Hat, Inc. and/or it's affiliates, 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.hibernate.test.cache.infinispan.collection;
+
+import org.hibernate.cache.CollectionRegion;
+import org.hibernate.cache.access.AccessType;
+import org.hibernate.cache.access.CollectionRegionAccessStrategy;
+import org.hibernate.cache.access.SoftLock;
+import org.hibernate.cache.infinispan.InfinispanRegionFactory;
+import org.hibernate.cfg.Configuration;
+import org.hibernate.test.cache.infinispan.AbstractNonFunctionalTestCase;
+import org.hibernate.test.cache.infinispan.util.CacheTestUtil;
+
+/**
+ * TransactionalExtraAPITestCase.
+ * 
+ * @author Galder Zamarreño
+ * @since 3.5
+ */
+public class TransactionalExtraAPITestCase extends AbstractNonFunctionalTestCase {
+
+   public static final String REGION_NAME = "test/com.foo.test";
+   public static final String KEY = "KEY";
+   public static final String VALUE1 = "VALUE1";
+   public static final String VALUE2 = "VALUE2";
+   
+   private static CollectionRegionAccessStrategy localAccessStrategy;
+   
+   public TransactionalExtraAPITestCase(String name) {
+      super(name);
+   }
+
+   protected void setUp() throws Exception {
+       super.setUp();
+       
+       if (getCollectionAccessStrategy() == null) {
+           Configuration cfg = createConfiguration();
+           InfinispanRegionFactory rf  = CacheTestUtil.startRegionFactory(cfg, getCacheTestSupport());
+           
+           // Sleep a bit to avoid concurrent FLUSH problem
+           avoidConcurrentFlush();
+           
+           CollectionRegion localCollectionRegion = rf.buildCollectionRegion(REGION_NAME, cfg.getProperties(), null);
+           setCollectionAccessStrategy(localCollectionRegion.buildAccessStrategy(getAccessType()));
+       }
+   }
+
+   protected void tearDown() throws Exception {
+       
+       super.tearDown();
+   }
+   
+   protected Configuration createConfiguration() {
+       Configuration cfg = CacheTestUtil.buildConfiguration(REGION_PREFIX, InfinispanRegionFactory.class, true, false);
+       cfg.setProperty(InfinispanRegionFactory.ENTITY_CACHE_RESOURCE_PROP, getCacheConfigName());
+       return cfg;
+   }
+   
+   protected String getCacheConfigName() {
+       return "entity";
+   }
+   
+   protected AccessType getAccessType() {
+       return AccessType.TRANSACTIONAL;
+   }
+   
+   protected CollectionRegionAccessStrategy getCollectionAccessStrategy() {
+       return localAccessStrategy;
+   }
+   
+   protected void setCollectionAccessStrategy(CollectionRegionAccessStrategy strategy) {
+       localAccessStrategy = strategy;
+   }
+
+   /**
+    * Test method for {@link TransactionalAccess#lockItem(java.lang.Object, java.lang.Object)}.
+    */
+   public void testLockItem() {
+       assertNull(getCollectionAccessStrategy().lockItem(KEY, new Integer(1)));
+   }
+
+   /**
+    * Test method for {@link TransactionalAccess#lockRegion()}.
+    */
+   public void testLockRegion() {
+       assertNull(getCollectionAccessStrategy().lockRegion());
+   }
+
+   /**
+    * Test method for {@link TransactionalAccess#unlockItem(java.lang.Object, org.hibernate.cache.access.SoftLock)}.
+    */
+   public void testUnlockItem() {
+       getCollectionAccessStrategy().unlockItem(KEY, new MockSoftLock());
+   }
+
+   /**
+    * Test method for {@link TransactionalAccess#unlockRegion(org.hibernate.cache.access.SoftLock)}.
+    */
+   public void testUnlockRegion() {
+       getCollectionAccessStrategy().unlockItem(KEY, new MockSoftLock());
+   }
+   
+   public static class MockSoftLock implements SoftLock {
+       
+   }
+}

Added: core/trunk/cache-infinispan/src/test/java/org/hibernate/test/cache/infinispan/entity/AbstractEntityRegionAccessStrategyTestCase.java
===================================================================
--- core/trunk/cache-infinispan/src/test/java/org/hibernate/test/cache/infinispan/entity/AbstractEntityRegionAccessStrategyTestCase.java	                        (rev 0)
+++ core/trunk/cache-infinispan/src/test/java/org/hibernate/test/cache/infinispan/entity/AbstractEntityRegionAccessStrategyTestCase.java	2009-08-21 08:39:33 UTC (rev 17386)
@@ -0,0 +1,743 @@
+/*
+ * Hibernate, Relational Persistence for Idiomatic Java
+ *
+ * Copyright (c) 2007, Red Hat, Inc. and/or it's affiliates or third-party contributors as
+ * indicated by the @author tags or express copyright attribution
+ * statements applied by the authors.  All third-party contributions are
+ * distributed under license by Red Hat, Inc. and/or it's affiliates.
+ *
+ * This copyrighted material is made available to anyone wishing to use, modify,
+ * copy, or redistribute it subject to the terms and conditions of the GNU
+ * Lesser General Public License, as published by the Free Software Foundation.
+ *
+ * This program 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 distribution; if not, write to:
+ * Free Software Foundation, Inc.
+ * 51 Franklin Street, Fifth Floor
+ * Boston, MA  02110-1301  USA
+ */
+package org.hibernate.test.cache.infinispan.entity;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+import junit.extensions.TestSetup;
+import junit.framework.AssertionFailedError;
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+import org.hibernate.cache.CacheDataDescription;
+import org.hibernate.cache.EntityRegion;
+import org.hibernate.cache.access.AccessType;
+import org.hibernate.cache.access.EntityRegionAccessStrategy;
+import org.hibernate.cache.impl.CacheDataDescriptionImpl;
+import org.hibernate.cache.infinispan.InfinispanRegionFactory;
+import org.hibernate.cache.infinispan.impl.BaseRegion;
+import org.hibernate.cache.infinispan.util.CacheHelper;
+import org.hibernate.cfg.Configuration;
+import org.hibernate.test.cache.infinispan.AbstractNonFunctionalTestCase;
+import org.hibernate.test.cache.infinispan.util.CacheTestUtil;
+import org.hibernate.util.ComparableComparator;
+import org.infinispan.Cache;
+import org.infinispan.context.Flag;
+import org.infinispan.transaction.tm.BatchModeTransactionManager;
+
+/**
+ * Base class for tests of EntityRegionAccessStrategy impls.
+ * 
+ * @author Galder Zamarreño
+ * @since 3.5
+ */
+public abstract class AbstractEntityRegionAccessStrategyTestCase extends AbstractNonFunctionalTestCase {
+
+   public static final String REGION_NAME = "test/com.foo.test";
+   public static final String KEY_BASE = "KEY";
+   public static final String VALUE1 = "VALUE1";
+   public static final String VALUE2 = "VALUE2";
+
+   protected static int testCount;
+
+   protected static Configuration localCfg;
+   protected static InfinispanRegionFactory localRegionFactory;
+   protected Cache localCache;
+   protected static Configuration remoteCfg;
+   protected static InfinispanRegionFactory remoteRegionFactory;
+   protected Cache remoteCache;
+
+   protected boolean invalidation;
+   protected boolean synchronous;
+
+   protected EntityRegion localEntityRegion;
+   protected EntityRegionAccessStrategy localAccessStrategy;
+
+   protected EntityRegion remoteEntityRegion;
+   protected EntityRegionAccessStrategy remoteAccessStrategy;
+
+   protected Exception node1Exception;
+   protected Exception node2Exception;
+
+   protected AssertionFailedError node1Failure;
+   protected AssertionFailedError node2Failure;
+
+   public static Test getTestSetup(Class testClass, String configName) {
+      TestSuite suite = new TestSuite(testClass);
+      return new AccessStrategyTestSetup(suite, configName);
+   }
+
+   public static Test getTestSetup(Test test, String configName) {
+      return new AccessStrategyTestSetup(test, configName);
+   }
+
+   /**
+    * Create a new TransactionalAccessTestCase.
+    * 
+    * @param name
+    */
+   public AbstractEntityRegionAccessStrategyTestCase(String name) {
+      super(name);
+   }
+
+   protected abstract AccessType getAccessType();
+
+   protected void setUp() throws Exception {
+      super.setUp();
+
+      // Sleep a bit to avoid concurrent FLUSH problem
+      avoidConcurrentFlush();
+
+      localEntityRegion = localRegionFactory.buildEntityRegion(REGION_NAME, localCfg
+               .getProperties(), getCacheDataDescription());
+      localAccessStrategy = localEntityRegion.buildAccessStrategy(getAccessType());
+
+      localCache = ((BaseRegion) localEntityRegion).getCache();
+
+      invalidation = CacheHelper.isClusteredInvalidation(localCache);
+      synchronous = CacheHelper.isSynchronous(localCache);
+
+      // Sleep a bit to avoid concurrent FLUSH problem
+      avoidConcurrentFlush();
+
+      remoteEntityRegion = remoteRegionFactory.buildEntityRegion(REGION_NAME, remoteCfg
+               .getProperties(), getCacheDataDescription());
+      remoteAccessStrategy = remoteEntityRegion.buildAccessStrategy(getAccessType());
+
+      remoteCache = ((BaseRegion) remoteEntityRegion).getCache();
+
+      node1Exception = null;
+      node2Exception = null;
+
+      node1Failure = null;
+      node2Failure = null;
+   }
+
+   protected void tearDown() throws Exception {
+
+      super.tearDown();
+
+      if (localEntityRegion != null)
+         localEntityRegion.destroy();
+      if (remoteEntityRegion != null)
+         remoteEntityRegion.destroy();
+
+      try {
+         localCache.getAdvancedCache().clear(Flag.CACHE_MODE_LOCAL);
+      } catch (Exception e) {
+         log.error("Problem purging local cache", e);
+      }
+
+      try {
+         remoteCache.getAdvancedCache().clear(Flag.CACHE_MODE_LOCAL);
+      } catch (Exception e) {
+         log.error("Problem purging remote cache", e);
+      }
+
+      node1Exception = null;
+      node2Exception = null;
+
+      node1Failure = null;
+      node2Failure = null;
+   }
+
+   protected static Configuration createConfiguration(String configName) {
+      Configuration cfg = CacheTestUtil.buildConfiguration(REGION_PREFIX, InfinispanRegionFactory.class, true, false);
+      cfg.setProperty(InfinispanRegionFactory.ENTITY_CACHE_RESOURCE_PROP, configName);
+      return cfg;
+   }
+
+   protected CacheDataDescription getCacheDataDescription() {
+      return new CacheDataDescriptionImpl(true, true, ComparableComparator.INSTANCE);
+   }
+
+   protected boolean isUsingInvalidation() {
+      return invalidation;
+   }
+
+   protected boolean isSynchronous() {
+      return synchronous;
+   }
+
+   protected void assertThreadsRanCleanly() {
+      if (node1Failure != null)
+         throw node1Failure;
+      if (node2Failure != null)
+         throw node2Failure;
+
+      if (node1Exception != null) {
+         log.error("node1 saw an exception", node1Exception);
+         assertEquals("node1 saw no exceptions", null, node1Exception);
+      }
+
+      if (node2Exception != null) {
+         log.error("node2 saw an exception", node2Exception);
+         assertEquals("node2 saw no exceptions", null, node2Exception);
+      }
+   }
+
+   /**
+    * This is just a setup test where we assert that the cache config is as we expected.
+    */
+   public abstract void testCacheConfiguration();
+
+   /**
+    * Test method for {@link TransactionalAccess#getRegion()}.
+    */
+   public void testGetRegion() {
+      assertEquals("Correct region", localEntityRegion, localAccessStrategy.getRegion());
+   }
+
+   /**
+    * Test method for
+    * {@link TransactionalAccess#putFromLoad(java.lang.Object, java.lang.Object, long, java.lang.Object)}
+    * .
+    */
+   public void testPutFromLoad() throws Exception {
+      putFromLoadTest(false);
+   }
+
+   /**
+    * Test method for
+    * {@link TransactionalAccess#putFromLoad(java.lang.Object, java.lang.Object, long, java.lang.Object, boolean)}
+    * .
+    */
+   public void testPutFromLoadMinimal() throws Exception {
+      putFromLoadTest(true);
+   }
+
+   /**
+    * Simulate 2 nodes, both start, tx do a get, experience a cache miss, then 'read from db.' First
+    * does a putFromLoad, then an update. Second tries to do a putFromLoad with stale data (i.e. it
+    * took longer to read from the db). Both commit their tx. Then both start a new tx and get.
+    * First should see the updated data; second should either see the updated data (isInvalidation()
+    * == false) or null (isInvalidation() == true).
+    * 
+    * @param useMinimalAPI
+    * @throws Exception
+    */
+   private void putFromLoadTest(final boolean useMinimalAPI) throws Exception {
+
+      final String KEY = KEY_BASE + testCount++;
+
+      final CountDownLatch writeLatch1 = new CountDownLatch(1);
+      final CountDownLatch writeLatch2 = new CountDownLatch(1);
+      final CountDownLatch completionLatch = new CountDownLatch(2);
+
+      Thread node1 = new Thread() {
+
+         public void run() {
+
+            try {
+               long txTimestamp = System.currentTimeMillis();
+               BatchModeTransactionManager.getInstance().begin();
+
+               assertNull("node1 starts clean", localAccessStrategy.get(KEY, txTimestamp));
+
+               writeLatch1.await();
+
+               if (useMinimalAPI) {
+                  localAccessStrategy.putFromLoad(KEY, VALUE1, txTimestamp, new Integer(1), true);
+               } else {
+                  localAccessStrategy.putFromLoad(KEY, VALUE1, txTimestamp, new Integer(1));
+               }
+
+               localAccessStrategy.update(KEY, VALUE2, new Integer(2), new Integer(1));
+
+               BatchModeTransactionManager.getInstance().commit();
+            } catch (Exception e) {
+               log.error("node1 caught exception", e);
+               node1Exception = e;
+               rollback();
+            } catch (AssertionFailedError e) {
+               node1Failure = e;
+               rollback();
+            } finally {
+               // Let node2 write
+               writeLatch2.countDown();
+               completionLatch.countDown();
+            }
+         }
+      };
+
+      Thread node2 = new Thread() {
+
+         public void run() {
+
+            try {
+               long txTimestamp = System.currentTimeMillis();
+               BatchModeTransactionManager.getInstance().begin();
+
+               assertNull("node1 starts clean", remoteAccessStrategy.get(KEY, txTimestamp));
+
+               // Let node1 write
+               writeLatch1.countDown();
+               // Wait for node1 to finish
+               writeLatch2.await();
+
+               if (useMinimalAPI) {
+                  remoteAccessStrategy.putFromLoad(KEY, VALUE1, txTimestamp, new Integer(1), true);
+               } else {
+                  remoteAccessStrategy.putFromLoad(KEY, VALUE1, txTimestamp, new Integer(1));
+               }
+
+               BatchModeTransactionManager.getInstance().commit();
+            } catch (Exception e) {
+               log.error("node2 caught exception", e);
+               node2Exception = e;
+               rollback();
+            } catch (AssertionFailedError e) {
+               node2Failure = e;
+               rollback();
+            } finally {
+               completionLatch.countDown();
+            }
+         }
+      };
+
+      node1.setDaemon(true);
+      node2.setDaemon(true);
+
+      node1.start();
+      node2.start();
+
+      assertTrue("Threads completed", completionLatch.await(2, TimeUnit.SECONDS));
+
+      assertThreadsRanCleanly();
+
+      long txTimestamp = System.currentTimeMillis();
+      assertEquals("Correct node1 value", VALUE2, localAccessStrategy.get(KEY, txTimestamp));
+
+      if (isUsingInvalidation()) {
+         // no data version to prevent the PFER; we count on db locks preventing this
+         assertEquals("Expected node2 value", VALUE1, remoteAccessStrategy.get(KEY, txTimestamp));
+      } else {
+         // The node1 update is replicated, preventing the node2 PFER
+         assertEquals("Correct node2 value", VALUE2, remoteAccessStrategy.get(KEY, txTimestamp));
+      }
+   }
+
+   /**
+    * Test method for
+    * {@link TransactionalAccess#insert(java.lang.Object, java.lang.Object, java.lang.Object)}.
+    */
+   public void testInsert() throws Exception {
+
+      final String KEY = KEY_BASE + testCount++;
+
+      final CountDownLatch readLatch = new CountDownLatch(1);
+      final CountDownLatch commitLatch = new CountDownLatch(1);
+      final CountDownLatch completionLatch = new CountDownLatch(2);
+
+      Thread inserter = new Thread() {
+
+         public void run() {
+
+            try {
+               long txTimestamp = System.currentTimeMillis();
+               BatchModeTransactionManager.getInstance().begin();
+
+               assertNull("Correct initial value", localAccessStrategy.get(KEY, txTimestamp));
+
+               localAccessStrategy.insert(KEY, VALUE1, new Integer(1));
+
+               readLatch.countDown();
+               commitLatch.await();
+
+               BatchModeTransactionManager.getInstance().commit();
+            } catch (Exception e) {
+               log.error("node1 caught exception", e);
+               node1Exception = e;
+               rollback();
+            } catch (AssertionFailedError e) {
+               node1Failure = e;
+               rollback();
+            } finally {
+               completionLatch.countDown();
+            }
+         }
+      };
+
+      Thread reader = new Thread() {
+
+         public void run() {
+
+            try {
+               long txTimestamp = System.currentTimeMillis();
+               BatchModeTransactionManager.getInstance().begin();
+
+               readLatch.await();
+//               Object expected = !isBlockingReads() ? null : VALUE1;
+               Object expected = null;
+
+               assertEquals("Correct initial value", expected, localAccessStrategy.get(KEY,
+                        txTimestamp));
+
+               BatchModeTransactionManager.getInstance().commit();
+            } catch (Exception e) {
+               log.error("node1 caught exception", e);
+               node1Exception = e;
+               rollback();
+            } catch (AssertionFailedError e) {
+               node1Failure = e;
+               rollback();
+            } finally {
+               commitLatch.countDown();
+               completionLatch.countDown();
+            }
+         }
+      };
+
+      inserter.setDaemon(true);
+      reader.setDaemon(true);
+      inserter.start();
+      reader.start();
+
+//      if (!isBlockingReads())
+         assertTrue("Threads completed", completionLatch.await(1, TimeUnit.SECONDS));
+//      else {
+//         // Reader should be blocking for lock
+//         assertFalse("Threads completed", completionLatch.await(250, TimeUnit.MILLISECONDS));
+//         commitLatch.countDown();
+//         assertTrue("Threads completed", completionLatch.await(1, TimeUnit.SECONDS));
+//      }
+
+      assertThreadsRanCleanly();
+
+      long txTimestamp = System.currentTimeMillis();
+      assertEquals("Correct node1 value", VALUE1, localAccessStrategy.get(KEY, txTimestamp));
+      Object expected = isUsingInvalidation() ? null : VALUE1;
+      assertEquals("Correct node2 value", expected, remoteAccessStrategy.get(KEY, txTimestamp));
+   }
+
+   /**
+    * Test method for
+    * {@link TransactionalAccess#update(java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object)}
+    * .
+    */
+   public void testUpdate() throws Exception {
+
+      final String KEY = KEY_BASE + testCount++;
+
+      // Set up initial state
+      localAccessStrategy.get(KEY, System.currentTimeMillis());
+      localAccessStrategy.putFromLoad(KEY, VALUE1, System.currentTimeMillis(), new Integer(1));
+      remoteAccessStrategy.get(KEY, System.currentTimeMillis());
+      remoteAccessStrategy.putFromLoad(KEY, VALUE1, System.currentTimeMillis(), new Integer(1));
+
+      // Let the async put propagate
+      sleep(250);
+
+      final CountDownLatch readLatch = new CountDownLatch(1);
+      final CountDownLatch commitLatch = new CountDownLatch(1);
+      final CountDownLatch completionLatch = new CountDownLatch(2);
+
+      Thread updater = new Thread("testUpdate-updater") {
+
+         public void run() {
+            boolean readerUnlocked = false;
+            try {
+               long txTimestamp = System.currentTimeMillis();
+               BatchModeTransactionManager.getInstance().begin();
+               log.debug("Transaction began, get initial value");
+               assertEquals("Correct initial value", VALUE1, localAccessStrategy.get(KEY, txTimestamp));
+               log.debug("Now update value");
+               localAccessStrategy.update(KEY, VALUE2, new Integer(2), new Integer(1));
+               log.debug("Notify the read latch");
+               readLatch.countDown();
+               readerUnlocked = true;
+               log.debug("Await commit");
+               commitLatch.await();
+               BatchModeTransactionManager.getInstance().commit();
+            } catch (Exception e) {
+               log.error("node1 caught exception", e);
+               node1Exception = e;
+               rollback();
+            } catch (AssertionFailedError e) {
+               node1Failure = e;
+               rollback();
+            } finally {
+               if (!readerUnlocked) readLatch.countDown();
+               log.debug("Completion latch countdown");
+               completionLatch.countDown();
+            }
+         }
+      };
+
+      Thread reader = new Thread("testUpdate-reader") {
+
+         public void run() {
+            try {
+               long txTimestamp = System.currentTimeMillis();
+               BatchModeTransactionManager.getInstance().begin();
+               log.debug("Transaction began, await read latch");
+               readLatch.await();
+               log.debug("Read latch acquired, verify local access strategy");
+
+               // This will block w/ pessimistic locking and then
+               // read the new value; w/ optimistic it shouldn't
+               // block and will read the old value
+//               Object expected = !isBlockingReads() ? VALUE1 : VALUE2;
+               Object expected = VALUE1;
+               assertEquals("Correct value", expected, localAccessStrategy.get(KEY, txTimestamp));
+
+               BatchModeTransactionManager.getInstance().commit();
+            } catch (Exception e) {
+               log.error("node1 caught exception", e);
+               node1Exception = e;
+               rollback();
+            } catch (AssertionFailedError e) {
+               node1Failure = e;
+               rollback();
+            } finally {
+               commitLatch.countDown();
+               log.debug("Completion latch countdown");
+               completionLatch.countDown();
+            }
+         }
+      };
+
+      updater.setDaemon(true);
+      reader.setDaemon(true);
+      updater.start();
+      reader.start();
+
+//      if (!isBlockingReads())
+         // Should complete promptly
+         assertTrue(completionLatch.await(2, TimeUnit.SECONDS));
+//      else {
+//         // Reader thread should be blocking
+//         assertFalse(completionLatch.await(250, TimeUnit.MILLISECONDS));
+//         // Let the writer commit down
+//         commitLatch.countDown();
+//         assertTrue(completionLatch.await(1, TimeUnit.SECONDS));
+//      }
+
+      assertThreadsRanCleanly();
+
+      long txTimestamp = System.currentTimeMillis();
+      assertEquals("Correct node1 value", VALUE2, localAccessStrategy.get(KEY, txTimestamp));
+      Object expected = isUsingInvalidation() ? null : VALUE2;
+      assertEquals("Correct node2 value", expected, remoteAccessStrategy.get(KEY, txTimestamp));
+   }
+
+   /**
+    * Test method for {@link TransactionalAccess#remove(java.lang.Object)}.
+    */
+   public void testRemove() {
+      evictOrRemoveTest(false);
+   }
+
+   /**
+    * Test method for {@link TransactionalAccess#removeAll()}.
+    */
+   public void testRemoveAll() {
+      evictOrRemoveAllTest(false);
+   }
+
+   /**
+    * Test method for {@link TransactionalAccess#evict(java.lang.Object)}.
+    * 
+    * FIXME add testing of the "immediately without regard for transaction isolation" bit in the
+    * EntityRegionAccessStrategy API.
+    */
+   public void testEvict() {
+      evictOrRemoveTest(true);
+   }
+
+   /**
+    * Test method for {@link TransactionalAccess#evictAll()}.
+    * 
+    * FIXME add testing of the "immediately without regard for transaction isolation" bit in the
+    * EntityRegionAccessStrategy API.
+    */
+   public void testEvictAll() {
+      evictOrRemoveAllTest(true);
+   }
+
+   private void evictOrRemoveTest(boolean evict) {
+
+      final String KEY = KEY_BASE + testCount++;
+
+      assertNull("local is clean", localAccessStrategy.get(KEY, System.currentTimeMillis()));
+      assertNull("remote is clean", remoteAccessStrategy.get(KEY, System.currentTimeMillis()));
+
+      localAccessStrategy.putFromLoad(KEY, VALUE1, System.currentTimeMillis(), new Integer(1));
+      assertEquals(VALUE1, localAccessStrategy.get(KEY, System.currentTimeMillis()));
+      remoteAccessStrategy.putFromLoad(KEY, VALUE1, System.currentTimeMillis(), new Integer(1));
+      assertEquals(VALUE1, remoteAccessStrategy.get(KEY, System.currentTimeMillis()));
+
+      // Wait for async propagation
+      sleep(250);
+
+      if (evict)
+         localAccessStrategy.evict(KEY);
+      else
+         localAccessStrategy.remove(KEY);
+
+      assertEquals(null, localAccessStrategy.get(KEY, System.currentTimeMillis()));
+
+      // sleep(1000);
+
+      assertEquals(null, remoteAccessStrategy.get(KEY, System.currentTimeMillis()));
+   }
+
+   private void evictOrRemoveAllTest(boolean evict) {
+
+      final String KEY = KEY_BASE + testCount++;
+
+//      Fqn regionFqn = getRegionFqn(REGION_NAME, REGION_PREFIX);
+//
+//      Node regionRoot = localCache.getRoot().getChild(regionFqn);
+//      assertFalse(regionRoot == null);
+      assertEquals(0, localCache.keySet().size());
+//      assertTrue(regionRoot.isResident());
+
+//      regionRoot = remoteCache.getRoot().getChild(regionFqn);
+//      assertFalse(regionRoot == null);
+      assertEquals(0, remoteCache.keySet().size());
+//      assertTrue(regionRoot.isResident());
+
+      assertNull("local is clean", localAccessStrategy.get(KEY, System.currentTimeMillis()));
+      assertNull("remote is clean", remoteAccessStrategy.get(KEY, System.currentTimeMillis()));
+
+      localAccessStrategy.putFromLoad(KEY, VALUE1, System.currentTimeMillis(), new Integer(1));
+      assertEquals(VALUE1, localAccessStrategy.get(KEY, System.currentTimeMillis()));
+
+      // Wait for async propagation
+      sleep(250);
+
+      remoteAccessStrategy.putFromLoad(KEY, VALUE1, System.currentTimeMillis(), new Integer(1));
+      assertEquals(VALUE1, remoteAccessStrategy.get(KEY, System.currentTimeMillis()));
+
+      // Wait for async propagation
+      sleep(250);
+
+      if (evict)
+         localAccessStrategy.evictAll();
+      else
+         localAccessStrategy.removeAll();
+
+      // This should re-establish the region root node in the optimistic case
+      assertNull(localAccessStrategy.get(KEY, System.currentTimeMillis()));
+
+//      regionRoot = localCache.getRoot().getChild(regionFqn);
+//      assertFalse(regionRoot == null);
+//      assertEquals(0, getValidChildrenCount(regionRoot));
+//      assertTrue(regionRoot.isValid());
+//      assertTrue(regionRoot.isResident());
+      assertEquals(0, localCache.keySet().size());
+
+      // Re-establishing the region root on the local node doesn't
+      // propagate it to other nodes. Do a get on the remote node to re-establish
+      assertEquals(null, remoteAccessStrategy.get(KEY, System.currentTimeMillis()));
+
+//      regionRoot = remoteCache.getRoot().getChild(regionFqn);
+//      assertFalse(regionRoot == null);
+//      assertTrue(regionRoot.isValid());
+//      assertTrue(regionRoot.isResident());
+//      // Not invalidation, so we didn't insert a child above
+//      assertEquals(0, getValidChildrenCount(regionRoot));
+      assertEquals(0, remoteCache.keySet().size());
+
+      // Test whether the get above messes up the optimistic version
+      remoteAccessStrategy.putFromLoad(KEY, VALUE1, System.currentTimeMillis(), new Integer(1));
+      assertEquals(VALUE1, remoteAccessStrategy.get(KEY, System.currentTimeMillis()));
+
+      // Revalidate the region root
+//      regionRoot = remoteCache.getRoot().getChild(regionFqn);
+//      assertFalse(regionRoot == null);
+//      assertTrue(regionRoot.isValid());
+//      assertTrue(regionRoot.isResident());
+//      // Region root should have 1 child -- the one we added above
+//      assertEquals(1, getValidChildrenCount(regionRoot));
+      assertEquals(1, remoteCache.keySet().size());
+
+      // Wait for async propagation
+      sleep(250);
+
+      assertEquals("local is correct", (isUsingInvalidation() ? null : VALUE1), localAccessStrategy
+               .get(KEY, System.currentTimeMillis()));
+      assertEquals("remote is correct", VALUE1, remoteAccessStrategy.get(KEY, System
+               .currentTimeMillis()));
+   }
+
+   protected void rollback() {
+      try {
+         BatchModeTransactionManager.getInstance().rollback();
+      } catch (Exception e) {
+         log.error(e.getMessage(), e);
+      }
+   }
+
+   private static class AccessStrategyTestSetup extends TestSetup {
+
+      private static final String PREFER_IPV4STACK = "java.net.preferIPv4Stack";
+      private final String configName;
+      private String preferIPv4Stack;
+
+      public AccessStrategyTestSetup(Test test, String configName) {
+         super(test);
+         this.configName = configName;
+      }
+
+      @Override
+      protected void setUp() throws Exception {
+         try {
+            super.tearDown();
+         } finally {
+            if (preferIPv4Stack == null)
+               System.clearProperty(PREFER_IPV4STACK);
+            else
+               System.setProperty(PREFER_IPV4STACK, preferIPv4Stack);
+         }
+
+         // Try to ensure we use IPv4; otherwise cluster formation is very slow
+         preferIPv4Stack = System.getProperty(PREFER_IPV4STACK);
+         System.setProperty(PREFER_IPV4STACK, "true");
+
+         localCfg = createConfiguration(configName);
+         localRegionFactory = CacheTestUtil.startRegionFactory(localCfg);
+//         localCache = localRegionFactory.getCacheManager().getCache("entity");
+
+         remoteCfg = createConfiguration(configName);
+         remoteRegionFactory = CacheTestUtil.startRegionFactory(remoteCfg);
+//         remoteCache = remoteRegionFactory.getCacheManager().getCache("entity");
+      }
+
+      @Override
+      protected void tearDown() throws Exception {
+         super.tearDown();
+
+         if (localRegionFactory != null)
+            localRegionFactory.stop();
+
+         if (remoteRegionFactory != null)
+            remoteRegionFactory.stop();
+      }
+
+   }
+
+}

Added: core/trunk/cache-infinispan/src/test/java/org/hibernate/test/cache/infinispan/entity/AbstractReadOnlyAccessTestCase.java
===================================================================
--- core/trunk/cache-infinispan/src/test/java/org/hibernate/test/cache/infinispan/entity/AbstractReadOnlyAccessTestCase.java	                        (rev 0)
+++ core/trunk/cache-infinispan/src/test/java/org/hibernate/test/cache/infinispan/entity/AbstractReadOnlyAccessTestCase.java	2009-08-21 08:39:33 UTC (rev 17386)
@@ -0,0 +1,93 @@
+/*
+ * Hibernate, Relational Persistence for Idiomatic Java
+ *
+ * Copyright (c) 2007, Red Hat, Inc. and/or it's affiliates or third-party contributors as
+ * indicated by the @author tags or express copyright attribution
+ * statements applied by the authors.  All third-party contributions are
+ * distributed under license by Red Hat, Inc. and/or it's affiliates.
+ *
+ * This copyrighted material is made available to anyone wishing to use, modify,
+ * copy, or redistribute it subject to the terms and conditions of the GNU
+ * Lesser General Public License, as published by the Free Software Foundation.
+ *
+ * This program 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 distribution; if not, write to:
+ * Free Software Foundation, Inc.
+ * 51 Franklin Street, Fifth Floor
+ * Boston, MA  02110-1301  USA
+ */
+package org.hibernate.test.cache.infinispan.entity;
+
+import org.hibernate.cache.access.AccessType;
+import org.infinispan.transaction.tm.BatchModeTransactionManager;
+
+/**
+ * Base class for tests of TRANSACTIONAL access.
+ * 
+ * @author Galder Zamarreño
+ * @since 3.5
+ */
+public abstract class AbstractReadOnlyAccessTestCase extends AbstractEntityRegionAccessStrategyTestCase {
+
+    /**
+     * Create a new AbstractTransactionalAccessTestCase.
+     * 
+     */
+    public AbstractReadOnlyAccessTestCase(String name) {
+        super(name);
+    }
+
+    @Override
+    protected AccessType getAccessType() {
+        return AccessType.READ_ONLY;
+    }   
+
+    @Override
+    public void testPutFromLoad() throws Exception {
+        putFromLoadTest(false);
+    }
+
+    @Override
+    public void testPutFromLoadMinimal() throws Exception {
+        putFromLoadTest(true);
+    }
+    
+    private void putFromLoadTest(boolean minimal) throws Exception {
+       
+        final String KEY = KEY_BASE + testCount++;
+        
+        long txTimestamp = System.currentTimeMillis();
+        BatchModeTransactionManager.getInstance().begin();
+        assertNull(localAccessStrategy.get(KEY, System.currentTimeMillis()));
+        if (minimal)
+            localAccessStrategy.putFromLoad(KEY, VALUE1, txTimestamp, new Integer(1), true);
+        else
+            localAccessStrategy.putFromLoad(KEY, VALUE1, txTimestamp, new Integer(1));
+        
+        sleep(250);
+        Object expected = isUsingInvalidation() ? null : VALUE1;
+        assertEquals(expected, remoteAccessStrategy.get(KEY, System.currentTimeMillis()));
+        
+        BatchModeTransactionManager.getInstance().commit();
+        assertEquals(VALUE1, localAccessStrategy.get(KEY, System.currentTimeMillis()));
+        assertEquals(expected, remoteAccessStrategy.get(KEY, System.currentTimeMillis()));
+    }
+
+    @Override
+    public void testUpdate() throws Exception {
+       
+        final String KEY = KEY_BASE + testCount++;
+        
+        try {
+            localAccessStrategy.update(KEY, VALUE2, new Integer(2), new Integer(1));
+            fail("Call to update did not throw exception");
+        }
+        catch (UnsupportedOperationException good) {}
+    }
+
+}

Added: core/trunk/cache-infinispan/src/test/java/org/hibernate/test/cache/infinispan/entity/AbstractTransactionalAccessTestCase.java
===================================================================
--- core/trunk/cache-infinispan/src/test/java/org/hibernate/test/cache/infinispan/entity/AbstractTransactionalAccessTestCase.java	                        (rev 0)
+++ core/trunk/cache-infinispan/src/test/java/org/hibernate/test/cache/infinispan/entity/AbstractTransactionalAccessTestCase.java	2009-08-21 08:39:33 UTC (rev 17386)
@@ -0,0 +1,132 @@
+/*
+ * Hibernate, Relational Persistence for Idiomatic Java
+ *
+ * Copyright (c) 2007, Red Hat, Inc. and/or it's affiliates or third-party contributors as
+ * indicated by the @author tags or express copyright attribution
+ * statements applied by the authors.  All third-party contributions are
+ * distributed under license by Red Hat, Inc. and/or it's affiliates.
+ *
+ * This copyrighted material is made available to anyone wishing to use, modify,
+ * copy, or redistribute it subject to the terms and conditions of the GNU
+ * Lesser General Public License, as published by the Free Software Foundation.
+ *
+ * This program 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 distribution; if not, write to:
+ * Free Software Foundation, Inc.
+ * 51 Franklin Street, Fifth Floor
+ * Boston, MA  02110-1301  USA
+ */
+package org.hibernate.test.cache.infinispan.entity;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+import junit.framework.AssertionFailedError;
+
+import org.hibernate.cache.access.AccessType;
+import org.infinispan.transaction.tm.BatchModeTransactionManager;
+
+/**
+ * Base class for tests of TRANSACTIONAL access.
+ * 
+ * @author Galder Zamarreño
+ * @since 3.5
+ */
+public abstract class AbstractTransactionalAccessTestCase extends AbstractEntityRegionAccessStrategyTestCase {
+
+   public AbstractTransactionalAccessTestCase(String name) {
+      super(name);
+   }
+
+   @Override
+   protected AccessType getAccessType() {
+      return AccessType.TRANSACTIONAL;
+   }
+
+   public void testContestedPutFromLoad() throws Exception {
+
+      final String KEY = KEY_BASE + testCount++;
+
+      localAccessStrategy.get(KEY, System.currentTimeMillis());
+      localAccessStrategy.putFromLoad(KEY, VALUE1, System.currentTimeMillis(), new Integer(1));
+
+      final CountDownLatch pferLatch = new CountDownLatch(1);
+      final CountDownLatch pferCompletionLatch = new CountDownLatch(1);
+      final CountDownLatch commitLatch = new CountDownLatch(1);
+      final CountDownLatch completionLatch = new CountDownLatch(1);
+
+      Thread blocker = new Thread("Blocker") {
+
+         public void run() {
+
+            try {
+               long txTimestamp = System.currentTimeMillis();
+               BatchModeTransactionManager.getInstance().begin();
+
+               assertEquals("Correct initial value", VALUE1, localAccessStrategy.get(KEY,
+                        txTimestamp));
+
+               localAccessStrategy.update(KEY, VALUE2, new Integer(2), new Integer(1));
+
+               pferLatch.countDown();
+               commitLatch.await();
+
+               BatchModeTransactionManager.getInstance().commit();
+            } catch (Exception e) {
+               log.error("node1 caught exception", e);
+               node1Exception = e;
+               rollback();
+            } catch (AssertionFailedError e) {
+               node1Failure = e;
+               rollback();
+            } finally {
+               completionLatch.countDown();
+            }
+         }
+      };
+
+      Thread putter = new Thread("Putter") {
+
+         public void run() {
+
+            try {
+               long txTimestamp = System.currentTimeMillis();
+               BatchModeTransactionManager.getInstance().begin();
+
+               localAccessStrategy.putFromLoad(KEY, VALUE1, txTimestamp, new Integer(1));
+
+               BatchModeTransactionManager.getInstance().commit();
+            } catch (Exception e) {
+               log.error("node1 caught exception", e);
+               node1Exception = e;
+               rollback();
+            } catch (AssertionFailedError e) {
+               node1Failure = e;
+               rollback();
+            } finally {
+               pferCompletionLatch.countDown();
+            }
+         }
+      };
+
+      blocker.start();
+      assertTrue("Active tx has done an update", pferLatch.await(1, TimeUnit.SECONDS));
+      putter.start();
+      assertTrue("putFromLoadreturns promtly", pferCompletionLatch.await(10, TimeUnit.MILLISECONDS));
+
+      commitLatch.countDown();
+
+      assertTrue("Threads completed", completionLatch.await(1, TimeUnit.SECONDS));
+
+      assertThreadsRanCleanly();
+
+      long txTimestamp = System.currentTimeMillis();
+      assertEquals("Correct node1 value", VALUE2, localAccessStrategy.get(KEY, txTimestamp));
+   }
+
+}

Added: core/trunk/cache-infinispan/src/test/java/org/hibernate/test/cache/infinispan/entity/EntityRegionImplTestCase.java
===================================================================
--- core/trunk/cache-infinispan/src/test/java/org/hibernate/test/cache/infinispan/entity/EntityRegionImplTestCase.java	                        (rev 0)
+++ core/trunk/cache-infinispan/src/test/java/org/hibernate/test/cache/infinispan/entity/EntityRegionImplTestCase.java	2009-08-21 08:39:33 UTC (rev 17386)
@@ -0,0 +1,101 @@
+/*
+ * Hibernate, Relational Persistence for Idiomatic Java
+ *
+ * Copyright (c) 2007, Red Hat, Inc. and/or it's affiliates or third-party contributors as
+ * indicated by the @author tags or express copyright attribution
+ * statements applied by the authors.  All third-party contributions are
+ * distributed under license by Red Hat, Inc. and/or it's affiliates.
+ *
+ * This copyrighted material is made available to anyone wishing to use, modify,
+ * copy, or redistribute it subject to the terms and conditions of the GNU
+ * Lesser General Public License, as published by the Free Software Foundation.
+ *
+ * This program 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 distribution; if not, write to:
+ * Free Software Foundation, Inc.
+ * 51 Franklin Street, Fifth Floor
+ * Boston, MA  02110-1301  USA
+ */
+package org.hibernate.test.cache.infinispan.entity;
+
+import java.util.Properties;
+
+import org.hibernate.cache.CacheDataDescription;
+import org.hibernate.cache.CacheException;
+import org.hibernate.cache.EntityRegion;
+import org.hibernate.cache.Region;
+import org.hibernate.cache.RegionFactory;
+import org.hibernate.cache.access.AccessType;
+import org.hibernate.cache.infinispan.InfinispanRegionFactory;
+import org.hibernate.test.cache.infinispan.AbstractEntityCollectionRegionTestCase;
+import org.infinispan.Cache;
+
+/**
+ * Tests of EntityRegionImpl.
+ * 
+ * @author Galder Zamarreño
+ * @since 3.5
+ */
+public class EntityRegionImplTestCase extends AbstractEntityCollectionRegionTestCase {
+    
+    public EntityRegionImplTestCase(String name) {
+        super(name);
+    } 
+    
+    @Override
+    protected void supportedAccessTypeTest(RegionFactory regionFactory, Properties properties) {
+        
+        EntityRegion region = regionFactory.buildEntityRegion("test", properties, null);
+        
+        assertNull("Got TRANSACTIONAL", region.buildAccessStrategy(AccessType.TRANSACTIONAL).lockRegion());
+        
+        try
+        {
+            region.buildAccessStrategy(AccessType.READ_ONLY).lockRegion();
+            fail("Did not get READ_ONLY");
+        }
+        catch (UnsupportedOperationException good) {}
+        
+        try
+        {
+            region.buildAccessStrategy(AccessType.NONSTRICT_READ_WRITE);
+            fail("Incorrectly got NONSTRICT_READ_WRITE");
+        }
+        catch (CacheException good) {}
+        
+        try
+        {
+            region.buildAccessStrategy(AccessType.READ_WRITE);
+            fail("Incorrectly got READ_WRITE");
+        }
+        catch (CacheException good) {}      
+        
+    }
+
+    @Override
+    protected void putInRegion(Region region, Object key, Object value) {
+        ((EntityRegion) region).buildAccessStrategy(AccessType.TRANSACTIONAL).insert(key, value, new Integer(1));
+    }
+
+    @Override
+    protected void removeFromRegion(Region region, Object key) {
+        ((EntityRegion) region).buildAccessStrategy(AccessType.TRANSACTIONAL).remove(key);
+    }
+
+   @Override
+   protected Region createRegion(InfinispanRegionFactory regionFactory, String regionName,
+            Properties properties, CacheDataDescription cdd) {
+      return regionFactory.buildEntityRegion(regionName, properties, cdd);
+   }
+
+   @Override
+   protected Cache getInfinispanCache(InfinispanRegionFactory regionFactory) {
+      return regionFactory.getCacheManager().getCache("entity");
+   }
+
+}

Added: core/trunk/cache-infinispan/src/test/java/org/hibernate/test/cache/infinispan/entity/InvalidatedTransactionalTestCase.java
===================================================================
--- core/trunk/cache-infinispan/src/test/java/org/hibernate/test/cache/infinispan/entity/InvalidatedTransactionalTestCase.java	                        (rev 0)
+++ core/trunk/cache-infinispan/src/test/java/org/hibernate/test/cache/infinispan/entity/InvalidatedTransactionalTestCase.java	2009-08-21 08:39:33 UTC (rev 17386)
@@ -0,0 +1,52 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2009, Red Hat, Inc. and/or it's affiliates, 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.hibernate.test.cache.infinispan.entity;
+
+import org.hibernate.test.cache.infinispan.util.CacheTestUtil;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+/**
+ * InvalidatedTransactionalTestCase.
+ * 
+ * @author Galder Zamarreño
+ * @since 3.5
+ */
+public class InvalidatedTransactionalTestCase extends AbstractTransactionalAccessTestCase {
+
+   public InvalidatedTransactionalTestCase(String name) {
+      super(name);
+   }
+
+   @Override
+   public void testCacheConfiguration() {
+      assertTrue("Using Invalidation", isUsingInvalidation());
+      assertTrue("Synchronous mode", isSynchronous());
+   }
+
+   public static Test suite() throws Exception {
+      TestSuite suite = CacheTestUtil.createFailureExpectedSuite(InvalidatedTransactionalTestCase.class);
+      return getTestSetup(suite, "entity");
+  }
+
+}

Added: core/trunk/cache-infinispan/src/test/java/org/hibernate/test/cache/infinispan/entity/ReadOnlyExtraAPITestCase.java
===================================================================
--- core/trunk/cache-infinispan/src/test/java/org/hibernate/test/cache/infinispan/entity/ReadOnlyExtraAPITestCase.java	                        (rev 0)
+++ core/trunk/cache-infinispan/src/test/java/org/hibernate/test/cache/infinispan/entity/ReadOnlyExtraAPITestCase.java	2009-08-21 08:39:33 UTC (rev 17386)
@@ -0,0 +1,95 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2009, Red Hat, Inc. and/or it's affiliates, 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.hibernate.test.cache.infinispan.entity;
+
+import org.hibernate.cache.access.AccessType;
+import org.hibernate.cache.access.EntityRegionAccessStrategy;
+
+/**
+ * Tests for the "extra API" in EntityRegionAccessStrategy; 
+ * <p>
+ * By "extra API" we mean those methods that are superfluous to the 
+ * function of the Infinispan integration, where the impl is a no-op or a static
+ * false return value, UnsupportedOperationException, etc.
+ * 
+ * @author Galder Zamarreño
+ * @since 3.5
+ */
+public class ReadOnlyExtraAPITestCase extends TransactionalExtraAPITestCase {
+   private static EntityRegionAccessStrategy localAccessStrategy;
+   
+   public ReadOnlyExtraAPITestCase(String name) {
+      super(name);
+   }
+
+   @Override
+   protected AccessType getAccessType() {
+       return AccessType.READ_ONLY;
+   }
+   
+   @Override
+   protected EntityRegionAccessStrategy getEntityAccessStrategy() {
+       return localAccessStrategy;
+   }
+   
+   @Override
+   protected void setEntityRegionAccessStrategy(EntityRegionAccessStrategy strategy) {
+       localAccessStrategy = strategy;
+   }
+   
+   /**
+    * Test method for {@link TransactionalAccess#lockItem(java.lang.Object, java.lang.Object)}.
+    */
+   @Override
+   public void testLockItem() {
+       try {
+           getEntityAccessStrategy().lockItem(KEY, new Integer(1));
+           fail("Call to lockItem did not throw exception");
+       }
+       catch (UnsupportedOperationException expected) {}
+   }
+
+   /**
+    * Test method for {@link TransactionalAccess#lockRegion()}.
+    */
+   @Override
+   public void testLockRegion() {
+       try {
+           getEntityAccessStrategy().lockRegion();
+           fail("Call to lockRegion did not throw exception");
+       }
+       catch (UnsupportedOperationException expected) {}
+   }
+
+   /**
+    * Test method for {@link TransactionalAccess#afterUpdate(java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, org.hibernate.cache.access.SoftLock)}.
+    */
+   @Override
+   public void testAfterUpdate() {
+       try {
+           getEntityAccessStrategy().afterUpdate(KEY, VALUE2, new Integer(1), new Integer(2), new MockSoftLock());
+           fail("Call to afterUpdate did not throw exception");
+       }
+       catch (UnsupportedOperationException expected) {}
+   }
+
+}

Added: core/trunk/cache-infinispan/src/test/java/org/hibernate/test/cache/infinispan/entity/ReadOnlyTestCase.java
===================================================================
--- core/trunk/cache-infinispan/src/test/java/org/hibernate/test/cache/infinispan/entity/ReadOnlyTestCase.java	                        (rev 0)
+++ core/trunk/cache-infinispan/src/test/java/org/hibernate/test/cache/infinispan/entity/ReadOnlyTestCase.java	2009-08-21 08:39:33 UTC (rev 17386)
@@ -0,0 +1,62 @@
+/*
+ * Hibernate, Relational Persistence for Idiomatic Java
+ *
+ * Copyright (c) 2007, Red Hat, Inc. and/or it's affiliates or third-party contributors as
+ * indicated by the @author tags or express copyright attribution
+ * statements applied by the authors.  All third-party contributions are
+ * distributed under license by Red Hat, Inc. and/or it's affiliates.
+ *
+ * This copyrighted material is made available to anyone wishing to use, modify,
+ * copy, or redistribute it subject to the terms and conditions of the GNU
+ * Lesser General Public License, as published by the Free Software Foundation.
+ *
+ * This program 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 distribution; if not, write to:
+ * Free Software Foundation, Inc.
+ * 51 Franklin Street, Fifth Floor
+ * Boston, MA  02110-1301  USA
+ */
+package org.hibernate.test.cache.infinispan.entity;
+
+import org.hibernate.test.cache.infinispan.util.CacheTestUtil;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+/**
+ * Tests READ_ONLY access when pessimistic locking and invalidation are used.
+ * 
+ * @author Galder Zamarreño
+ * @since 3.5
+ */
+public class ReadOnlyTestCase extends AbstractReadOnlyAccessTestCase {
+
+   /**
+    * Create a new PessimisticTransactionalAccessTestCase.
+    * 
+    * @param name
+    */
+   public ReadOnlyTestCase(String name) {
+      super(name);
+   }
+
+   public static Test suite() throws Exception {
+      TestSuite suite = CacheTestUtil.createFailureExpectedSuite(ReadOnlyTestCase.class);
+      return getTestSetup(suite, "entity");
+   }
+
+   // Known failures
+
+   // Overrides
+
+   @Override
+   public void testCacheConfiguration() {
+      assertTrue("Using Invalidation", isUsingInvalidation());
+   }
+
+}

Added: core/trunk/cache-infinispan/src/test/java/org/hibernate/test/cache/infinispan/entity/TransactionalExtraAPITestCase.java
===================================================================
--- core/trunk/cache-infinispan/src/test/java/org/hibernate/test/cache/infinispan/entity/TransactionalExtraAPITestCase.java	                        (rev 0)
+++ core/trunk/cache-infinispan/src/test/java/org/hibernate/test/cache/infinispan/entity/TransactionalExtraAPITestCase.java	2009-08-21 08:39:33 UTC (rev 17386)
@@ -0,0 +1,147 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2009, Red Hat, Inc. and/or it's affiliates, 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.hibernate.test.cache.infinispan.entity;
+
+import org.hibernate.cache.EntityRegion;
+import org.hibernate.cache.access.AccessType;
+import org.hibernate.cache.access.EntityRegionAccessStrategy;
+import org.hibernate.cache.access.SoftLock;
+import org.hibernate.cache.infinispan.InfinispanRegionFactory;
+import org.hibernate.cfg.Configuration;
+import org.hibernate.test.cache.infinispan.AbstractNonFunctionalTestCase;
+import org.hibernate.test.cache.infinispan.util.CacheTestUtil;
+
+/**
+ * Tests for the "extra API" in EntityRegionAccessStrategy;.
+ * <p>
+ * By "extra API" we mean those methods that are superfluous to the 
+ * function of the JBC integration, where the impl is a no-op or a static
+ * false return value, UnsupportedOperationException, etc.
+ * 
+ * @author Galder Zamarreño
+ * @since 3.5
+ */
+public class TransactionalExtraAPITestCase extends AbstractNonFunctionalTestCase {
+
+   public TransactionalExtraAPITestCase(String name) {
+      super(name);
+   }
+   
+   public static final String REGION_NAME = "test/com.foo.test";
+   public static final String KEY = "KEY";
+   public static final String VALUE1 = "VALUE1";
+   public static final String VALUE2 = "VALUE2";
+   
+   private static EntityRegionAccessStrategy localAccessStrategy;
+   
+   private static boolean optimistic;
+   
+   protected void setUp() throws Exception {
+       super.setUp();
+       
+       if (getEntityAccessStrategy() == null) {
+           Configuration cfg = createConfiguration();
+           InfinispanRegionFactory rf  = CacheTestUtil.startRegionFactory(cfg, getCacheTestSupport());
+           
+           // Sleep a bit to avoid concurrent FLUSH problem
+           avoidConcurrentFlush();
+           
+           EntityRegion localEntityRegion = rf.buildEntityRegion(REGION_NAME, cfg.getProperties(), null);
+           setEntityRegionAccessStrategy(localEntityRegion.buildAccessStrategy(getAccessType()));
+       }
+   }
+
+   protected void tearDown() throws Exception {
+       
+       super.tearDown();
+   }
+   
+   protected Configuration createConfiguration() {
+       Configuration cfg = CacheTestUtil.buildConfiguration(REGION_PREFIX, InfinispanRegionFactory.class, true, false);
+       cfg.setProperty(InfinispanRegionFactory.ENTITY_CACHE_RESOURCE_PROP, getCacheConfigName());
+       return cfg;
+   }
+   
+   protected String getCacheConfigName() {
+       return "entity";
+   }
+   
+   protected AccessType getAccessType() {
+       return AccessType.TRANSACTIONAL;
+   }
+   
+   protected EntityRegionAccessStrategy getEntityAccessStrategy() {
+       return localAccessStrategy;
+   }
+   
+   protected void setEntityRegionAccessStrategy(EntityRegionAccessStrategy strategy) {
+       localAccessStrategy = strategy;
+   }
+
+   /**
+    * Test method for {@link TransactionalAccess#lockItem(java.lang.Object, java.lang.Object)}.
+    */
+   public void testLockItem() {
+       assertNull(getEntityAccessStrategy().lockItem(KEY, new Integer(1)));
+   }
+
+   /**
+    * Test method for {@link TransactionalAccess#lockRegion()}.
+    */
+   public void testLockRegion() {
+       assertNull(getEntityAccessStrategy().lockRegion());
+   }
+
+   /**
+    * Test method for {@link TransactionalAccess#unlockItem(java.lang.Object, org.hibernate.cache.access.SoftLock)}.
+    */
+   public void testUnlockItem() {
+       getEntityAccessStrategy().unlockItem(KEY, new MockSoftLock());
+   }
+
+   /**
+    * Test method for {@link TransactionalAccess#unlockRegion(org.hibernate.cache.access.SoftLock)}.
+    */
+   public void testUnlockRegion() {
+       getEntityAccessStrategy().unlockItem(KEY, new MockSoftLock());
+   }
+
+   /**
+    * Test method for {@link TransactionalAccess#afterInsert(java.lang.Object, java.lang.Object, java.lang.Object)}.
+    */
+   public void testAfterInsert() {
+       assertFalse("afterInsert always returns false", getEntityAccessStrategy().afterInsert(KEY, VALUE1, new Integer(1)));
+   }
+
+   /**
+    * Test method for {@link TransactionalAccess#afterUpdate(java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, org.hibernate.cache.access.SoftLock)}.
+    */
+   public void testAfterUpdate() {
+       assertFalse("afterInsert always returns false", getEntityAccessStrategy().afterUpdate(KEY, VALUE2, new Integer(1), new Integer(2), new MockSoftLock()));
+   }
+   
+   public static class MockSoftLock implements SoftLock {
+       
+   }
+
+
+}

Added: core/trunk/cache-infinispan/src/test/java/org/hibernate/test/cache/infinispan/functional/AbstractFunctionalTestCase.java
===================================================================
--- core/trunk/cache-infinispan/src/test/java/org/hibernate/test/cache/infinispan/functional/AbstractFunctionalTestCase.java	                        (rev 0)
+++ core/trunk/cache-infinispan/src/test/java/org/hibernate/test/cache/infinispan/functional/AbstractFunctionalTestCase.java	2009-08-21 08:39:33 UTC (rev 17386)
@@ -0,0 +1,39 @@
+package org.hibernate.test.cache.infinispan.functional;
+
+import java.util.Map;
+
+import org.hibernate.junit.functional.FunctionalTestCase;
+import org.hibernate.stat.SecondLevelCacheStatistics;
+import org.hibernate.stat.Statistics;
+
+/**
+ * @author Galder Zamarreño
+ * @since 3.5
+ */
+public abstract class AbstractFunctionalTestCase extends FunctionalTestCase {
+   private final String cacheConcurrencyStrategy;
+
+   public AbstractFunctionalTestCase(String string, String cacheConcurrencyStrategy) {
+      super(string);
+      this.cacheConcurrencyStrategy = cacheConcurrencyStrategy;
+   }
+
+   public String[] getMappings() {
+      return new String[] { "cache/infinispan/functional/Item.hbm.xml" };
+   }
+
+   @Override
+   public String getCacheConcurrencyStrategy() {
+      return cacheConcurrencyStrategy;
+   }
+   
+   public void testEmptySecondLevelCacheEntry() throws Exception {
+//      getSessions().evictEntity(Item.class.getName());
+      getSessions().getCache().evictEntityRegion(Item.class.getName());
+      Statistics stats = getSessions().getStatistics();
+      stats.clear();
+      SecondLevelCacheStatistics statistics = stats.getSecondLevelCacheStatistics(Item.class.getName() + ".items");
+      Map cacheEntries = statistics.getEntries();
+      assertEquals(0, cacheEntries.size());
+  }
+}
\ No newline at end of file

Added: core/trunk/cache-infinispan/src/test/java/org/hibernate/test/cache/infinispan/functional/BasicReadOnlyTestCase.java
===================================================================
--- core/trunk/cache-infinispan/src/test/java/org/hibernate/test/cache/infinispan/functional/BasicReadOnlyTestCase.java	                        (rev 0)
+++ core/trunk/cache-infinispan/src/test/java/org/hibernate/test/cache/infinispan/functional/BasicReadOnlyTestCase.java	2009-08-21 08:39:33 UTC (rev 17386)
@@ -0,0 +1,13 @@
+package org.hibernate.test.cache.infinispan.functional;
+
+/**
+ * @author Galder Zamarreño
+ * @since 3.5
+ */
+public class BasicReadOnlyTestCase extends AbstractFunctionalTestCase {
+
+   public BasicReadOnlyTestCase(String string) {
+      super(string, "read-only");
+   }
+
+}
\ No newline at end of file

Added: core/trunk/cache-infinispan/src/test/java/org/hibernate/test/cache/infinispan/functional/BasicTransactionalTestCase.java
===================================================================
--- core/trunk/cache-infinispan/src/test/java/org/hibernate/test/cache/infinispan/functional/BasicTransactionalTestCase.java	                        (rev 0)
+++ core/trunk/cache-infinispan/src/test/java/org/hibernate/test/cache/infinispan/functional/BasicTransactionalTestCase.java	2009-08-21 08:39:33 UTC (rev 17386)
@@ -0,0 +1,188 @@
+package org.hibernate.test.cache.infinispan.functional;
+
+import java.io.Serializable;
+
+import org.hibernate.Session;
+import org.hibernate.Transaction;
+import org.hibernate.cache.entry.CacheEntry;
+import org.hibernate.stat.SecondLevelCacheStatistics;
+import org.hibernate.stat.Statistics;
+
+/**
+ * @author Galder Zamarreño
+ * @since 3.5
+ */
+public class BasicTransactionalTestCase extends AbstractFunctionalTestCase {
+
+   public BasicTransactionalTestCase(String string) {
+      super(string, "transactional");
+   }
+
+   public void testEntityCache() {
+      Item item = new Item("chris", "Chris's Item");
+
+      Session s = openSession();
+      Statistics stats = s.getSessionFactory().getStatistics();
+      s.getTransaction().begin();
+      s.persist(item);
+      s.getTransaction().commit();
+      s.close();
+
+      s = openSession();
+      Item found = (Item) s.load(Item.class, item.getId());
+      System.out.println(stats);
+      assertEquals(item.getDescription(), found.getDescription());
+      assertEquals(0, stats.getSecondLevelCacheMissCount());
+      assertEquals(1, stats.getSecondLevelCacheHitCount());
+      s.delete(found);
+      s.close();
+   }
+   
+   public void testCollectionCache() {
+      Item item = new Item("chris", "Chris's Item");
+      Item another = new Item("another", "Owned Item");
+      item.addItem(another);
+
+      Session s = openSession();
+      s.getTransaction().begin();
+      s.persist(item);
+      s.persist(another);
+      s.getTransaction().commit();
+      s.close();
+
+      s = openSession();
+      Statistics stats = s.getSessionFactory().getStatistics();
+      Item loaded = (Item) s.load(Item.class, item.getId());
+      assertEquals(1, loaded.getItems().size());
+      s.close();
+
+      s = openSession();
+      SecondLevelCacheStatistics cStats = stats.getSecondLevelCacheStatistics(Item.class.getName() + ".items");
+      Item loadedWithCachedCollection = (Item) s.load(Item.class, item.getId());
+      stats.logSummary();
+      assertEquals(item.getName(), loadedWithCachedCollection.getName());
+      assertEquals(item.getItems().size(), loadedWithCachedCollection.getItems().size());
+      assertEquals(1, cStats.getHitCount());
+      s.close();
+   }
+
+   public void testStaleWritesLeaveCacheConsistent() {
+      Session s = openSession();
+      Transaction txn = s.beginTransaction();
+      VersionedItem item = new VersionedItem();
+      item.setName("steve");
+      item.setDescription("steve's item");
+      s.save(item);
+      txn.commit();
+      s.close();
+
+      Long initialVersion = item.getVersion();
+
+      // manually revert the version property
+      item.setVersion(new Long(item.getVersion().longValue() - 1));
+
+      try {
+          s = openSession();
+          txn = s.beginTransaction();
+          s.update(item);
+          txn.commit();
+          s.close();
+          fail("expected stale write to fail");
+      } catch (Throwable expected) {
+          // expected behavior here
+          if (txn != null) {
+              try {
+                  txn.rollback();
+              } catch (Throwable ignore) {
+              }
+          }
+      } finally {
+          if (s != null && s.isOpen()) {
+              try {
+                  s.close();
+              } catch (Throwable ignore) {
+              }
+          }
+      }
+
+      // check the version value in the cache...
+      SecondLevelCacheStatistics slcs = sfi().getStatistics().getSecondLevelCacheStatistics(VersionedItem.class.getName());
+
+      Object entry = slcs.getEntries().get(item.getId());
+      Long cachedVersionValue;
+      cachedVersionValue = (Long) ((CacheEntry) entry).getVersion();
+      assertEquals(initialVersion.longValue(), cachedVersionValue.longValue());
+
+      // cleanup
+      s = openSession();
+      txn = s.beginTransaction();
+      item = (VersionedItem) s.load(VersionedItem.class, item.getId());
+      s.delete(item);
+      txn.commit();
+      s.close();
+  }
+
+   public void testQueryCacheInvalidation() {
+      Session s = openSession();
+      Transaction t = s.beginTransaction();
+      Item i = new Item();
+      i.setName("widget");
+      i.setDescription("A really top-quality, full-featured widget.");
+      s.persist(i);
+      t.commit();
+      s.close();
+
+      SecondLevelCacheStatistics slcs = s.getSessionFactory().getStatistics().getSecondLevelCacheStatistics(Item.class.getName());
+
+      assertEquals(slcs.getPutCount(), 1);
+      assertEquals(slcs.getElementCountInMemory(), 1);
+      assertEquals(slcs.getEntries().size(), 1);
+
+      s = openSession();
+      t = s.beginTransaction();
+      i = (Item) s.get(Item.class, i.getId());
+
+      assertEquals(slcs.getHitCount(), 1);
+      assertEquals(slcs.getMissCount(), 0);
+
+      i.setDescription("A bog standard item");
+
+      t.commit();
+      s.close();
+
+      assertEquals(slcs.getPutCount(), 2);
+
+      CacheEntry entry = (CacheEntry) slcs.getEntries().get(i.getId());
+      Serializable[] ser = entry.getDisassembledState();
+      assertTrue(ser[0].equals("widget"));
+      assertTrue(ser[1].equals("A bog standard item"));
+      
+      // cleanup
+      s = openSession();
+      t = s.beginTransaction();
+      s.delete(i);
+      t.commit();
+      s.close();
+   }
+   
+   public void testQueryCache() {
+      Item item = new Item("chris", "Chris's Item");
+
+      Session s = openSession();
+      s.getTransaction().begin();
+      s.persist(item);
+      s.getTransaction().commit();
+      s.close();
+
+      s = openSession();
+      s.createQuery("from Item").setCacheable(true).list();
+      s.close();
+
+      s = openSession();
+      Statistics stats = s.getSessionFactory().getStatistics();
+      s.createQuery("from Item").setCacheable(true).list();
+      assertEquals(1, stats.getQueryCacheHitCount());
+      s.createQuery("delete from Item").executeUpdate();
+      s.close();
+   }
+}

Added: core/trunk/cache-infinispan/src/test/java/org/hibernate/test/cache/infinispan/functional/Contact.java
===================================================================
--- core/trunk/cache-infinispan/src/test/java/org/hibernate/test/cache/infinispan/functional/Contact.java	                        (rev 0)
+++ core/trunk/cache-infinispan/src/test/java/org/hibernate/test/cache/infinispan/functional/Contact.java	2009-08-21 08:39:33 UTC (rev 17386)
@@ -0,0 +1,91 @@
+/*
+ * Hibernate, Relational Persistence for Idiomatic Java
+ *
+ * Copyright (c) 2008, Red Hat, Inc. and/or it's affiliates or third-party contributors as
+ * indicated by the @author tags or express copyright attribution
+ * statements applied by the authors.  All third-party contributions are
+ * distributed under license by Red Hat, Inc. and/or it's affiliates.
+ *
+ * This copyrighted material is made available to anyone wishing to use, modify,
+ * copy, or redistribute it subject to the terms and conditions of the GNU
+ * Lesser General Public License, as published by the Free Software Foundation.
+ *
+ * This program 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 distribution; if not, write to:
+ * Free Software Foundation, Inc.
+ * 51 Franklin Street, Fifth Floor
+ * Boston, MA  02110-1301  USA
+ */
+package org.hibernate.test.cache.infinispan.functional;
+
+import java.io.Serializable;
+
+/**
+ * Entity that has a many-to-one relationship to a Customer
+ * 
+ * @author Galder Zamarreño
+ * @since 3.5
+ */
+public class Contact implements Serializable {
+   Integer id;
+   String name;
+   String tlf;
+   Customer customer;
+
+   public Integer getId() {
+      return id;
+   }
+
+   public void setId(Integer id) {
+      this.id = id;
+   }
+
+   public String getName() {
+      return name;
+   }
+
+   public void setName(String name) {
+      this.name = name;
+   }
+
+   public String getTlf() {
+      return tlf;
+   }
+
+   public void setTlf(String tlf) {
+      this.tlf = tlf;
+   }
+
+   public Customer getCustomer() {
+      return customer;
+   }
+
+   public void setCustomer(Customer customer) {
+      this.customer = customer;
+   }
+
+   @Override
+   public boolean equals(Object o) {
+      if (o == this)
+         return true;
+      if (!(o instanceof Contact))
+         return false;
+      Contact c = (Contact) o;
+      return c.id.equals(id) && c.name.equals(name) && c.tlf.equals(tlf);
+   }
+
+   @Override
+   public int hashCode() {
+      int result = 17;
+      result = 31 * result + (id == null ? 0 : id.hashCode());
+      result = 31 * result + name.hashCode();
+      result = 31 * result + tlf.hashCode();
+      return result;
+   }
+
+}


Property changes on: core/trunk/cache-infinispan/src/test/java/org/hibernate/test/cache/infinispan/functional/Contact.java
___________________________________________________________________
Name: svn:executable
   + *

Added: core/trunk/cache-infinispan/src/test/java/org/hibernate/test/cache/infinispan/functional/Customer.java
===================================================================
--- core/trunk/cache-infinispan/src/test/java/org/hibernate/test/cache/infinispan/functional/Customer.java	                        (rev 0)
+++ core/trunk/cache-infinispan/src/test/java/org/hibernate/test/cache/infinispan/functional/Customer.java	2009-08-21 08:39:33 UTC (rev 17386)
@@ -0,0 +1,67 @@
+/*
+ * Hibernate, Relational Persistence for Idiomatic Java
+ *
+ * Copyright (c) 2008, Red Hat, Inc. and/or it's affiliates or third-party contributors as
+ * indicated by the @author tags or express copyright attribution
+ * statements applied by the authors.  All third-party contributions are
+ * distributed under license by Red Hat, Inc. and/or it's affiliates.
+ *
+ * This copyrighted material is made available to anyone wishing to use, modify,
+ * copy, or redistribute it subject to the terms and conditions of the GNU
+ * Lesser General Public License, as published by the Free Software Foundation.
+ *
+ * This program 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 distribution; if not, write to:
+ * Free Software Foundation, Inc.
+ * 51 Franklin Street, Fifth Floor
+ * Boston, MA  02110-1301  USA
+ */
+package org.hibernate.test.cache.infinispan.functional;
+
+import java.io.Serializable;
+import java.util.Set;
+
+/**
+ * Company customer
+ * 
+ * @author Emmanuel Bernard
+ * @author Kabir Khan
+ */
+public class Customer implements Serializable {
+   Integer id;
+   String name;
+
+   private transient Set<Contact> contacts;
+
+   public Customer() {
+   }
+
+   public Integer getId() {
+      return id;
+   }
+
+   public void setId(Integer id) {
+      this.id = id;
+   }
+
+   public String getName() {
+      return name;
+   }
+
+   public void setName(String string) {
+      name = string;
+   }
+
+   public Set<Contact> getContacts() {
+      return contacts;
+   }
+
+   public void setContacts(Set<Contact> contacts) {
+      this.contacts = contacts;
+   }
+}


Property changes on: core/trunk/cache-infinispan/src/test/java/org/hibernate/test/cache/infinispan/functional/Customer.java
___________________________________________________________________
Name: svn:executable
   + *

Added: core/trunk/cache-infinispan/src/test/java/org/hibernate/test/cache/infinispan/functional/Item.java
===================================================================
--- core/trunk/cache-infinispan/src/test/java/org/hibernate/test/cache/infinispan/functional/Item.java	                        (rev 0)
+++ core/trunk/cache-infinispan/src/test/java/org/hibernate/test/cache/infinispan/functional/Item.java	2009-08-21 08:39:33 UTC (rev 17386)
@@ -0,0 +1,90 @@
+/*
+ * Hibernate, Relational Persistence for Idiomatic Java
+ *
+ * Copyright (c) 2007, Red Hat, Inc. and/or it's affiliates or third-party contributors as
+ * indicated by the @author tags or express copyright attribution
+ * statements applied by the authors.  All third-party contributions are
+ * distributed under license by Red Hat, Inc. and/or it's affiliates.
+ *
+ * This copyrighted material is made available to anyone wishing to use, modify,
+ * copy, or redistribute it subject to the terms and conditions of the GNU
+ * Lesser General Public License, as published by the Free Software Foundation.
+ *
+ * This program 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 distribution; if not, write to:
+ * Free Software Foundation, Inc.
+ * 51 Franklin Street, Fifth Floor
+ * Boston, MA  02110-1301  USA
+ */
+package org.hibernate.test.cache.infinispan.functional;
+
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * @author Gavin King
+ */
+public class Item {
+    private Long id;
+    private String name;
+    private String description;
+    private Item owner;
+    private Set<Item> items = new HashSet<Item>();
+
+    public Item() {}
+    
+    public Item( String name, String description ) {
+		this.name = name;
+		this.description = description;
+	}
+
+	public String getDescription() {
+        return description;
+    }
+
+    public void setDescription(String description) {
+        this.description = description;
+    }
+
+    public Long getId() {
+        return id;
+    }
+
+    public void setId(Long id) {
+        this.id = id;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+	public Item getOwner() {
+		return owner;
+	}
+
+	public void setOwner( Item owner ) {
+		this.owner = owner;
+	}
+
+	public Set<Item> getItems() {
+		return items;
+	}
+
+	public void setItems( Set<Item> items ) {
+		this.items = items;
+	}
+	
+	public void addItem( Item item ) {
+		item.setOwner( this );
+		getItems().add( item );
+	}
+}


Property changes on: core/trunk/cache-infinispan/src/test/java/org/hibernate/test/cache/infinispan/functional/Item.java
___________________________________________________________________
Name: svn:executable
   + *

Added: core/trunk/cache-infinispan/src/test/java/org/hibernate/test/cache/infinispan/functional/VersionedItem.java
===================================================================
--- core/trunk/cache-infinispan/src/test/java/org/hibernate/test/cache/infinispan/functional/VersionedItem.java	                        (rev 0)
+++ core/trunk/cache-infinispan/src/test/java/org/hibernate/test/cache/infinispan/functional/VersionedItem.java	2009-08-21 08:39:33 UTC (rev 17386)
@@ -0,0 +1,39 @@
+/*
+ * Hibernate, Relational Persistence for Idiomatic Java
+ *
+ * Copyright (c) 2007, Red Hat, Inc. and/or it's affiliates or third-party contributors as
+ * indicated by the @author tags or express copyright attribution
+ * statements applied by the authors.  All third-party contributions are
+ * distributed under license by Red Hat, Inc. and/or it's affiliates.
+ *
+ * This copyrighted material is made available to anyone wishing to use, modify,
+ * copy, or redistribute it subject to the terms and conditions of the GNU
+ * Lesser General Public License, as published by the Free Software Foundation.
+ *
+ * This program 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 distribution; if not, write to:
+ * Free Software Foundation, Inc.
+ * 51 Franklin Street, Fifth Floor
+ * Boston, MA  02110-1301  USA
+ */
+package org.hibernate.test.cache.infinispan.functional;
+
+/**
+ * @author Steve Ebersole
+ */
+public class VersionedItem extends Item {
+    private Long version;
+
+    public Long getVersion() {
+        return version;
+    }
+
+    public void setVersion(Long version) {
+        this.version = version;
+    }
+}


Property changes on: core/trunk/cache-infinispan/src/test/java/org/hibernate/test/cache/infinispan/functional/VersionedItem.java
___________________________________________________________________
Name: svn:executable
   + *

Added: core/trunk/cache-infinispan/src/test/java/org/hibernate/test/cache/infinispan/functional/bulk/BulkOperationsTestCase.java
===================================================================
--- core/trunk/cache-infinispan/src/test/java/org/hibernate/test/cache/infinispan/functional/bulk/BulkOperationsTestCase.java	                        (rev 0)
+++ core/trunk/cache-infinispan/src/test/java/org/hibernate/test/cache/infinispan/functional/bulk/BulkOperationsTestCase.java	2009-08-21 08:39:33 UTC (rev 17386)
@@ -0,0 +1,321 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2009, Red Hat, Inc. and/or it's affiliates, 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.hibernate.test.cache.infinispan.functional.bulk;
+
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import javax.transaction.TransactionManager;
+
+import org.hibernate.FlushMode;
+import org.hibernate.cfg.Configuration;
+import org.hibernate.cfg.Environment;
+import org.hibernate.classic.Session;
+import org.hibernate.junit.functional.FunctionalTestCase;
+import org.hibernate.test.cache.infinispan.functional.Contact;
+import org.hibernate.test.cache.infinispan.functional.Customer;
+import org.hibernate.transaction.CMTTransactionFactory;
+import org.hibernate.transaction.TransactionManagerLookup;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * BulkOperationsTestCase.
+ * 
+ * @author Galder Zamarreño
+ * @since 3.5
+ */
+public class BulkOperationsTestCase extends FunctionalTestCase {
+
+   private static final Logger log = LoggerFactory.getLogger(BulkOperationsTestCase.class);
+   
+   private TransactionManager tm;
+            
+   public BulkOperationsTestCase(String string) {
+      super(string);
+   }
+
+   public String[] getMappings() {
+      return new String[] { "cache/infinispan/functional/Contact.hbm.xml", "cache/infinispan/functional/Customer.hbm.xml" };
+   }
+   
+   @Override
+   public String getCacheConcurrencyStrategy() {
+      return "transactional";
+   }
+   
+   protected Class getTransactionFactoryClass() {
+       return CMTTransactionFactory.class;
+   }
+
+   protected Class getConnectionProviderClass() {
+//      return org.hibernate.test.tm.ConnectionProviderImpl.class;
+      return org.hibernate.test.cache.infinispan.tm.XaConnectionProvider.class;
+   }
+  
+   protected Class<? extends TransactionManagerLookup> getTransactionManagerLookupClass() {
+      return org.hibernate.test.cache.infinispan.tm.XaTransactionManagerLookup.class;
+//      return org.hibernate.test.tm.TransactionManagerLookupImpl.class;
+   }
+
+   public void configure(Configuration cfg) {
+      super.configure(cfg);
+
+      cfg.setProperty(Environment.USE_SECOND_LEVEL_CACHE, "true");
+      cfg.setProperty(Environment.GENERATE_STATISTICS, "true");
+      cfg.setProperty(Environment.CONNECTION_PROVIDER, getConnectionProviderClass().getName());
+      cfg.setProperty(Environment.TRANSACTION_MANAGER_STRATEGY, getTransactionManagerLookupClass().getName());
+      
+      Class transactionFactory = getTransactionFactoryClass();
+      cfg.setProperty( Environment.TRANSACTION_STRATEGY, transactionFactory.getName());
+   }
+
+   public void testBulkOperations() throws Throwable {
+      System.out.println("*** testBulkOperations()");
+      boolean cleanedUp = false;
+      try {
+         tm = getTransactionManagerLookupClass().newInstance().getTransactionManager(null);
+         
+         createContacts();
+
+         List<Integer> rhContacts = getContactsByCustomer("Red Hat");
+         assertNotNull("Red Hat contacts exist", rhContacts);
+         assertEquals("Created expected number of Red Hat contacts", 10, rhContacts.size());
+
+         assertEquals("Deleted all Red Hat contacts", 10, deleteContacts());
+
+         List<Integer> jbContacts = getContactsByCustomer("JBoss");
+         assertNotNull("JBoss contacts exist", jbContacts);
+         assertEquals("JBoss contacts remain", 10, jbContacts.size());
+
+         for (Integer id : rhContacts) {
+            assertNull("Red Hat contact " + id + " cannot be retrieved", getContact(id));
+         }
+         rhContacts = getContactsByCustomer("Red Hat");
+         if (rhContacts != null) {
+            assertEquals("No Red Hat contacts remain", 0, rhContacts.size());
+         }
+
+         updateContacts("Kabir", "Updated");
+         for (Integer id : jbContacts) {
+            Contact contact = getContact(id);
+            assertNotNull("JBoss contact " + id + " exists", contact);
+            String expected = ("Kabir".equals(contact.getName())) ? "Updated" : "2222";
+            assertEquals("JBoss contact " + id + " has correct TLF", expected, contact.getTlf());
+         }
+
+         List<Integer> updated = getContactsByTLF("Updated");
+         assertNotNull("Got updated contacts", updated);
+         assertEquals("Updated contacts", 5, updated.size());
+      } catch(Throwable t) {
+         cleanedUp = true;
+         log.debug("Exceptional cleanup");
+         cleanup(true);
+         throw t;
+      } finally {
+         // cleanup the db so we can run this test multiple times w/o restarting the cluster
+         if (!cleanedUp) {
+            log.debug("Non exceptional cleanup");
+            cleanup(false);
+         }
+      }
+   }
+
+   public void createContacts() throws Exception {
+      log.debug("Create 10 contacts");
+      tm.begin();
+      try {
+         for (int i = 0; i < 10; i++)
+            createCustomer(i);
+         tm.commit();
+      } catch (Exception e) {
+         log.error("Unable to create customer", e);
+         tm.rollback();
+         throw e;
+      }
+   }
+
+   public int deleteContacts() throws Exception {
+      String deleteHQL = "delete Contact where customer in ";
+      deleteHQL += " (select customer FROM Customer as customer ";
+      deleteHQL += " where customer.name = :cName)";
+
+      tm.begin();
+      try {
+
+         Session session = getSessions().getCurrentSession();
+         int rowsAffected = session.createQuery(deleteHQL).setFlushMode(FlushMode.AUTO)
+                  .setParameter("cName", "Red Hat").executeUpdate();
+         tm.commit();
+         return rowsAffected;
+      } catch (Exception e) {
+         try {
+            tm.rollback();
+         } catch (Exception ee) {
+            // ignored
+         }
+         throw e;
+      }
+   }
+
+   public List<Integer> getContactsByCustomer(String customerName) throws Exception {
+      String selectHQL = "select contact.id from Contact contact";
+      selectHQL += " where contact.customer.name = :cName";
+
+      log.debug("Get contacts for customer " + customerName);
+      tm.begin();
+      try {
+
+         Session session = getSessions().getCurrentSession();
+         List results = session.createQuery(selectHQL).setFlushMode(FlushMode.AUTO).setParameter("cName", customerName)
+                  .list();
+         tm.commit();
+         return results;
+      } catch (Exception e) {
+         tm.rollback();
+         throw e;
+      }
+   }
+
+   public List<Integer> getContactsByTLF(String tlf) throws Exception {
+      String selectHQL = "select contact.id from Contact contact";
+      selectHQL += " where contact.tlf = :cTLF";
+
+      tm.begin();
+      try {
+
+         Session session = getSessions().getCurrentSession();
+         List results = session.createQuery(selectHQL).setFlushMode(FlushMode.AUTO).setParameter("cTLF", tlf).list();
+         tm.commit();
+         return results;
+      } catch (Exception e) {
+         tm.rollback();
+         throw e;
+      }
+   }
+
+   public int updateContacts(String name, String newTLF) throws Exception {
+      String updateHQL = "update Contact set tlf = :cNewTLF where name = :cName";
+
+      tm.begin();
+      try {
+
+         Session session = getSessions().getCurrentSession();
+         int rowsAffected = session.createQuery(updateHQL).setFlushMode(FlushMode.AUTO).setParameter("cNewTLF", newTLF)
+                  .setParameter("cName", name).executeUpdate();
+         tm.commit();
+         return rowsAffected;
+      } catch (Exception e) {
+         tm.rollback();
+         throw e;
+      }
+   }
+
+   public Contact getContact(Integer id) throws Exception {
+      tm.begin();
+      try {
+
+         Session session = getSessions().getCurrentSession();
+         Contact contact = (Contact) session.get(Contact.class, id);
+         tm.commit();
+         return contact;
+      } catch (Exception e) {
+         tm.rollback();
+         throw e;
+      }
+   }
+
+//   public void cleanup() throws Exception {
+//      String deleteContactHQL = "delete from Contact";
+//      String deleteCustomerHQL = "delete from Customer";
+//      tm.begin();
+//      try {
+//         Session session = getSessions().getCurrentSession();
+//         session.createQuery(deleteContactHQL).setFlushMode(FlushMode.AUTO).executeUpdate();
+//         session.createQuery(deleteCustomerHQL).setFlushMode(FlushMode.AUTO).executeUpdate();
+//         tm.commit();
+//      } catch (Exception e) {
+//         try {
+//            tm.rollback();
+//         } catch (Exception ee) {
+//            // ignored
+//         }
+//         throw e;
+//      }
+//   }
+   
+   public void cleanup(boolean ignore) throws Exception {
+      String deleteContactHQL = "delete from Contact";
+      String deleteCustomerHQL = "delete from Customer";
+      tm.begin();
+      try {
+         Session session = getSessions().getCurrentSession();
+         session.createQuery(deleteContactHQL).setFlushMode(FlushMode.AUTO).executeUpdate();
+         session.createQuery(deleteCustomerHQL).setFlushMode(FlushMode.AUTO).executeUpdate();
+         tm.commit();
+      } catch (Exception e) {
+         if (!ignore) {
+            try {
+               tm.rollback();
+            } catch (Exception ee) {
+               // ignored
+            }
+            throw e;
+         }
+      }
+   }
+
+   private Customer createCustomer(int id) throws Exception {
+      System.out.println("CREATE CUSTOMER " + id);
+      try {
+         Customer customer = new Customer();
+         customer.setName((id % 2 == 0) ? "JBoss" : "Red Hat");
+         Set<Contact> contacts = new HashSet<Contact>();
+
+         Contact kabir = new Contact();
+         kabir.setCustomer(customer);
+         kabir.setName("Kabir");
+         kabir.setTlf("1111");
+         contacts.add(kabir);
+
+         Contact bill = new Contact();
+         bill.setCustomer(customer);
+         bill.setName("Bill");
+         bill.setTlf("2222");
+         contacts.add(bill);
+
+         customer.setContacts(contacts);
+
+         Session s = openSession();
+         s.getTransaction().begin();
+         s.persist(customer);
+         s.getTransaction().commit();
+         s.close();
+         
+         return customer;
+      } finally {
+         System.out.println("CREATE CUSTOMER " + id + " -  END");
+      }
+   }
+
+}

Added: core/trunk/cache-infinispan/src/test/java/org/hibernate/test/cache/infinispan/functional/classloader/Account.java
===================================================================
--- core/trunk/cache-infinispan/src/test/java/org/hibernate/test/cache/infinispan/functional/classloader/Account.java	                        (rev 0)
+++ core/trunk/cache-infinispan/src/test/java/org/hibernate/test/cache/infinispan/functional/classloader/Account.java	2009-08-21 08:39:33 UTC (rev 17386)
@@ -0,0 +1,122 @@
+/*
+ * Hibernate, Relational Persistence for Idiomatic Java
+ *
+ * Copyright (c) 2007, Red Hat, Inc. and/or it's affiliates or third-party contributors as
+ * indicated by the @author tags or express copyright attribution
+ * statements applied by the authors.  All third-party contributions are
+ * distributed under license by Red Hat, Inc. and/or it's affiliates.
+ *
+ * This copyrighted material is made available to anyone wishing to use, modify,
+ * copy, or redistribute it subject to the terms and conditions of the GNU
+ * Lesser General Public License, as published by the Free Software Foundation.
+ *
+ * This program 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 distribution; if not, write to:
+ * Free Software Foundation, Inc.
+ * 51 Franklin Street, Fifth Floor
+ * Boston, MA  02110-1301  USA
+ */
+package org.hibernate.test.cache.infinispan.functional.classloader;
+
+import java.io.Serializable;
+
+/**
+ * Comment
+ * 
+ * @author Brian Stansberry
+ */
+public class Account implements Serializable {
+
+   private static final long serialVersionUID = 1L;
+
+   private Integer id;
+   private AccountHolder accountHolder;
+   private Integer balance;
+   private String branch;
+
+   public Integer getId() {
+      return id;
+   }
+
+   public void setId(Integer id) {
+      this.id = id;
+   }
+
+   public AccountHolder getAccountHolder() {
+      return accountHolder;
+   }
+
+   public void setAccountHolder(AccountHolder accountHolder) {
+      this.accountHolder = accountHolder;
+   }
+
+   public Integer getBalance() {
+      return balance;
+   }
+
+   public void setBalance(Integer balance) {
+      this.balance = balance;
+   }
+
+   public String getBranch() {
+      return branch;
+   }
+
+   public void setBranch(String branch) {
+      this.branch = branch;
+   }
+
+   public boolean equals(Object obj) {
+      if (obj == this)
+         return true;
+      if (!(obj instanceof Account))
+         return false;
+      Account acct = (Account) obj;
+      if (!safeEquals(id, acct.id))
+         return false;
+      if (!safeEquals(branch, acct.branch))
+         return false;
+      if (!safeEquals(balance, acct.balance))
+         return false;
+      if (!safeEquals(accountHolder, acct.accountHolder))
+         return false;
+      return true;
+   }
+
+   public int hashCode() {
+      int result = 17;
+      result = result * 31 + safeHashCode(id);
+      result = result * 31 + safeHashCode(branch);
+      result = result * 31 + safeHashCode(balance);
+      result = result * 31 + safeHashCode(accountHolder);
+      return result;
+   }
+
+   public String toString() {
+      StringBuffer sb = new StringBuffer(getClass().getName());
+      sb.append("[id=");
+      sb.append(id);
+      sb.append(",branch=");
+      sb.append(branch);
+      sb.append(",balance=");
+      sb.append(balance);
+      sb.append(",accountHolder=");
+      sb.append(accountHolder);
+      sb.append("]");
+      return sb.toString();
+   }
+
+   private static int safeHashCode(Object obj) {
+      return obj == null ? 0 : obj.hashCode();
+   }
+
+   private static boolean safeEquals(Object a, Object b) {
+      return (a == b || (a != null && a.equals(b)));
+   }
+
+}

Added: core/trunk/cache-infinispan/src/test/java/org/hibernate/test/cache/infinispan/functional/classloader/AccountHolder.java
===================================================================
--- core/trunk/cache-infinispan/src/test/java/org/hibernate/test/cache/infinispan/functional/classloader/AccountHolder.java	                        (rev 0)
+++ core/trunk/cache-infinispan/src/test/java/org/hibernate/test/cache/infinispan/functional/classloader/AccountHolder.java	2009-08-21 08:39:33 UTC (rev 17386)
@@ -0,0 +1,96 @@
+/*
+ * Hibernate, Relational Persistence for Idiomatic Java
+ *
+ * Copyright (c) 2007, Red Hat, Inc. and/or it's affiliates or third-party contributors as
+ * indicated by the @author tags or express copyright attribution
+ * statements applied by the authors.  All third-party contributions are
+ * distributed under license by Red Hat, Inc. and/or it's affiliates.
+ *
+ * This copyrighted material is made available to anyone wishing to use, modify,
+ * copy, or redistribute it subject to the terms and conditions of the GNU
+ * Lesser General Public License, as published by the Free Software Foundation.
+ *
+ * This program 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 distribution; if not, write to:
+ * Free Software Foundation, Inc.
+ * 51 Franklin Street, Fifth Floor
+ * Boston, MA  02110-1301  USA
+ */
+package org.hibernate.test.cache.infinispan.functional.classloader;
+
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.Serializable;
+
+/**
+ * Comment
+ * 
+ * @author Brian Stansberry
+ */
+public class AccountHolder implements Serializable
+{   
+   private static final long serialVersionUID = 1L;
+   
+   private String lastName;
+   private String ssn;
+   private transient boolean deserialized;
+   
+   public AccountHolder( ) {
+      this("Stansberry", "123-456-7890");
+   }
+   
+   public AccountHolder(String lastName, String ssn)
+   {
+      this.lastName = lastName;
+      this.ssn = ssn;
+   }
+   
+   public String getLastName( ) { return this.lastName; }
+   public void setLastName(String lastName) { this.lastName = lastName; }
+   
+   public String getSsn( ) { return ssn; }
+   public void setSsn(String ssn) { this.ssn = ssn; }
+   
+   public boolean equals(Object obj)
+   {
+      if (obj == this) return true;
+      if (!(obj instanceof AccountHolder)) return false;
+      AccountHolder pk = (AccountHolder)obj;
+      if (!lastName.equals(pk.lastName)) return false;
+      if (!ssn.equals(pk.ssn)) return false;
+      return true;
+   }
+   
+   public int hashCode( )
+   {
+      int result = 17;
+      result = result * 31 + lastName.hashCode();
+      result = result * 31 + ssn.hashCode();
+      return result;
+   }
+   
+   public String toString()
+   {
+      StringBuffer sb = new StringBuffer(getClass().getName());
+      sb.append("[lastName=");
+      sb.append(lastName);
+      sb.append(",ssn=");
+      sb.append(ssn);
+      sb.append(",deserialized=");
+      sb.append(deserialized);
+      sb.append("]");
+      return sb.toString();
+   }
+   
+   private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException
+   {
+      ois.defaultReadObject();
+      deserialized = true;
+   }
+
+}

Added: core/trunk/cache-infinispan/src/test/java/org/hibernate/test/cache/infinispan/functional/classloader/CacheAccessListener.java
===================================================================
--- core/trunk/cache-infinispan/src/test/java/org/hibernate/test/cache/infinispan/functional/classloader/CacheAccessListener.java	                        (rev 0)
+++ core/trunk/cache-infinispan/src/test/java/org/hibernate/test/cache/infinispan/functional/classloader/CacheAccessListener.java	2009-08-21 08:39:33 UTC (rev 17386)
@@ -0,0 +1,105 @@
+/*
+ * Hibernate, Relational Persistence for Idiomatic Java
+ *
+ * Copyright (c) 2008, Red Hat, Inc. and/or it's affiliates or third-party contributors as
+ * indicated by the @author tags or express copyright attribution
+ * statements applied by the authors.  All third-party contributions are
+ * distributed under license by Red Hat, Inc. and/or it's affiliates.
+ *
+ * This copyrighted material is made available to anyone wishing to use, modify,
+ * copy, or redistribute it subject to the terms and conditions of the GNU
+ * Lesser General Public License, as published by the Free Software Foundation.
+ *
+ * This program 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 distribution; if not, write to:
+ * Free Software Foundation, Inc.
+ * 51 Franklin Street, Fifth Floor
+ * Boston, MA  02110-1301  USA
+ */
+package org.hibernate.test.cache.infinispan.functional.classloader;
+
+import java.util.HashSet;
+import java.util.Set;
+
+import org.infinispan.notifications.Listener;
+import org.infinispan.notifications.cachelistener.annotation.CacheEntryCreated;
+import org.infinispan.notifications.cachelistener.annotation.CacheEntryModified;
+import org.infinispan.notifications.cachelistener.annotation.CacheEntryVisited;
+import org.infinispan.notifications.cachelistener.event.CacheEntryCreatedEvent;
+import org.infinispan.notifications.cachelistener.event.CacheEntryModifiedEvent;
+import org.infinispan.notifications.cachelistener.event.CacheEntryVisitedEvent;
+
+ at Listener
+public class CacheAccessListener {
+//   HashSet<Fqn<String>> modified = new HashSet<Fqn<String>>();
+//   HashSet<Fqn<String>> accessed = new HashSet<Fqn<String>>();
+   HashSet modified = new HashSet();
+   HashSet accessed = new HashSet();
+
+   public void clear() {
+      modified.clear();
+      accessed.clear();
+   }
+
+   @CacheEntryModified
+   public void nodeModified(CacheEntryModifiedEvent event) {
+      if (!event.isPre()) {
+         Object key = event.getKey();
+         System.out.println("MyListener - Modified node " + key);
+         modified.add(key);
+      }
+   }
+   
+   @CacheEntryCreated
+   public void nodeCreated(CacheEntryCreatedEvent event) {
+      if (!event.isPre()) {
+         Object key = event.getKey();
+         System.out.println("MyListener - Created node " + key);
+         modified.add(key);
+      }
+   }
+
+   @CacheEntryVisited
+   public void nodeVisited(CacheEntryVisitedEvent event) {
+      if (!event.isPre()) {
+         Object key = event.getKey();
+         System.out.println("MyListener - Visited node " + key);
+         accessed.add(key);
+      }
+   }
+
+   public boolean getSawRegionModification(Object key) {
+      return getSawRegion(key, modified);
+   }
+
+   public boolean getSawRegionAccess(Object key) {
+      return getSawRegion(key, accessed);
+   }
+
+   private boolean getSawRegion(Object key, Set sawEvents) {
+      if (sawEvents.contains(key)) {
+         sawEvents.remove(key);
+         return true;
+      }
+      return false;
+//      boolean saw = false;
+//      for (Object key : sawEvents) {
+//         
+//      }
+//      Fqn<String> fqn = Fqn.fromString(regionName);
+//      for (Iterator<Fqn<String>> it = sawEvent.iterator(); it.hasNext();) {
+//         Fqn<String> modified = (Fqn<String>) it.next();
+//         if (modified.isChildOf(fqn)) {
+//            it.remove();
+//            saw = true;
+//         }
+//      }
+//      return saw;
+   }
+
+}
\ No newline at end of file

Added: core/trunk/cache-infinispan/src/test/java/org/hibernate/test/cache/infinispan/functional/classloader/ClassLoaderTestDAO.java
===================================================================
--- core/trunk/cache-infinispan/src/test/java/org/hibernate/test/cache/infinispan/functional/classloader/ClassLoaderTestDAO.java	                        (rev 0)
+++ core/trunk/cache-infinispan/src/test/java/org/hibernate/test/cache/infinispan/functional/classloader/ClassLoaderTestDAO.java	2009-08-21 08:39:33 UTC (rev 17386)
@@ -0,0 +1,295 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2006, Red Hat, Inc. and/or it's affiliates, 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.hibernate.test.cache.infinispan.functional.classloader;
+
+import java.lang.reflect.Method;
+import java.util.Iterator;
+import java.util.List;
+
+import javax.transaction.TransactionManager;
+
+import org.hibernate.Query;
+import org.hibernate.Session;
+import org.hibernate.SessionFactory;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Comment
+ * 
+ * @author Brian Stansberry
+ */
+public class ClassLoaderTestDAO {
+   private static final Logger log = LoggerFactory.getLogger(ClassLoaderTestDAO.class);
+
+   private SessionFactory sessionFactory;
+   private TransactionManager tm;
+
+   private Class acctClass;
+   private Class holderClass;
+   private Method setId;
+   private Method setBalance;
+   private Method setBranch;
+   private Method setHolder;
+   private Object smith;
+   private Object jones;
+   private Object barney;
+   private Method setName;
+   private Method setSsn;
+
+   public ClassLoaderTestDAO(SessionFactory factory, TransactionManager tm) throws Exception {
+      this.sessionFactory = factory;
+      this.tm = tm;
+
+      acctClass = Thread.currentThread().getContextClassLoader().loadClass(
+               getClass().getPackage().getName() + ".Account");
+      holderClass = Thread.currentThread().getContextClassLoader().loadClass(
+               getClass().getPackage().getName() + ".AccountHolder");
+      setId = acctClass.getMethod("setId", Integer.class);
+      setBalance = acctClass.getMethod("setBalance", Integer.class);
+      setBranch = acctClass.getMethod("setBranch", String.class);
+      setHolder = acctClass.getMethod("setAccountHolder", holderClass);
+
+      setName = holderClass.getMethod("setLastName", String.class);
+      setSsn = holderClass.getMethod("setSsn", String.class);
+
+      smith = holderClass.newInstance();
+      setName.invoke(smith, "Smith");
+      setSsn.invoke(smith, "1000");
+
+      jones = holderClass.newInstance();
+      setName.invoke(jones, "Jones");
+      setSsn.invoke(jones, "2000");
+
+      barney = holderClass.newInstance();
+      setName.invoke(barney, "Barney");
+      setSsn.invoke(barney, "3000");
+   }
+
+   public Object getSmith() {
+      return smith;
+   }
+
+   public Object getJones() {
+      return jones;
+   }
+
+   public Object getBarney() {
+      return barney;
+   }
+
+   public void updateAccountBranch(Integer id, String branch) throws Exception {
+      log.debug("Updating account " + id + " to branch " + branch);
+      tm.begin();
+      try {
+         Session session = sessionFactory.getCurrentSession();
+         Object account = session.get(acctClass, id);
+         log.debug("Set branch " + branch);
+         setBranch.invoke(account, branch);
+         session.update(account);
+         tm.commit();
+      } catch (Exception e) {
+         log.error("rolling back", e);
+         tm.rollback();
+         throw e;
+      }
+      log.debug("Updated account " + id + " to branch " + branch);
+   }
+
+   public int getCountForBranch(String branch, boolean useRegion) throws Exception {
+      tm.begin();
+      try {
+         Query query = sessionFactory.getCurrentSession().createQuery(
+                  "select account from Account as account where account.branch = :branch");
+         query.setString("branch", branch);
+         if (useRegion) {
+            query.setCacheRegion("AccountRegion");
+         }
+         query.setCacheable(true);
+         int result = query.list().size();
+         tm.commit();
+         return result;
+      } catch (Exception e) {
+         log.error("rolling back", e);
+         tm.rollback();
+         throw e;
+      }
+
+   }
+
+   public void createAccount(Object holder, Integer id, Integer openingBalance, String branch) throws Exception {
+      log.debug("Creating account " + id);
+      tm.begin();
+      try {
+         Object account = acctClass.newInstance();
+         setId.invoke(account, id);
+         setHolder.invoke(account, holder);
+         setBalance.invoke(account, openingBalance);
+         log.debug("Set branch " + branch);
+         setBranch.invoke(account, branch);
+         sessionFactory.getCurrentSession().persist(account);
+         tm.commit();
+      } catch (Exception e) {
+         log.error("rolling back", e);
+         tm.rollback();
+         throw e;
+      }
+
+      log.debug("Created account " + id);
+   }
+
+   public Account getAccount(Integer id) throws Exception {
+      log.debug("Getting account " + id);
+      tm.begin();
+      try {
+         Session session = sessionFactory.getCurrentSession();
+         Account acct = (Account) session.get(acctClass, id);
+         tm.commit();
+         return acct;
+      } catch (Exception e) {
+         log.error("rolling back", e);
+         tm.rollback();
+         throw e;
+      }
+   }
+
+   public Account getAccountWithRefresh(Integer id) throws Exception {
+      log.debug("Getting account " + id + " with refresh");
+      tm.begin();
+      try {
+         Session session = sessionFactory.getCurrentSession();
+         Account acct = (Account) session.get(acctClass, id);
+         session.refresh(acct);
+         acct = (Account) session.get(acctClass, id);
+         tm.commit();
+         return acct;
+      } catch (Exception e) {
+         log.error("rolling back", e);
+         tm.rollback();
+         throw e;
+      }
+   }
+
+   public void updateAccountBalance(Integer id, Integer newBalance) throws Exception {
+      log.debug("Updating account " + id + " to balance " + newBalance);
+      tm.begin();
+      try {
+         Session session = sessionFactory.getCurrentSession();
+         Object account = session.get(acctClass, id);
+         setBalance.invoke(account, newBalance);
+         session.update(account);
+         tm.commit();
+      } catch (Exception e) {
+         log.error("rolling back", e);
+         tm.rollback();
+         throw e;
+      }
+      log.debug("Updated account " + id + " to balance " + newBalance);
+   }
+
+   public String getBranch(Object holder, boolean useRegion) throws Exception {
+      tm.begin();
+      try {
+         Query query = sessionFactory.getCurrentSession().createQuery(
+                  "select account.branch from Account as account where account.accountHolder = ?");
+         query.setParameter(0, holder);
+         if (useRegion) {
+            query.setCacheRegion("AccountRegion");
+         }
+         query.setCacheable(true);
+         String result = (String) query.list().get(0);
+         tm.commit();
+         return result;
+      } catch (Exception e) {
+         log.error("rolling back", e);
+         tm.rollback();
+         throw e;
+      }
+   }
+
+   public int getTotalBalance(Object holder, boolean useRegion) throws Exception {
+      List results = null;
+      tm.begin();
+      try {
+         Query query = sessionFactory.getCurrentSession().createQuery(
+                  "select account.balance from Account as account where account.accountHolder = ?");
+         query.setParameter(0, holder);
+         if (useRegion) {
+            query.setCacheRegion("AccountRegion");
+         }
+         query.setCacheable(true);
+         results = query.list();
+         tm.commit();
+      } catch (Exception e) {
+         log.error("rolling back", e);
+         tm.rollback();
+         throw e;
+      }
+
+      int total = 0;
+      if (results != null) {
+         for (Iterator it = results.iterator(); it.hasNext();) {
+            total += ((Integer) it.next()).intValue();
+            System.out.println("Total = " + total);
+         }
+      }
+      return total;
+   }
+
+   public void cleanup() throws Exception {
+      internalCleanup();
+   }
+
+   private void internalCleanup() throws Exception {
+      if (sessionFactory != null) {
+         tm.begin();
+         try {
+
+            Session session = sessionFactory.getCurrentSession();
+            Query query = session.createQuery("select account from Account as account");
+            List accts = query.list();
+            if (accts != null) {
+               for (Iterator it = accts.iterator(); it.hasNext();) {
+                  try {
+                     Object acct = it.next();
+                     log.info("Removing " + acct);
+                     session.delete(acct);
+                  } catch (Exception ignored) {
+                  }
+               }
+            }
+            tm.commit();
+         } catch (Exception e) {
+            tm.rollback();
+            throw e;
+         }
+      }
+   }
+
+   public void remove() {
+      try {
+         internalCleanup();
+      } catch (Exception e) {
+         log.error("Caught exception in remove", e);
+      }
+   }
+}

Added: core/trunk/cache-infinispan/src/test/java/org/hibernate/test/cache/infinispan/functional/classloader/IsolatedCacheTestSetup.java
===================================================================
--- core/trunk/cache-infinispan/src/test/java/org/hibernate/test/cache/infinispan/functional/classloader/IsolatedCacheTestSetup.java	                        (rev 0)
+++ core/trunk/cache-infinispan/src/test/java/org/hibernate/test/cache/infinispan/functional/classloader/IsolatedCacheTestSetup.java	2009-08-21 08:39:33 UTC (rev 17386)
@@ -0,0 +1,90 @@
+/*
+ * Copyright (c) 2007, Red Hat, Inc. and/or it's affiliates. All rights reserved.
+ *
+ * This copyrighted material is made available to anyone wishing to use, modify,
+ * copy, or redistribute it subject to the terms and conditions of the GNU
+ * Lesser General Public License, v. 2.1. This program is distributed in the
+ * hope that it will be useful, but WITHOUT A 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, v.2.1 along with this
+ * distribution; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * Red Hat Author(s): Brian Stansberry
+ */
+
+package org.hibernate.test.cache.infinispan.functional.classloader;
+
+import org.hibernate.test.cache.infinispan.functional.cluster.ClusterAwareRegionFactory;
+import org.hibernate.test.cache.infinispan.functional.cluster.DualNodeJtaTransactionManagerImpl;
+
+import junit.framework.Test;
+
+/**
+ * A TestSetup that uses SelectedClassnameClassLoader to ensure that certain classes are not visible
+ * to JBoss Cache or JGroups' classloader.
+ * 
+ * @author <a href="brian.stansberry at jboss.com">Brian Stansberry</a>
+ */
+public class IsolatedCacheTestSetup extends SelectedClassnameClassLoaderTestSetup {
+
+   private String[] isolatedClasses;
+   private String cacheConfig;
+
+   /**
+    * Create a new IsolatedCacheTestSetup.
+    */
+   public IsolatedCacheTestSetup(Test test, String[] isolatedClasses, String cacheConfig) {
+      super(test, null, null, isolatedClasses);
+      this.isolatedClasses = isolatedClasses;
+      this.cacheConfig = cacheConfig;
+   }
+
+   @Override
+   protected void setUp() throws Exception {
+      super.setUp();
+
+//      // At this point the TCCL cannot see the isolatedClasses
+//      // We want the caches to use this CL as their default classloader
+
+      ClassLoader tccl = Thread.currentThread().getContextClassLoader();
+
+//      org.jgroups.ChannelFactory cf = new org.jgroups.JChannelFactory();
+//      cf.setMultiplexerConfig(DEF_JGROUPS_RESOURCE);
+//
+//      // Use a CacheManager that will inject the desired defaultClassLoader into our caches
+//      CustomClassLoaderCacheManager cm = new CustomClassLoaderCacheManager(DEF_CACHE_FACTORY_RESOURCE, cf, tccl);
+//      cm.start();
+//      TestCacheInstanceManager.addTestCacheManager(DualNodeTestUtil.LOCAL, cm);
+//
+//      cm.getCache(cacheConfig, true);
+//
+//      // Repeat for the "remote" cache
+//
+//      cf = new org.jgroups.JChannelFactory();
+//      cf.setMultiplexerConfig(DEF_JGROUPS_RESOURCE);
+//
+//      cm = new CustomClassLoaderCacheManager(DEF_CACHE_FACTORY_RESOURCE, cf, tccl);
+//      cm.start();
+//      TestCacheInstanceManager.addTestCacheManager(DualNodeTestUtil.REMOTE, cm);
+//
+//      cm.getCache(cacheConfig, true);
+
+      // Now make the isolatedClasses visible to the test driver itself
+      SelectedClassnameClassLoader visible = new SelectedClassnameClassLoader(isolatedClasses, null, null, tccl);
+      Thread.currentThread().setContextClassLoader(visible);
+   }
+
+   @Override
+   protected void tearDown() throws Exception {
+      try {
+         super.tearDown();
+      } finally {
+         ClusterAwareRegionFactory.clearCacheManagers();
+         DualNodeJtaTransactionManagerImpl.cleanupTransactions();
+         DualNodeJtaTransactionManagerImpl.cleanupTransactionManagers();
+      }
+   }
+
+}

Added: core/trunk/cache-infinispan/src/test/java/org/hibernate/test/cache/infinispan/functional/classloader/IsolatedClassLoaderTest.java
===================================================================
--- core/trunk/cache-infinispan/src/test/java/org/hibernate/test/cache/infinispan/functional/classloader/IsolatedClassLoaderTest.java	                        (rev 0)
+++ core/trunk/cache-infinispan/src/test/java/org/hibernate/test/cache/infinispan/functional/classloader/IsolatedClassLoaderTest.java	2009-08-21 08:39:33 UTC (rev 17386)
@@ -0,0 +1,331 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2009, Red Hat, Inc. and/or it's affiliates, 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.hibernate.test.cache.infinispan.functional.classloader;
+
+import javax.transaction.TransactionManager;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+import org.hibernate.SessionFactory;
+import org.hibernate.cache.StandardQueryCache;
+import org.hibernate.test.cache.infinispan.functional.cluster.AbstractDualNodeTestCase;
+import org.hibernate.test.cache.infinispan.functional.cluster.ClusterAwareRegionFactory;
+import org.hibernate.test.cache.infinispan.functional.cluster.DualNodeJtaTransactionManagerImpl;
+import org.infinispan.Cache;
+import org.infinispan.manager.CacheManager;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Tests entity and query caching when class of objects being cached are not visible to Infinispan's
+ * classloader. Also serves as a general integration test.
+ * <p/>
+ * This test stores an object (AccountHolder) that isn't visible to the Infinispan classloader in
+ * the cache in two places:
+ * 
+ * 1) As part of the value tuple in an Account entity 2) As part of the FQN in a query cache entry
+ * (see query in ClassLoaderTestDAO.getBranch())
+ * 
+ * @author Galder Zamarreño
+ * @since 3.5
+ */
+public class IsolatedClassLoaderTest extends AbstractDualNodeTestCase {
+
+   public static final String OUR_PACKAGE = IsolatedClassLoaderTest.class.getPackage().getName();
+
+   private static final String CACHE_CONFIG = "replicated-query";
+
+   protected static final long SLEEP_TIME = 300L;
+
+   protected final Logger log = LoggerFactory.getLogger(getClass());
+
+   static int test = 0;
+
+   private Cache localQueryCache ;
+   private CacheAccessListener localQueryListener;
+
+   private Cache remoteQueryCache;
+   private CacheAccessListener remoteQueryListener;
+
+   public IsolatedClassLoaderTest(String string) {
+      super(string);
+   }
+
+   public static Test suite() throws Exception {
+      TestSuite suite = new TestSuite(IsolatedClassLoaderTest.class);
+      String[] acctClasses = { OUR_PACKAGE + ".Account", OUR_PACKAGE + ".AccountHolder" };
+      return new IsolatedCacheTestSetup(suite, acctClasses, CACHE_CONFIG);
+   }
+
+   @Override
+   public String[] getMappings() {
+      return new String[] { "cache/infinispan/functional/classloader/Account.hbm.xml" };
+   }
+
+   @Override
+   protected void cleanupTransactionManagement() {
+      // Don't clean up the managers, just the transactions
+      // Managers are still needed by the long-lived caches
+      DualNodeJtaTransactionManagerImpl.cleanupTransactions();
+   }
+
+   @Override
+   protected void cleanupTest() throws Exception {
+      try {
+         if (localQueryCache != null && localQueryListener != null)
+            localQueryCache.removeListener(localQueryListener);
+         if (remoteQueryCache != null && remoteQueryListener != null)
+            remoteQueryCache.removeListener(remoteQueryListener);
+      } finally {
+         super.cleanupTest();
+      }
+   }
+
+   /**
+    * Simply confirms that the test fixture's classloader isolation setup is functioning as
+    * expected.
+    * 
+    * @throws Exception
+    */
+   public void testIsolatedSetup() throws Exception {
+      // Bind a listener to the "local" cache
+      // Our region factory makes its CacheManager available to us
+      CacheManager localManager = ClusterAwareRegionFactory.getCacheManager(AbstractDualNodeTestCase.LOCAL);
+      Cache localAccountCache = localManager.getCache(Account.class.getName());
+      Cache localAccountHolderCache = localManager.getCache(AccountHolder.class.getName());
+
+      // Bind a listener to the "remote" cache
+      CacheManager remoteManager = ClusterAwareRegionFactory.getCacheManager(AbstractDualNodeTestCase.REMOTE);
+      Cache remoteAccountCache = remoteManager.getCache(Account.class.getName());
+      Cache remoteAccountHolderCache = remoteManager.getCache(AccountHolder.class.getName());
+
+      ClassLoader cl = Thread.currentThread().getContextClassLoader();
+      log.info("TCCL is " + cl);
+      Thread.currentThread().setContextClassLoader(cl.getParent());
+
+      // org.jboss.cache.Fqn fqn = org.jboss.cache.Fqn.fromString("/isolated1");
+      // org.jboss.cache.Region r = localCache.getRegion(fqn, true);
+      // r.registerContextClassLoader(cl.getParent());
+      // r.activate();
+      //      
+      // r = remoteCache.getRegion(fqn, true);
+      // r.registerContextClassLoader(cl.getParent());
+      // r.activate();
+      // Thread.currentThread().setContextClassLoader(cl);
+      Account acct = new Account();
+      acct.setAccountHolder(new AccountHolder());
+
+      try {
+         localAccountCache.put("isolated1", acct);
+         fail("Should not have succeeded in putting acct -- classloader not isolated");
+      } catch (Exception e) {
+         log.info("Caught exception as desired", e);
+      }
+
+      // localCache.getRegion(fqn,
+      // false).registerContextClassLoader(Thread.currentThread().getContextClassLoader());
+      // remoteCache.getRegion(fqn,
+      // false).registerContextClassLoader(Thread.currentThread().getContextClassLoader());
+
+      Thread.currentThread().setContextClassLoader(cl);
+      localAccountCache.put("isolated1", acct);
+      assertEquals(acct.getClass().getName(), remoteAccountCache.get("isolated1").getClass().getName());
+   }
+
+   public void testClassLoaderHandlingNamedQueryRegion() throws Exception {
+      queryTest(true);
+   }
+
+   public void testClassLoaderHandlingStandardQueryCache() throws Exception {
+      queryTest(false);
+   }
+
+   protected void queryTest(boolean useNamedRegion) throws Exception {
+      // Bind a listener to the "local" cache
+      // Our region factory makes its CacheManager available to us
+      CacheManager localManager = ClusterAwareRegionFactory.getCacheManager(AbstractDualNodeTestCase.LOCAL);
+      localQueryCache = localManager.getCache("local-query");
+      localQueryListener = new CacheAccessListener();
+      localQueryCache.addListener(localQueryListener);
+
+      TransactionManager localTM = DualNodeJtaTransactionManagerImpl.getInstance(AbstractDualNodeTestCase.LOCAL);
+
+      // Bind a listener to the "remote" cache
+      CacheManager remoteManager = ClusterAwareRegionFactory.getCacheManager(AbstractDualNodeTestCase.REMOTE);
+      remoteQueryCache = remoteManager.getCache("local-query");
+      remoteQueryListener = new CacheAccessListener();
+      remoteQueryCache.addListener(remoteQueryListener);
+
+      TransactionManager remoteTM = DualNodeJtaTransactionManagerImpl.getInstance(AbstractDualNodeTestCase.REMOTE);
+
+      SessionFactory localFactory = getEnvironment().getSessionFactory();
+      SessionFactory remoteFactory = getSecondNodeEnvironment().getSessionFactory();
+
+      ClassLoaderTestDAO dao0 = new ClassLoaderTestDAO(localFactory, localTM);
+      ClassLoaderTestDAO dao1 = new ClassLoaderTestDAO(remoteFactory, remoteTM);
+
+//      // Determine whether our query region is already there (in which case it
+//      // will receive remote messages immediately) or is yet to be created on
+//      // first use (in which case it will initially discard remote messages)
+//      String regionName = createRegionName(useNamedRegion ? "AccountRegion" : StandardQueryCache.class.getName());
+//      Region queryRegion = remoteCache.getRegion(Fqn.fromString(regionName), false);
+//      boolean queryRegionExists = queryRegion != null && queryRegion.isActive();
+
+      // Initial ops on node 0
+      setupEntities(dao0);
+
+      // Query on post code count
+      assertEquals("63088 has correct # of accounts", 6, dao0.getCountForBranch("63088", useNamedRegion));
+
+      assertTrue("Query cache used ", localQueryListener.getSawRegionModification("???"));
+      // Clear the access state
+      localQueryListener.getSawRegionAccess("???");
+
+      log.info("First query on node0 done");
+
+      // Sleep a bit to allow async repl to happen
+      sleep(SLEEP_TIME);
+
+//      // If region isn't activated yet, should not have been modified
+//      if (!queryRegionExists) {
+         assertFalse("Query cache remotely modified", remoteQueryListener.getSawRegionModification("???"));
+         // Clear the access state
+         remoteQueryListener.getSawRegionAccess("???");
+//      } else {
+         assertTrue("Query cache remotely modified ", remoteQueryListener.getSawRegionModification("???"));
+         // Clear the access state
+         remoteQueryListener.getSawRegionAccess("???");
+//      }
+
+      // Do query again from node 1
+      assertEquals("63088 has correct # of accounts", 6, dao1.getCountForBranch("63088", useNamedRegion));
+
+//      if (!queryRegionExists) {
+//         // Query should have activated the region and then been inserted
+         assertTrue("Query cache modified ", remoteQueryListener.getSawRegionModification("???"));
+         // Clear the access state
+         remoteQueryListener.getSawRegionAccess("???");
+//      }
+
+      log.info("First query on node 1 done");
+
+      // We now have the query cache region activated on both nodes.
+
+      // Sleep a bit to allow async repl to happen
+      sleep(SLEEP_TIME);
+
+      // Do some more queries on node 0
+
+      assertEquals("Correct branch for Smith", "94536", dao0.getBranch(dao0.getSmith(), useNamedRegion));
+
+      assertEquals("Correct high balances for Jones", 40, dao0.getTotalBalance(dao0.getJones(), useNamedRegion));
+
+      assertTrue("Query cache used ", localQueryListener.getSawRegionModification("???"));
+      // Clear the access state
+      localQueryListener.getSawRegionAccess("???");
+
+      log.info("Second set of queries on node0 done");
+
+      // Sleep a bit to allow async repl to happen
+      sleep(SLEEP_TIME);
+
+      // Check if the previous queries replicated
+      assertTrue("Query cache remotely modified ", remoteQueryListener.getSawRegionModification("???"));
+      // Clear the access state
+      remoteQueryListener.getSawRegionAccess("???");
+
+      // Do queries again from node 1
+      assertEquals("Correct branch for Smith", "94536", dao1.getBranch(dao1.getSmith(), useNamedRegion));
+
+      assertEquals("Correct high balances for Jones", 40, dao1.getTotalBalance(dao1.getJones(), useNamedRegion));
+
+      // Should be no change; query was already there
+      assertFalse("Query cache modified ", remoteQueryListener.getSawRegionModification("???"));
+      assertTrue("Query cache accessed ", remoteQueryListener.getSawRegionAccess("???"));
+
+      log.info("Second set of queries on node1 done");
+
+      // allow async to propagate
+      sleep(SLEEP_TIME);
+
+      // Modify underlying data on node 1
+      modifyEntities(dao1);
+
+      // allow async timestamp change to propagate
+      sleep(SLEEP_TIME);
+
+      // Confirm query results are correct on node 0
+
+      assertEquals("63088 has correct # of accounts", 7, dao0.getCountForBranch("63088", useNamedRegion));
+
+      assertEquals("Correct branch for Smith", "63088", dao0.getBranch(dao0.getSmith(), useNamedRegion));
+
+      assertEquals("Correct high balances for Jones", 50, dao0.getTotalBalance(dao0.getJones(), useNamedRegion));
+
+      log.info("Third set of queries on node0 done");
+   }
+
+   protected void setupEntities(ClassLoaderTestDAO dao) throws Exception {
+      dao.cleanup();
+
+      dao.createAccount(dao.getSmith(), new Integer(1001), new Integer(5), "94536");
+      dao.createAccount(dao.getSmith(), new Integer(1002), new Integer(15), "94536");
+      dao.createAccount(dao.getSmith(), new Integer(1003), new Integer(20), "94536");
+
+      dao.createAccount(dao.getJones(), new Integer(2001), new Integer(5), "63088");
+      dao.createAccount(dao.getJones(), new Integer(2002), new Integer(15), "63088");
+      dao.createAccount(dao.getJones(), new Integer(2003), new Integer(20), "63088");
+
+      dao.createAccount(dao.getBarney(), new Integer(3001), new Integer(5), "63088");
+      dao.createAccount(dao.getBarney(), new Integer(3002), new Integer(15), "63088");
+      dao.createAccount(dao.getBarney(), new Integer(3003), new Integer(20), "63088");
+
+      log.info("Standard entities created");
+   }
+
+   protected void resetRegionUsageState(CacheAccessListener localListener, CacheAccessListener remoteListener) {
+      String stdName = StandardQueryCache.class.getName();
+      String acctName = Account.class.getName();
+
+      localListener.getSawRegionModification(stdName);
+      localListener.getSawRegionModification(acctName);
+
+      localListener.getSawRegionAccess(stdName);
+      localListener.getSawRegionAccess(acctName);
+
+      remoteListener.getSawRegionModification(stdName);
+      remoteListener.getSawRegionModification(acctName);
+
+      remoteListener.getSawRegionAccess(stdName);
+      remoteListener.getSawRegionAccess(acctName);
+
+      log.info("Region usage state cleared");
+   }
+
+   protected void modifyEntities(ClassLoaderTestDAO dao) throws Exception {
+      dao.updateAccountBranch(1001, "63088");
+      dao.updateAccountBalance(2001, 15);
+
+      log.info("Entities modified");
+   }
+}

Added: core/trunk/cache-infinispan/src/test/java/org/hibernate/test/cache/infinispan/functional/classloader/SelectedClassnameClassLoader.java
===================================================================
--- core/trunk/cache-infinispan/src/test/java/org/hibernate/test/cache/infinispan/functional/classloader/SelectedClassnameClassLoader.java	                        (rev 0)
+++ core/trunk/cache-infinispan/src/test/java/org/hibernate/test/cache/infinispan/functional/classloader/SelectedClassnameClassLoader.java	2009-08-21 08:39:33 UTC (rev 17386)
@@ -0,0 +1,288 @@
+/*
+ * Hibernate, Relational Persistence for Idiomatic Java
+ *
+ * Copyright (c) 2007, Red Hat, Inc. and/or it's affiliates or third-party contributors as
+ * indicated by the @author tags or express copyright attribution
+ * statements applied by the authors.  All third-party contributions are
+ * distributed under license by Red Hat, Inc. and/or it's affiliates.
+ *
+ * This copyrighted material is made available to anyone wishing to use, modify,
+ * copy, or redistribute it subject to the terms and conditions of the GNU
+ * Lesser General Public License, as published by the Free Software Foundation.
+ *
+ * This program 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 distribution; if not, write to:
+ * Free Software Foundation, Inc.
+ * 51 Franklin Street, Fifth Floor
+ * Boston, MA  02110-1301  USA
+ */
+package org.hibernate.test.cache.infinispan.functional.classloader;
+
+import java.io.ByteArrayOutputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Map;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * A ClassLoader that loads classes whose classname begins with one of a
+ * given set of strings, without attempting first to delegate to its
+ * parent loader.
+ * <p>
+ * This class is intended to allow emulation of 2 different types of common J2EE
+ * classloading situations.
+ * <ul>
+ * <li>Servlet-style child-first classloading, where this class is the
+ * child loader.</li>
+ * <li>Parent-first classloading where the parent does not have access to
+ * certain classes</li>
+ * </ul>
+ * </p>
+ * <p>
+ * This class can also be configured to raise a ClassNotFoundException if
+ * asked to load certain classes, thus allowing classes on the classpath
+ * to be hidden from a test environment.
+ * </p>
+ *
+ * @author Brian Stansberry
+ */
+public class SelectedClassnameClassLoader extends ClassLoader
+{
+   private Logger log = LoggerFactory.getLogger(SelectedClassnameClassLoader.class);
+   
+   private String[] includedClasses = null;
+   private String[] excludedClasses = null;
+   private String[] notFoundClasses = null;
+
+   private Map<String, Class> classes = new java.util.HashMap<String, Class>();
+
+   /**
+    * Creates a new classloader that loads the given classes.
+    *
+    * @param includedClasses array of class or package names that should be
+    *                        directly loaded by this loader.  Classes
+    *                        whose name starts with any of the strings
+    *                        in this array will be loaded by this class,
+    *                        unless their name appears in
+    *                        <code>excludedClasses</code>.
+    *                        Can be <code>null</code>
+    * @param excludedClasses array of class or package names that should NOT
+    *                        be directly loaded by this loader.  Loading of
+    *                        classes whose name starts with any of the
+    *                        strings in this array will be delegated to
+    *                        <code>parent</code>, even if the classes
+    *                        package or classname appears in
+    *                        <code>includedClasses</code>.  Typically this
+    *                        parameter is used to exclude loading one or
+    *                        more classes in a package whose other classes
+    *                        are loaded by this object.
+    * @param parent          ClassLoader to which loading of classes should
+    *                        be delegated if necessary
+    */
+   public SelectedClassnameClassLoader(String[] includedClasses,
+                                       String[] excludedClasses,
+                                       ClassLoader parent)
+   {
+      this(includedClasses, excludedClasses, null, parent);
+   }
+
+   /**
+    * Creates a new classloader that loads the given classes.
+    *
+    * @param includedClasses array of class or package names that should be
+    *                        directly loaded by this loader.  Classes
+    *                        whose name starts with any of the strings
+    *                        in this array will be loaded by this class,
+    *                        unless their name appears in
+    *                        <code>excludedClasses</code>.
+    *                        Can be <code>null</code>
+    * @param excludedClasses array of class or package names that should NOT
+    *                        be directly loaded by this loader.  Loading of
+    *                        classes whose name starts with any of the
+    *                        strings in this array will be delegated to
+    *                        <code>parent</code>, even if the classes
+    *                        package or classname appears in
+    *                        <code>includedClasses</code>.  Typically this
+    *                        parameter is used to exclude loading one or
+    *                        more classes in a package whose other classes
+    *                        are loaded by this object.
+    * @param notFoundClasses array of class or package names for which this
+    *                        should raise a ClassNotFoundException
+    * @param parent          ClassLoader to which loading of classes should
+    *                        be delegated if necessary
+    */
+   public SelectedClassnameClassLoader(String[] includedClasses,
+                                       String[] excludedClasses,
+                                       String[] notFoundClasses,
+                                       ClassLoader parent)
+   {
+      super(parent);
+      this.includedClasses = includedClasses;
+      this.excludedClasses = excludedClasses;
+      this.notFoundClasses = notFoundClasses;
+      
+      log.debug("created " + this);
+   }
+
+   protected synchronized Class<?> loadClass(String name, boolean resolve)
+         throws ClassNotFoundException
+   {
+      log.trace("loadClass(" + name + "," + resolve + ")");
+      if (isIncluded(name) && (isExcluded(name) == false))
+      {
+         Class c = findClass(name);
+
+         if (resolve)
+         {
+            resolveClass(c);
+         }
+         return c;
+      }
+      else if (isNotFound(name))
+      {
+         throw new ClassNotFoundException(name + " is discarded");
+      }
+      else
+      {
+         return super.loadClass(name, resolve);
+      }
+   }
+
+   protected Class<?> findClass(String name) throws ClassNotFoundException
+   {
+      log.trace("findClass(" + name + ")");
+      Class result = classes.get(name);
+      if (result != null)
+      {
+         return result;
+      }
+
+      if (isIncluded(name) && (isExcluded(name) == false))
+      {
+         result = createClass(name);
+      }
+      else if (isNotFound(name))
+      {
+         throw new ClassNotFoundException(name + " is discarded");
+      }
+      else
+      {
+         result = super.findClass(name);
+      }
+
+      classes.put(name, result);
+
+      return result;
+   }
+
+   protected Class createClass(String name) throws ClassFormatError, ClassNotFoundException
+   {
+      log.info("createClass(" + name + ")");
+      try
+      {
+         InputStream is = getResourceAsStream(name.replace('.', '/').concat(".class"));
+         byte[] bytes = new byte[1024];
+         ByteArrayOutputStream baos = new ByteArrayOutputStream(1024);
+         int read;
+         while ((read = is.read(bytes)) > -1)
+         {
+            baos.write(bytes, 0, read);
+         }
+         bytes = baos.toByteArray();
+         return this.defineClass(name, bytes, 0, bytes.length);
+      }
+      catch (FileNotFoundException e)
+      {
+         throw new ClassNotFoundException("cannot find " + name, e);
+      }
+      catch (IOException e)
+      {
+         throw new ClassNotFoundException("cannot read " + name, e);
+      }
+   }
+
+   protected boolean isIncluded(String className)
+   {
+
+      if (includedClasses != null)
+      {
+         for (int i = 0; i < includedClasses.length; i++)
+         {
+            if (className.startsWith(includedClasses[i]))
+            {
+               return true;
+            }
+         }
+      }
+
+      return false;
+   }
+
+   protected boolean isExcluded(String className)
+   {
+
+      if (excludedClasses != null)
+      {
+         for (int i = 0; i < excludedClasses.length; i++)
+         {
+            if (className.startsWith(excludedClasses[i]))
+            {
+               return true;
+            }
+         }
+      }
+
+      return false;
+   }
+
+   protected boolean isNotFound(String className)
+   {
+
+      if (notFoundClasses != null)
+      {
+         for (int i = 0; i < notFoundClasses.length; i++)
+         {
+            if (className.startsWith(notFoundClasses[i]))
+            {
+               return true;
+            }
+         }
+      }
+
+      return false;
+   }
+   
+   public String toString()  {
+      String s = getClass().getName();
+      s += "[includedClasses=";
+      s += listClasses(includedClasses);
+      s += ";excludedClasses=";
+      s += listClasses(excludedClasses);
+      s += ";notFoundClasses=";
+      s += listClasses(notFoundClasses);
+      s += ";parent=";
+      s += getParent();
+      s += "]";
+      return s;
+   }
+   
+   private static String listClasses(String[] classes) {
+      if (classes == null) return null;
+      String s = "";
+      for (int i = 0; i < classes.length; i++) {
+         if (i > 0)
+            s += ",";
+         s += classes[i];
+      }
+      return s;
+   }
+   
+}

Added: core/trunk/cache-infinispan/src/test/java/org/hibernate/test/cache/infinispan/functional/classloader/SelectedClassnameClassLoaderTestSetup.java
===================================================================
--- core/trunk/cache-infinispan/src/test/java/org/hibernate/test/cache/infinispan/functional/classloader/SelectedClassnameClassLoaderTestSetup.java	                        (rev 0)
+++ core/trunk/cache-infinispan/src/test/java/org/hibernate/test/cache/infinispan/functional/classloader/SelectedClassnameClassLoaderTestSetup.java	2009-08-21 08:39:33 UTC (rev 17386)
@@ -0,0 +1,73 @@
+/*
+ * Copyright (c) 2007, Red Hat, Inc. and/or it's affiliates. All rights reserved.
+ *
+ * This copyrighted material is made available to anyone wishing to use, modify,
+ * copy, or redistribute it subject to the terms and conditions of the GNU
+ * Lesser General Public License, v. 2.1. This program is distributed in the
+ * hope that it will be useful, but WITHOUT A 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, v.2.1 along with this
+ * distribution; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * Red Hat Author(s): Brian Stansberry
+ */
+
+package org.hibernate.test.cache.infinispan.functional.classloader;
+
+import junit.extensions.TestSetup;
+import junit.framework.Test;
+
+/**
+ * A TestSetup that makes SelectedClassnameClassLoader the thread
+ * context classloader for the duration of the test.
+ * 
+ * @author <a href="brian.stansberry at jboss.com">Brian Stansberry</a>
+ * @version $Revision: 1 $
+ */
+public class SelectedClassnameClassLoaderTestSetup extends TestSetup
+{
+   private ClassLoader originalTCCL;
+   private String[] includedClasses;
+   private String[] excludedClasses;
+   private String[] notFoundClasses;
+   
+   
+   /**
+    * Create a new SelectedClassnameClassLoaderTestSetup.
+    * 
+    * @param test
+    */
+   public SelectedClassnameClassLoaderTestSetup(Test test,
+                                                String[] includedClasses,
+                                                String[] excludedClasses,
+                                                String[] notFoundClasses)
+   {
+      super(test);
+      this.includedClasses = includedClasses;
+      this.excludedClasses = excludedClasses;
+      this.notFoundClasses = notFoundClasses;
+   }
+
+   @Override
+   protected void setUp() throws Exception
+   {      
+      super.setUp();
+      
+      originalTCCL = Thread.currentThread().getContextClassLoader();
+      ClassLoader parent = originalTCCL == null ? getClass().getClassLoader() : originalTCCL;
+      ClassLoader selectedTCCL = new SelectedClassnameClassLoader(includedClasses, excludedClasses, notFoundClasses, parent);
+      Thread.currentThread().setContextClassLoader(selectedTCCL);
+   }
+
+   @Override
+   protected void tearDown() throws Exception
+   {
+      Thread.currentThread().setContextClassLoader(originalTCCL);
+      super.tearDown();
+   }
+   
+   
+
+}

Added: core/trunk/cache-infinispan/src/test/java/org/hibernate/test/cache/infinispan/functional/cluster/AbstractDualNodeTestCase.java
===================================================================
--- core/trunk/cache-infinispan/src/test/java/org/hibernate/test/cache/infinispan/functional/cluster/AbstractDualNodeTestCase.java	                        (rev 0)
+++ core/trunk/cache-infinispan/src/test/java/org/hibernate/test/cache/infinispan/functional/cluster/AbstractDualNodeTestCase.java	2009-08-21 08:39:33 UTC (rev 17386)
@@ -0,0 +1,238 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2009, Red Hat, Inc. and/or it's affiliates, 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.hibernate.test.cache.infinispan.functional.cluster;
+
+import org.hibernate.Session;
+import org.hibernate.cfg.Configuration;
+import org.hibernate.cfg.Environment;
+import org.hibernate.cfg.Mappings;
+import org.hibernate.dialect.Dialect;
+import org.hibernate.engine.SessionFactoryImplementor;
+import org.hibernate.junit.functional.ExecutionEnvironment;
+import org.hibernate.junit.functional.FunctionalTestCase;
+import org.hibernate.transaction.CMTTransactionFactory;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * AbstractDualNodeTestCase.
+ * 
+ * @author Galder Zamarreño
+ * @since 3.5
+ */
+public abstract class AbstractDualNodeTestCase extends FunctionalTestCase {
+   
+   private static final Logger log = LoggerFactory.getLogger(AbstractDualNodeTestCase.class);
+   public static final String NODE_ID_PROP = "hibernate.test.cluster.node.id";
+   public static final String LOCAL = "local";
+   public static final String REMOTE = "remote";
+   private ExecutionEnvironment secondNodeEnvironment;
+   private Session secondNodeSession;
+
+   public AbstractDualNodeTestCase(String string) {
+      super(string);
+   }
+   
+   public String[] getMappings() {
+      return new String[] { "cache/infinispan/functional/Contact.hbm.xml", "cache/infinispan/functional/Customer.hbm.xml" };
+   }
+   
+   @Override
+   public String getCacheConcurrencyStrategy() {
+      return "transactional";
+   }
+   
+   protected Class getCacheRegionFactory() {
+      return ClusterAwareRegionFactory.class;
+   }
+
+   @Override
+   public void configure(Configuration cfg) {
+      standardConfigure(cfg);
+      configureFirstNode(cfg);
+   }
+
+   @Override
+   protected void prepareTest() throws Exception {
+      log.info("Building second node locally managed execution env");
+      secondNodeEnvironment = new ExecutionEnvironment(new SecondNodeSettings());
+      secondNodeEnvironment.initialize();
+      super.prepareTest();
+   }
+   
+   @Override
+   protected void runTest() throws Throwable {
+      try {
+          super.runTest();
+      } finally {
+         if ( secondNodeSession != null && secondNodeSession.isOpen() ) {
+             if ( secondNodeSession.isConnected() ) {
+                secondNodeSession.connection().rollback();
+             }
+             secondNodeSession.close();
+             secondNodeSession = null;
+             fail( "unclosed session" );
+         } else {
+            secondNodeSession = null;
+         }
+         
+      }
+   }
+
+   @Override
+   protected void cleanupTest() throws Exception {
+      try {
+          super.cleanupTest();
+      
+          log.info( "Destroying second node locally managed execution env" );
+          secondNodeEnvironment.complete();
+          secondNodeEnvironment = null;
+      } finally {
+         cleanupTransactionManagement();
+      }
+   }
+   
+   protected void cleanupTransactionManagement() {
+      DualNodeJtaTransactionManagerImpl.cleanupTransactions();
+      DualNodeJtaTransactionManagerImpl.cleanupTransactionManagers();
+   }
+
+   public ExecutionEnvironment getSecondNodeEnvironment() {
+      return secondNodeEnvironment;
+   }
+
+   protected Class getConnectionProviderClass() {
+      return DualNodeConnectionProviderImpl.class;
+   }
+
+   protected Class getTransactionManagerLookupClass() {
+      return DualNodeTransactionManagerLookup.class;
+   }
+
+   protected Class getTransactionFactoryClass() {
+      return CMTTransactionFactory.class;
+   }
+
+   /**
+    * Apply any node-specific configurations to our first node.
+    * 
+    * @param the
+    *           Configuration to update.
+    */
+   protected void configureFirstNode(Configuration cfg) {
+      cfg.setProperty(NODE_ID_PROP, LOCAL);
+   }
+
+   /**
+    * Apply any node-specific configurations to our second node.
+    * 
+    * @param the
+    *           Configuration to update.
+    */
+   protected void configureSecondNode(Configuration cfg) {
+      cfg.setProperty(NODE_ID_PROP, REMOTE);
+   }
+   
+   protected void sleep(long ms) {
+      try {
+          Thread.sleep(ms);
+      }
+      catch (InterruptedException e) {
+          log.warn("Interrupted during sleep", e);
+      }
+  }
+
+   private void standardConfigure(Configuration cfg) {
+      super.configure(cfg);
+
+      cfg.setProperty(Environment.CONNECTION_PROVIDER, getConnectionProviderClass().getName());
+      cfg.setProperty(Environment.TRANSACTION_MANAGER_STRATEGY, getTransactionManagerLookupClass().getName());
+      cfg.setProperty(Environment.TRANSACTION_STRATEGY, getTransactionFactoryClass().getName());
+      cfg.setProperty(Environment.CACHE_REGION_FACTORY, getCacheRegionFactory().getName());
+   }
+
+   /**
+    * Settings impl that delegates most calls to the DualNodeTestCase itself, but overrides the
+    * configure method to allow separate cache settings for the second node.
+    */
+   public class SecondNodeSettings implements ExecutionEnvironment.Settings {
+      private final AbstractDualNodeTestCase delegate;
+
+      public SecondNodeSettings() {
+         this.delegate = AbstractDualNodeTestCase.this;
+      }
+
+      /**
+       * This is the important one -- we extend the delegate's work by adding second-node specific
+       * settings
+       */
+      public void configure(Configuration arg0) {
+         delegate.standardConfigure(arg0);
+         configureSecondNode(arg0);
+      }
+
+      /**
+       * Disable creating of schemas; we let the primary session factory do that to our shared
+       * database.
+       */
+      public boolean createSchema() {
+         return false;
+      }
+
+      /**
+       * Disable creating of schemas; we let the primary session factory do that to our shared
+       * database.
+       */
+      public boolean recreateSchemaAfterFailure() {
+         return false;
+      }
+
+      public void afterConfigurationBuilt(Mappings arg0, Dialect arg1) {
+         delegate.afterConfigurationBuilt(arg0, arg1);
+      }
+
+      public void afterSessionFactoryBuilt(SessionFactoryImplementor arg0) {
+         delegate.afterSessionFactoryBuilt(arg0);
+      }
+
+      public boolean appliesTo(Dialect arg0) {
+         return delegate.appliesTo(arg0);
+      }
+
+      public String getBaseForMappings() {
+         return delegate.getBaseForMappings();
+      }
+
+      public String getCacheConcurrencyStrategy() {
+         return delegate.getCacheConcurrencyStrategy();
+      }
+
+      public String[] getMappings() {
+         return delegate.getMappings();
+      }
+
+      public boolean overrideCacheStrategy() {
+         return delegate.overrideCacheStrategy();
+      }
+   }
+
+}

Added: core/trunk/cache-infinispan/src/test/java/org/hibernate/test/cache/infinispan/functional/cluster/ClusterAwareRegionFactory.java
===================================================================
--- core/trunk/cache-infinispan/src/test/java/org/hibernate/test/cache/infinispan/functional/cluster/ClusterAwareRegionFactory.java	                        (rev 0)
+++ core/trunk/cache-infinispan/src/test/java/org/hibernate/test/cache/infinispan/functional/cluster/ClusterAwareRegionFactory.java	2009-08-21 08:39:33 UTC (rev 17386)
@@ -0,0 +1,123 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2009, Red Hat, Inc. and/or it's affiliates, 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.hibernate.test.cache.infinispan.functional.cluster;
+
+import java.util.Hashtable;
+import java.util.Properties;
+
+import org.hibernate.cache.CacheDataDescription;
+import org.hibernate.cache.CacheException;
+import org.hibernate.cache.CollectionRegion;
+import org.hibernate.cache.EntityRegion;
+import org.hibernate.cache.QueryResultsRegion;
+import org.hibernate.cache.RegionFactory;
+import org.hibernate.cache.TimestampsRegion;
+import org.hibernate.cache.infinispan.InfinispanRegionFactory;
+import org.hibernate.cfg.Settings;
+import org.infinispan.manager.CacheManager;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * ClusterAwareRegionFactory.
+ * 
+ * @author Galder Zamarreño
+ * @since 3.5
+ */
+public class ClusterAwareRegionFactory implements RegionFactory {
+   
+   private static final Logger log = LoggerFactory.getLogger(ClusterAwareRegionFactory.class);
+   private static final Hashtable<String, CacheManager> cacheManagers = new Hashtable<String, CacheManager>();
+
+   private final InfinispanRegionFactory delegate = new InfinispanRegionFactory();
+   private String cacheManagerName;
+   private boolean locallyAdded;
+   
+   public ClusterAwareRegionFactory(Properties props) {
+   }
+   
+   public static CacheManager getCacheManager(String name) {
+      return cacheManagers.get(name);
+   }
+   
+   public static void addCacheManager(String name, CacheManager manager) {
+      cacheManagers.put(name, manager);
+   }
+   
+   public static void clearCacheManagers() {
+      for (CacheManager manager : cacheManagers.values()) {
+         try {
+            manager.stop();
+         } catch (Exception e) {
+            log.error("Exception cleaning up CacheManager " + manager, e);
+         }
+      }
+      cacheManagers.clear();      
+   }
+
+   public void start(Settings settings, Properties properties) throws CacheException {
+      cacheManagerName = properties.getProperty(AbstractDualNodeTestCase.NODE_ID_PROP);
+      
+      CacheManager existing = getCacheManager(cacheManagerName);
+      locallyAdded = (existing == null);
+      
+      if (locallyAdded) {
+         delegate.start(settings, properties);
+         cacheManagers.put(cacheManagerName, delegate.getCacheManager());
+      } else {
+         delegate.setCacheManager(existing);
+      }      
+   }
+
+   public void stop() {
+      if (locallyAdded) cacheManagers.remove(cacheManagerName);     
+      delegate.stop();
+   }
+
+   public CollectionRegion buildCollectionRegion(String regionName, Properties properties,
+            CacheDataDescription metadata) throws CacheException {
+      return delegate.buildCollectionRegion(regionName, properties, metadata);
+   }
+
+   public EntityRegion buildEntityRegion(String regionName, Properties properties,
+            CacheDataDescription metadata) throws CacheException {
+      return delegate.buildEntityRegion(regionName, properties, metadata);
+   }
+
+   public QueryResultsRegion buildQueryResultsRegion(String regionName, Properties properties)
+            throws CacheException {
+      return delegate.buildQueryResultsRegion(regionName, properties);
+   }
+
+   public TimestampsRegion buildTimestampsRegion(String regionName, Properties properties)
+            throws CacheException {
+      return delegate.buildTimestampsRegion(regionName, properties);
+   }
+
+   public boolean isMinimalPutsEnabledByDefault() {
+      return delegate.isMinimalPutsEnabledByDefault();
+   }
+
+   public long nextTimestamp() {
+      return delegate.nextTimestamp();
+   }
+}

Added: core/trunk/cache-infinispan/src/test/java/org/hibernate/test/cache/infinispan/functional/cluster/DualNodeConnectionProviderImpl.java
===================================================================
--- core/trunk/cache-infinispan/src/test/java/org/hibernate/test/cache/infinispan/functional/cluster/DualNodeConnectionProviderImpl.java	                        (rev 0)
+++ core/trunk/cache-infinispan/src/test/java/org/hibernate/test/cache/infinispan/functional/cluster/DualNodeConnectionProviderImpl.java	2009-08-21 08:39:33 UTC (rev 17386)
@@ -0,0 +1,85 @@
+/*
+ * Hibernate, Relational Persistence for Idiomatic Java
+ *
+ * Copyright (c) 2007, Red Hat, Inc. and/or it's affiliates or third-party contributors as
+ * indicated by the @author tags or express copyright attribution
+ * statements applied by the authors.  All third-party contributions are
+ * distributed under license by Red Hat, Inc. and/or it's affiliates.
+ *
+ * This copyrighted material is made available to anyone wishing to use, modify,
+ * copy, or redistribute it subject to the terms and conditions of the GNU
+ * Lesser General Public License, as published by the Free Software Foundation.
+ *
+ * This program 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 distribution; if not, write to:
+ * Free Software Foundation, Inc.
+ * 51 Franklin Street, Fifth Floor
+ * Boston, MA  02110-1301  USA
+ */
+package org.hibernate.test.cache.infinispan.functional.cluster;
+
+import java.sql.Connection;
+import java.sql.SQLException;
+import java.util.Properties;
+
+import org.hibernate.HibernateException;
+import org.hibernate.connection.ConnectionProvider;
+import org.hibernate.connection.ConnectionProviderFactory;
+
+/**
+ * A {@link ConnectionProvider} implementation adding JTA-style transactionality around the returned
+ * connections using the {@link DualNodeJtaTransactionManagerImpl}.
+ * 
+ * @author Brian Stansberry
+ */
+public class DualNodeConnectionProviderImpl implements ConnectionProvider {
+   private static ConnectionProvider actualConnectionProvider = ConnectionProviderFactory.newConnectionProvider();
+   private String nodeId;
+   private boolean isTransactional;
+
+   public static ConnectionProvider getActualConnectionProvider() {
+      return actualConnectionProvider;
+   }
+
+   public void configure(Properties props) throws HibernateException {
+      nodeId = props.getProperty(AbstractDualNodeTestCase.NODE_ID_PROP);
+      if (nodeId == null)
+         throw new HibernateException(AbstractDualNodeTestCase.NODE_ID_PROP + " not configured");
+   }
+
+   public Connection getConnection() throws SQLException {
+      DualNodeJtaTransactionImpl currentTransaction = DualNodeJtaTransactionManagerImpl
+               .getInstance(nodeId).getCurrentTransaction();
+      if (currentTransaction == null) {
+         isTransactional = false;
+         return actualConnectionProvider.getConnection();
+      } else {
+         isTransactional = true;
+         Connection connection = currentTransaction.getEnlistedConnection();
+         if (connection == null) {
+            connection = actualConnectionProvider.getConnection();
+            currentTransaction.enlistConnection(connection);
+         }
+         return connection;
+      }
+   }
+
+   public void closeConnection(Connection conn) throws SQLException {
+      if (!isTransactional) {
+         conn.close();
+      }
+   }
+
+   public void close() throws HibernateException {
+      actualConnectionProvider.close();
+   }
+
+   public boolean supportsAggressiveRelease() {
+      return true;
+   }
+}

Added: core/trunk/cache-infinispan/src/test/java/org/hibernate/test/cache/infinispan/functional/cluster/DualNodeJtaTransactionImpl.java
===================================================================
--- core/trunk/cache-infinispan/src/test/java/org/hibernate/test/cache/infinispan/functional/cluster/DualNodeJtaTransactionImpl.java	                        (rev 0)
+++ core/trunk/cache-infinispan/src/test/java/org/hibernate/test/cache/infinispan/functional/cluster/DualNodeJtaTransactionImpl.java	2009-08-21 08:39:33 UTC (rev 17386)
@@ -0,0 +1,253 @@
+/*
+ * Hibernate, Relational Persistence for Idiomatic Java
+ *
+ * Copyright (c) 2007, Red Hat, Inc. and/or it's affiliates or third-party contributors as
+ * indicated by the @author tags or express copyright attribution
+ * statements applied by the authors.  All third-party contributions are
+ * distributed under license by Red Hat, Inc. and/or it's affiliates.
+ *
+ * This copyrighted material is made available to anyone wishing to use, modify,
+ * copy, or redistribute it subject to the terms and conditions of the GNU
+ * Lesser General Public License, as published by the Free Software Foundation.
+ *
+ * This program 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 distribution; if not, write to:
+ * Free Software Foundation, Inc.
+ * 51 Franklin Street, Fifth Floor
+ * Boston, MA  02110-1301  USA
+ */
+package org.hibernate.test.cache.infinispan.functional.cluster;
+
+import java.sql.Connection;
+import java.sql.SQLException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import javax.transaction.HeuristicMixedException;
+import javax.transaction.HeuristicRollbackException;
+import javax.transaction.RollbackException;
+import javax.transaction.Status;
+import javax.transaction.Synchronization;
+import javax.transaction.SystemException;
+import javax.transaction.Transaction;
+import javax.transaction.xa.XAException;
+import javax.transaction.xa.XAResource;
+import javax.transaction.xa.Xid;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * SimpleJtaTransactionImpl variant that works with DualNodeTransactionManagerImpl.
+ * 
+ * @author Brian Stansberry
+ */
+public class DualNodeJtaTransactionImpl implements Transaction {
+   private static final Logger log = LoggerFactory.getLogger(DualNodeJtaTransactionImpl.class);
+
+   private int status;
+   private LinkedList synchronizations;
+   private Connection connection; // the only resource we care about is jdbc connection
+   private final DualNodeJtaTransactionManagerImpl jtaTransactionManager;
+   private List<XAResource> enlistedResources = new ArrayList<XAResource>();
+   private Xid xid = new DualNodeJtaTransactionXid();
+
+   public DualNodeJtaTransactionImpl(DualNodeJtaTransactionManagerImpl jtaTransactionManager) {
+      this.jtaTransactionManager = jtaTransactionManager;
+      this.status = Status.STATUS_ACTIVE;
+   }
+
+   public int getStatus() {
+      return status;
+   }
+
+   public void commit() throws RollbackException, HeuristicMixedException,
+            HeuristicRollbackException, IllegalStateException, SystemException {
+
+      if (status == Status.STATUS_MARKED_ROLLBACK) {
+         log.trace("on commit, status was marked for rollback-only");
+         rollback();
+      } else {
+         status = Status.STATUS_PREPARING;
+         
+         for (int i = 0; i < synchronizations.size(); i++) {
+            Synchronization s = (Synchronization) synchronizations.get(i);
+            s.beforeCompletion();
+         }
+         
+         if (!runXaResourcePrepare()) {
+            status = Status.STATUS_ROLLING_BACK;
+         } else {
+            status = Status.STATUS_PREPARED;
+         }
+
+         status = Status.STATUS_COMMITTING;
+
+         if (connection != null) {
+            try {
+               connection.commit();
+               connection.close();
+            } catch (SQLException sqle) {
+               status = Status.STATUS_UNKNOWN;
+               throw new SystemException();
+            }
+         }
+         
+
+         runXaResourceCommitTx();
+
+         status = Status.STATUS_COMMITTED;
+
+         for (int i = 0; i < synchronizations.size(); i++) {
+            Synchronization s = (Synchronization) synchronizations.get(i);
+            s.afterCompletion(status);
+         }
+
+         // status = Status.STATUS_NO_TRANSACTION;
+         jtaTransactionManager.endCurrent(this);
+      }
+   }
+
+   public void rollback() throws IllegalStateException, SystemException {
+      status = Status.STATUS_ROLLING_BACK;
+      runXaResourceRollback();
+      status = Status.STATUS_ROLLEDBACK;
+
+      if (connection != null) {
+         try {
+            connection.rollback();
+            connection.close();
+         } catch (SQLException sqle) {
+            status = Status.STATUS_UNKNOWN;
+            throw new SystemException();
+         }
+      }
+
+      if (synchronizations != null) {
+         for (int i = 0; i < synchronizations.size(); i++) {
+            Synchronization s = (Synchronization) synchronizations.get(i);
+            s.afterCompletion(status);
+         }
+      }
+
+      // status = Status.STATUS_NO_TRANSACTION;
+      jtaTransactionManager.endCurrent(this);
+   }
+
+   public void setRollbackOnly() throws IllegalStateException, SystemException {
+      status = Status.STATUS_MARKED_ROLLBACK;
+   }
+
+   public void registerSynchronization(Synchronization synchronization) throws RollbackException,
+            IllegalStateException, SystemException {
+      // todo : find the spec-allowable statuses during which synch can be registered...
+      if (synchronizations == null) {
+         synchronizations = new LinkedList();
+      }
+      synchronizations.add(synchronization);
+   }
+
+   public void enlistConnection(Connection connection) {
+      if (this.connection != null) {
+         throw new IllegalStateException("Connection already registered");
+      }
+      this.connection = connection;
+   }
+
+   public Connection getEnlistedConnection() {
+      return connection;
+   }
+
+   public boolean enlistResource(XAResource xaResource) throws RollbackException,
+            IllegalStateException, SystemException {
+      enlistedResources.add(xaResource);
+      try {
+         xaResource.start(xid, 0);
+      } catch (XAException e) {
+         log.error("Got an exception", e);
+         throw new SystemException(e.getMessage());
+      }
+      return true;
+   }
+
+   public boolean delistResource(XAResource xaResource, int i) throws IllegalStateException,
+            SystemException {
+      throw new SystemException("not supported");
+   }
+   
+   public Collection<XAResource> getEnlistedResources() {
+      return enlistedResources;
+   }
+   
+   private boolean runXaResourcePrepare() throws SystemException {
+      Collection<XAResource> resources = getEnlistedResources();
+      for (XAResource res : resources) {
+         try {
+            res.prepare(xid);
+         } catch (XAException e) {
+            log.trace("The resource wants to rollback!", e);
+            return false;
+         } catch (Throwable th) {
+            log.error("Unexpected error from resource manager!", th);
+            throw new SystemException(th.getMessage());
+         }
+      }
+      return true;
+   }
+   
+   private void runXaResourceRollback() {
+      Collection<XAResource> resources = getEnlistedResources();
+      for (XAResource res : resources) {
+         try {
+            res.rollback(xid);
+         } catch (XAException e) {
+            log.warn("Error while rolling back",e);
+         }
+      }
+   }
+
+   private boolean runXaResourceCommitTx() throws HeuristicMixedException {
+      Collection<XAResource> resources = getEnlistedResources();
+      for (XAResource res : resources) {
+         try {
+            res.commit(xid, false);//todo we only support one phase commit for now, change this!!!
+         } catch (XAException e) {
+            log.warn("exception while committing",e);
+            throw new HeuristicMixedException(e.getMessage());
+         }
+      }
+      return true;
+   }
+   
+   private static class DualNodeJtaTransactionXid implements Xid {
+      private static AtomicInteger txIdCounter = new AtomicInteger(0);
+      private int id = txIdCounter.incrementAndGet();
+
+      public int getFormatId() {
+         return id;
+      }
+
+      public byte[] getGlobalTransactionId() {
+         throw new IllegalStateException("TODO - please implement me!!!"); //todo implement!!!
+      }
+
+      public byte[] getBranchQualifier() {
+         throw new IllegalStateException("TODO - please implement me!!!"); //todo implement!!!
+      }
+
+      @Override
+      public String toString() {
+         return getClass().getSimpleName() + "{" +
+               "id=" + id +
+               '}';
+      }
+   }
+}

Added: core/trunk/cache-infinispan/src/test/java/org/hibernate/test/cache/infinispan/functional/cluster/DualNodeJtaTransactionManagerImpl.java
===================================================================
--- core/trunk/cache-infinispan/src/test/java/org/hibernate/test/cache/infinispan/functional/cluster/DualNodeJtaTransactionManagerImpl.java	                        (rev 0)
+++ core/trunk/cache-infinispan/src/test/java/org/hibernate/test/cache/infinispan/functional/cluster/DualNodeJtaTransactionManagerImpl.java	2009-08-21 08:39:33 UTC (rev 17386)
@@ -0,0 +1,158 @@
+/*
+ * Hibernate, Relational Persistence for Idiomatic Java
+ *
+ * Copyright (c) 2007, Red Hat, Inc. and/or it's affiliates or third-party contributors as
+ * indicated by the @author tags or express copyright attribution
+ * statements applied by the authors.  All third-party contributions are
+ * distributed under license by Red Hat, Inc. and/or it's affiliates.
+ *
+ * This copyrighted material is made available to anyone wishing to use, modify,
+ * copy, or redistribute it subject to the terms and conditions of the GNU
+ * Lesser General Public License, as published by the Free Software Foundation.
+ *
+ * This program 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 distribution; if not, write to:
+ * Free Software Foundation, Inc.
+ * 51 Franklin Street, Fifth Floor
+ * Boston, MA  02110-1301  USA
+ */
+package org.hibernate.test.cache.infinispan.functional.cluster;
+
+import java.util.Hashtable;
+
+import javax.transaction.HeuristicMixedException;
+import javax.transaction.HeuristicRollbackException;
+import javax.transaction.InvalidTransactionException;
+import javax.transaction.NotSupportedException;
+import javax.transaction.RollbackException;
+import javax.transaction.Status;
+import javax.transaction.SystemException;
+import javax.transaction.Transaction;
+import javax.transaction.TransactionManager;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Variant of SimpleJtaTransactionManagerImpl that doesn't use a VM-singleton, but rather a set of
+ * impls keyed by a node id.
+ * 
+ * @author Brian Stansberry
+ */
+public class DualNodeJtaTransactionManagerImpl implements TransactionManager {
+
+   private static final Logger log = LoggerFactory.getLogger(DualNodeJtaTransactionManagerImpl.class);
+
+   private static final Hashtable INSTANCES = new Hashtable();
+
+   private ThreadLocal currentTransaction = new ThreadLocal();
+   private String nodeId;
+
+   public synchronized static DualNodeJtaTransactionManagerImpl getInstance(String nodeId) {
+      DualNodeJtaTransactionManagerImpl tm = (DualNodeJtaTransactionManagerImpl) INSTANCES
+               .get(nodeId);
+      if (tm == null) {
+         tm = new DualNodeJtaTransactionManagerImpl(nodeId);
+         INSTANCES.put(nodeId, tm);
+      }
+      return tm;
+   }
+
+   public synchronized static void cleanupTransactions() {
+      for (java.util.Iterator it = INSTANCES.values().iterator(); it.hasNext();) {
+         TransactionManager tm = (TransactionManager) it.next();
+         try {
+            tm.suspend();
+         } catch (Exception e) {
+            log.error("Exception cleaning up TransactionManager " + tm);
+         }
+      }
+   }
+
+   public synchronized static void cleanupTransactionManagers() {
+      INSTANCES.clear();
+   }
+
+   private DualNodeJtaTransactionManagerImpl(String nodeId) {
+      this.nodeId = nodeId;
+   }
+
+   public int getStatus() throws SystemException {
+      Transaction tx = getCurrentTransaction();
+      return tx == null ? Status.STATUS_NO_TRANSACTION : tx.getStatus();
+   }
+
+   public Transaction getTransaction() throws SystemException {
+      return (Transaction) currentTransaction.get();
+   }
+
+   public DualNodeJtaTransactionImpl getCurrentTransaction() {
+      return (DualNodeJtaTransactionImpl) currentTransaction.get();
+   }
+
+   public void begin() throws NotSupportedException, SystemException {
+      currentTransaction.set(new DualNodeJtaTransactionImpl(this));
+   }
+
+   public Transaction suspend() throws SystemException {
+      DualNodeJtaTransactionImpl suspended = getCurrentTransaction();
+      log.trace(nodeId + ": Suspending " + suspended + " for thread "
+               + Thread.currentThread().getName());
+      currentTransaction.set(null);
+      return suspended;
+   }
+
+   public void resume(Transaction transaction) throws InvalidTransactionException,
+            IllegalStateException, SystemException {
+      currentTransaction.set((DualNodeJtaTransactionImpl) transaction);
+      log.trace(nodeId + ": Resumed " + transaction + " for thread "
+               + Thread.currentThread().getName());
+   }
+
+   public void commit() throws RollbackException, HeuristicMixedException,
+            HeuristicRollbackException, SecurityException, IllegalStateException, SystemException {
+      Transaction tx = getCurrentTransaction();
+      if (tx == null) {
+         throw new IllegalStateException("no current transaction to commit");
+      }
+      tx.commit();
+   }
+
+   public void rollback() throws IllegalStateException, SecurityException, SystemException {
+      Transaction tx = getCurrentTransaction();
+      if (tx == null) {
+         throw new IllegalStateException("no current transaction");
+      }
+      tx.rollback();
+   }
+
+   public void setRollbackOnly() throws IllegalStateException, SystemException {
+      Transaction tx = getCurrentTransaction();
+      if (tx == null) {
+         throw new IllegalStateException("no current transaction");
+      }
+      tx.setRollbackOnly();
+   }
+
+   public void setTransactionTimeout(int i) throws SystemException {
+   }
+
+   void endCurrent(DualNodeJtaTransactionImpl transaction) {
+      if (transaction == currentTransaction.get()) {
+         currentTransaction.set(null);
+      }
+   }
+
+   public String toString() {
+      StringBuffer sb = new StringBuffer(getClass().getName());
+      sb.append("[nodeId=");
+      sb.append(nodeId);
+      sb.append("]");
+      return sb.toString();
+   }
+}

Added: core/trunk/cache-infinispan/src/test/java/org/hibernate/test/cache/infinispan/functional/cluster/DualNodeTransactionManagerLookup.java
===================================================================
--- core/trunk/cache-infinispan/src/test/java/org/hibernate/test/cache/infinispan/functional/cluster/DualNodeTransactionManagerLookup.java	                        (rev 0)
+++ core/trunk/cache-infinispan/src/test/java/org/hibernate/test/cache/infinispan/functional/cluster/DualNodeTransactionManagerLookup.java	2009-08-21 08:39:33 UTC (rev 17386)
@@ -0,0 +1,55 @@
+/*
+ * Hibernate, Relational Persistence for Idiomatic Java
+ *
+ * Copyright (c) 2007, Red Hat, Inc. and/or it's affiliates or third-party contributors as
+ * indicated by the @author tags or express copyright attribution
+ * statements applied by the authors.  All third-party contributions are
+ * distributed under license by Red Hat, Inc. and/or it's affiliates.
+ *
+ * This copyrighted material is made available to anyone wishing to use, modify,
+ * copy, or redistribute it subject to the terms and conditions of the GNU
+ * Lesser General Public License, as published by the Free Software Foundation.
+ *
+ * This program 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 distribution; if not, write to:
+ * Free Software Foundation, Inc.
+ * 51 Franklin Street, Fifth Floor
+ * Boston, MA  02110-1301  USA
+ */
+package org.hibernate.test.cache.infinispan.functional.cluster;
+
+import java.util.Properties;
+import javax.transaction.TransactionManager;
+import javax.transaction.Transaction;
+
+import org.hibernate.transaction.TransactionManagerLookup;
+import org.hibernate.HibernateException;
+
+/**
+ * SimpleJtaTransactionManagerLookupImpl subclass that finds a different DualNodeTransactionManager
+ * based on the value of property {@link DualNodeTestUtil#NODE_ID_PROP}.
+ * 
+ * @author Brian Stansberry
+ */
+public class DualNodeTransactionManagerLookup implements TransactionManagerLookup {
+
+   public TransactionManager getTransactionManager(Properties props) throws HibernateException {
+      String nodeId = props.getProperty(AbstractDualNodeTestCase.NODE_ID_PROP);
+      if (nodeId == null)
+         throw new HibernateException(AbstractDualNodeTestCase.NODE_ID_PROP + " not configured");
+      return DualNodeJtaTransactionManagerImpl.getInstance(nodeId);
+   }
+
+   public String getUserTransactionName() {
+      throw new UnsupportedOperationException("jndi currently not implemented for these tests");
+   }
+
+   public Object getTransactionIdentifier(Transaction transaction) {
+      return transaction;
+   }
+}

Added: core/trunk/cache-infinispan/src/test/java/org/hibernate/test/cache/infinispan/functional/cluster/EntityCollectionInvalidationTestCase.java
===================================================================
--- core/trunk/cache-infinispan/src/test/java/org/hibernate/test/cache/infinispan/functional/cluster/EntityCollectionInvalidationTestCase.java	                        (rev 0)
+++ core/trunk/cache-infinispan/src/test/java/org/hibernate/test/cache/infinispan/functional/cluster/EntityCollectionInvalidationTestCase.java	2009-08-21 08:39:33 UTC (rev 17386)
@@ -0,0 +1,387 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2009, Red Hat, Inc. and/or it's affiliates, 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.hibernate.test.cache.infinispan.functional.cluster;
+
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Set;
+
+import javax.transaction.TransactionManager;
+
+import org.hibernate.Session;
+import org.hibernate.SessionFactory;
+import org.hibernate.cache.CacheKey;
+import org.hibernate.test.cache.infinispan.functional.Contact;
+import org.hibernate.test.cache.infinispan.functional.Customer;
+import org.infinispan.Cache;
+import org.infinispan.manager.CacheManager;
+import org.infinispan.marshall.MarshalledValue;
+import org.infinispan.notifications.Listener;
+import org.infinispan.notifications.cachelistener.annotation.CacheEntryVisited;
+import org.infinispan.notifications.cachelistener.event.CacheEntryVisitedEvent;
+import org.jboss.util.collection.ConcurrentSet;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * EntityCollectionInvalidationTestCase.
+ * 
+ * @author Galder Zamarreño
+ * @since 3.5
+ */
+public class EntityCollectionInvalidationTestCase extends AbstractDualNodeTestCase {
+   private static final Logger log = LoggerFactory.getLogger(EntityCollectionInvalidationTestCase.class);
+   private static final long SLEEP_TIME = 50l;
+   private static final Integer CUSTOMER_ID = new Integer(1);
+   static int test = 0;
+
+   public EntityCollectionInvalidationTestCase(String string) {
+      super(string);
+   }
+
+   protected String getEntityCacheConfigName() {
+      return "entity";
+   }
+
+   public void testAll() throws Exception {
+      log.info("*** testAll()");
+
+      // Bind a listener to the "local" cache
+      // Our region factory makes its CacheManager available to us
+      CacheManager localManager = ClusterAwareRegionFactory.getCacheManager(AbstractDualNodeTestCase.LOCAL);
+      // Cache localCache = localManager.getCache("entity");
+      Cache localCustomerCache = localManager.getCache(Customer.class.getName());
+      Cache localContactCache = localManager.getCache(Contact.class.getName());
+      Cache localCollectionCache = localManager.getCache(Customer.class.getName() + ".contacts");
+      MyListener localListener = new MyListener("local");
+      localCustomerCache.addListener(localListener);
+      localContactCache.addListener(localListener);
+      localCollectionCache.addListener(localListener);
+      TransactionManager localTM = DualNodeJtaTransactionManagerImpl.getInstance(AbstractDualNodeTestCase.LOCAL);
+
+      // Bind a listener to the "remote" cache
+      CacheManager remoteManager = ClusterAwareRegionFactory.getCacheManager(AbstractDualNodeTestCase.REMOTE);
+      Cache remoteCustomerCache = remoteManager.getCache(Customer.class.getName());
+      Cache remoteContactCache = remoteManager.getCache(Contact.class.getName());
+      Cache remoteCollectionCache = remoteManager.getCache(Customer.class.getName() + ".contacts");
+      MyListener remoteListener = new MyListener("remote");
+      remoteCustomerCache.addListener(remoteListener);
+      remoteContactCache.addListener(remoteListener);
+      remoteCollectionCache.addListener(remoteListener);
+      TransactionManager remoteTM = DualNodeJtaTransactionManagerImpl.getInstance(AbstractDualNodeTestCase.REMOTE);
+
+      SessionFactory localFactory = getEnvironment().getSessionFactory();
+      SessionFactory remoteFactory = getSecondNodeEnvironment().getSessionFactory();
+
+      try {
+         assertTrue(remoteListener.isEmpty());
+         assertTrue(localListener.isEmpty());
+         
+         log.debug("Create node 0");
+         IdContainer ids = createCustomer(localFactory, localTM);
+
+         assertTrue(remoteListener.isEmpty());
+         assertTrue(localListener.isEmpty());
+         
+         // Sleep a bit to let async commit propagate. Really just to
+         // help keep the logs organized for debugging any issues
+         sleep(SLEEP_TIME);
+
+         log.debug("Find node 0");
+         // This actually brings the collection into the cache
+         getCustomer(ids.customerId, localFactory, localTM);
+
+         sleep(SLEEP_TIME);
+
+         // Now the collection is in the cache so, the 2nd "get"
+         // should read everything from the cache
+         log.debug("Find(2) node 0");
+         localListener.clear();
+         getCustomer(ids.customerId, localFactory, localTM);
+
+         // Check the read came from the cache
+         log.debug("Check cache 0");
+         assertLoadedFromCache(localListener, ids.customerId, ids.contactIds);
+
+         log.debug("Find node 1");
+         // This actually brings the collection into the cache since invalidation is in use
+         getCustomer(ids.customerId, remoteFactory, remoteTM);
+         
+         // Now the collection is in the cache so, the 2nd "get"
+         // should read everything from the cache
+         log.debug("Find(2) node 1");
+         remoteListener.clear();
+         getCustomer(ids.customerId, remoteFactory, remoteTM);
+
+         // Check the read came from the cache
+         log.debug("Check cache 1");
+         assertLoadedFromCache(remoteListener, ids.customerId, ids.contactIds);
+
+         // Modify customer in remote
+         remoteListener.clear();
+         ids = modifyCustomer(ids.customerId, remoteFactory, remoteTM);
+         sleep(250);
+         assertLoadedFromCache(remoteListener, ids.customerId, ids.contactIds);
+
+         // After modification, local cache should have been invalidated and hence should be empty
+         assertTrue(localCollectionCache.isEmpty());
+         assertTrue(localCustomerCache.isEmpty());
+      } catch (Exception e) {
+         log.error("Error", e);
+         throw e;
+      } finally {
+         // cleanup the db
+         log.debug("Cleaning up");
+         cleanup(localFactory, localTM);
+      }
+   }
+
+   private IdContainer createCustomer(SessionFactory sessionFactory, TransactionManager tm)
+            throws Exception {
+      log.debug("CREATE CUSTOMER");
+
+      tm.begin();
+
+      try {
+         Session session = sessionFactory.getCurrentSession();
+         Customer customer = new Customer();
+         customer.setName("JBoss");
+         Set<Contact> contacts = new HashSet<Contact>();
+
+         Contact kabir = new Contact();
+         kabir.setCustomer(customer);
+         kabir.setName("Kabir");
+         kabir.setTlf("1111");
+         contacts.add(kabir);
+
+         Contact bill = new Contact();
+         bill.setCustomer(customer);
+         bill.setName("Bill");
+         bill.setTlf("2222");
+         contacts.add(bill);
+
+         customer.setContacts(contacts);
+
+         session.save(customer);
+         tm.commit();
+
+         IdContainer ids = new IdContainer();
+         ids.customerId = customer.getId();
+         Set contactIds = new HashSet();
+         contactIds.add(kabir.getId());
+         contactIds.add(bill.getId());
+         ids.contactIds = contactIds;
+
+         return ids;
+      } catch (Exception e) {
+         log.error("Caught exception creating customer", e);
+         try {
+            tm.rollback();
+         } catch (Exception e1) {
+            log.error("Exception rolling back txn", e1);
+         }
+         throw e;
+      } finally {
+         log.debug("CREATE CUSTOMER -  END");
+      }
+   }
+
+   private Customer getCustomer(Integer id, SessionFactory sessionFactory, TransactionManager tm) throws Exception {
+      log.debug("Find customer with id=" + id);
+      tm.begin();
+      try {
+         Session session = sessionFactory.getCurrentSession();
+         Customer customer = doGetCustomer(id, session, tm);
+         tm.commit();
+         return customer;
+      } catch (Exception e) {
+         try {
+            tm.rollback();
+         } catch (Exception e1) {
+            log.error("Exception rolling back txn", e1);
+         }
+         throw e;
+      } finally {
+         log.debug("Find customer ended.");
+      }
+   }
+   
+   private Customer doGetCustomer(Integer id, Session session, TransactionManager tm) throws Exception {
+      Customer customer = (Customer) session.get(Customer.class, id);
+      // Access all the contacts
+      for (Iterator it = customer.getContacts().iterator(); it.hasNext();) {
+         ((Contact) it.next()).getName();
+      }
+      return customer;
+   }
+   
+   private IdContainer modifyCustomer(Integer id, SessionFactory sessionFactory, TransactionManager tm) throws Exception {
+      log.debug("Modify customer with id=" + id);
+      tm.begin();
+      try {
+         Session session = sessionFactory.getCurrentSession();
+         IdContainer ids = new IdContainer();
+         Set contactIds = new HashSet();
+         Customer customer = doGetCustomer(id, session, tm);
+         customer.setName("NewJBoss");
+         ids.customerId = customer.getId();
+         
+//         Set<Contact> contacts = customer.getContacts();
+//         for (Contact c : contacts) {
+//            if (c.getName().equals("Kabir")) {
+//               contacts.remove(c);
+//            } else {
+//               contactIds.add(c.getId());
+//            }
+//         }
+//         ids.contactIds = contactIds;
+//         customer.setContacts(contacts);
+         Set<Contact> contacts = customer.getContacts();
+         for (Contact c : contacts) {
+            contactIds.add(c.getId());
+         }
+         Contact contact = contacts.iterator().next();
+         contacts.remove(contact);
+         contactIds.remove(contact.getId());
+         ids.contactIds = contactIds;
+         contact.setCustomer(null);
+         
+         session.save(customer);
+         tm.commit();
+         return ids;
+      } catch (Exception e) {
+         try {
+            tm.rollback();
+         } catch (Exception e1) {
+            log.error("Exception rolling back txn", e1);
+         }
+         throw e;
+      } finally {
+         log.debug("Find customer ended.");
+      }
+   }
+
+   private void cleanup(SessionFactory sessionFactory, TransactionManager tm) throws Exception {
+      tm.begin();
+      try {
+         Session session = sessionFactory.getCurrentSession();
+         Customer c = (Customer) session.get(Customer.class, CUSTOMER_ID);
+         if (c != null) {
+            Set contacts = c.getContacts();
+            for (Iterator it = contacts.iterator(); it.hasNext();)
+               session.delete(it.next());
+            c.setContacts(null);
+            session.delete(c);
+         }
+
+         tm.commit();
+      } catch (Exception e) {
+         try {
+            tm.rollback();
+         } catch (Exception e1) {
+            log.error("Exception rolling back txn", e1);
+         }
+         log.error("Caught exception in cleanup", e);
+      }
+   }
+
+   private void assertLoadedFromCache(MyListener listener, Integer custId, Set contactIds) {
+      assertTrue("Customer#" + custId + " was in cache", listener.visited.contains("Customer#"
+               + custId));
+      for (Iterator it = contactIds.iterator(); it.hasNext();) {
+         Integer contactId = (Integer) it.next();
+         assertTrue("Contact#" + contactId + " was in cache", listener.visited.contains("Contact#"
+                  + contactId));
+         assertTrue("Contact#" + contactId + " was in cache", listener.visited.contains("Contact#"
+                  + contactId));
+      }
+      assertTrue("Customer.contacts" + custId + " was in cache", listener.visited
+               .contains("Customer.contacts#" + custId));
+   }
+
+   @Listener
+   public static class MyListener {
+      private static final Logger log = LoggerFactory.getLogger(MyListener.class);
+      private Set<String> visited = new ConcurrentSet<String>();
+      private final String name;
+      
+      public MyListener(String name) {
+         this.name = name;
+      }      
+
+      public void clear() {
+         visited.clear();
+      }
+      
+      public boolean isEmpty() {
+         return visited.isEmpty();
+      }
+
+      @CacheEntryVisited
+      public void nodeVisited(CacheEntryVisitedEvent event) {
+         log.debug(event.toString());
+         if (!event.isPre()) {
+            MarshalledValue mv = (MarshalledValue) event.getKey();
+            CacheKey cacheKey = (CacheKey) mv.get();
+            Integer primKey = (Integer) cacheKey.getKey();
+            String key = (String) cacheKey.getEntityOrRoleName() + '#' + primKey;
+            log.debug("MyListener[" + name +"] - Visiting key " + key);
+            // String name = fqn.toString();
+            String token = ".functional.";
+            int index = key.indexOf(token);
+            if (index > -1) {
+               index += token.length();
+               key = key.substring(index);
+               log.debug("MyListener[" + name +"] - recording visit to " + key);
+               visited.add(key);
+            }
+         }
+      }
+      
+//      @CacheEntryModified
+//      public void nodeModified(CacheEntryModifiedEvent event) {
+//         log.debug(event.toString());
+//         if (!event.isPre()) {
+//            MarshalledValue mv = (MarshalledValue) event.getKey();
+//            CacheKey cacheKey = (CacheKey) mv.get();
+//            Integer primKey = (Integer) cacheKey.getKey();
+//            String key = (String) cacheKey.getEntityOrRoleName() + '#' + primKey;
+//            log.debug("MyListener[" + name +"] - Modified key " + key);
+//            // String name = fqn.toString();
+//            String token = ".functional.";
+//            int index = key.indexOf(token);
+//            if (index > -1) {
+//               index += token.length();
+//               key = key.substring(index);
+//               log.debug("MyListener[" + name +"] - recording modification of " + key);
+//               visited.add(key);
+//            }
+//         }
+//      }
+   }
+
+   private class IdContainer {
+      Integer customerId;
+      Set<Integer> contactIds;
+   }
+
+}

Added: core/trunk/cache-infinispan/src/test/java/org/hibernate/test/cache/infinispan/functional/cluster/SessionRefreshTestCase.java
===================================================================
--- core/trunk/cache-infinispan/src/test/java/org/hibernate/test/cache/infinispan/functional/cluster/SessionRefreshTestCase.java	                        (rev 0)
+++ core/trunk/cache-infinispan/src/test/java/org/hibernate/test/cache/infinispan/functional/cluster/SessionRefreshTestCase.java	2009-08-21 08:39:33 UTC (rev 17386)
@@ -0,0 +1,131 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2009, Red Hat, Inc. and/or it's affiliates, 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.hibernate.test.cache.infinispan.functional.cluster;
+
+import javax.transaction.TransactionManager;
+
+import org.hibernate.SessionFactory;
+import org.hibernate.cfg.Configuration;
+import org.hibernate.cfg.Environment;
+import org.hibernate.test.cache.infinispan.functional.classloader.Account;
+import org.hibernate.test.cache.infinispan.functional.classloader.ClassLoaderTestDAO;
+import org.infinispan.Cache;
+import org.infinispan.manager.CacheManager;
+import org.infinispan.test.TestingUtil;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * SessionRefreshTestCase.
+ * 
+ * @author Galder Zamarreño
+ * @since 3.5
+ */
+public class SessionRefreshTestCase extends AbstractDualNodeTestCase {
+
+   public static final String OUR_PACKAGE = SessionRefreshTestCase.class.getPackage().getName();
+   
+   protected final Logger log = LoggerFactory.getLogger(getClass());
+
+   static int test = 0;
+   
+   private Cache localCache;
+
+   public SessionRefreshTestCase(String string) {
+      super(string);
+   }
+
+   protected String getEntityCacheConfigName() {
+      return "entity";
+   }
+   
+   /**
+    * Disables use of the second level cache for this session factory.
+    * 
+    * {@inheritDoc} 
+    */
+   @Override
+   protected void configureSecondNode(Configuration cfg) {
+      super.configureSecondNode(cfg);
+      cfg.setProperty(Environment.USE_SECOND_LEVEL_CACHE, "false");
+   }
+   
+   @Override
+   public String[] getMappings() {
+      return new String[] { "cache/infinispan/functional/classloader/Account.hbm.xml" };
+   }
+
+   @Override
+   protected void cleanupTransactionManagement() {
+      // Don't clean up the managers, just the transactions
+      // Managers are still needed by the long-lived caches
+      DualNodeJtaTransactionManagerImpl.cleanupTransactions();
+   }
+
+   public void testRefreshAfterExternalChange() throws Exception {
+      // First session factory uses a cache
+      CacheManager localManager = ClusterAwareRegionFactory.getCacheManager(AbstractDualNodeTestCase.LOCAL);
+      localCache = localManager.getCache(Account.class.getName());
+      TransactionManager localTM = DualNodeJtaTransactionManagerImpl.getInstance(AbstractDualNodeTestCase.LOCAL);
+      SessionFactory localFactory = getEnvironment().getSessionFactory();
+
+      // Second session factory doesn't; just needs a transaction manager
+      TransactionManager remoteTM = DualNodeJtaTransactionManagerImpl.getInstance(AbstractDualNodeTestCase.REMOTE);
+      SessionFactory remoteFactory = getSecondNodeEnvironment().getSessionFactory();
+
+      ClassLoaderTestDAO dao0 = new ClassLoaderTestDAO(localFactory, localTM);
+      ClassLoaderTestDAO dao1 = new ClassLoaderTestDAO(remoteFactory, remoteTM);
+
+      Integer id = new Integer(1);
+      dao0.createAccount(dao0.getSmith(), id, new Integer(5), AbstractDualNodeTestCase.LOCAL);
+
+      // Basic sanity check
+      Account acct1 = dao1.getAccount(id);
+      assertNotNull(acct1);
+      assertEquals(AbstractDualNodeTestCase.LOCAL, acct1.getBranch());
+
+      // This dao's session factory isn't caching, so cache won't see this change
+      dao1.updateAccountBranch(id, AbstractDualNodeTestCase.REMOTE);
+
+      // dao1's session doesn't touch the cache,
+      // so reading from dao0 should show a stale value from the cache
+      // (we check to confirm the cache is used)
+      Account acct0 = dao0.getAccount(id);
+      assertNotNull(acct0);
+      assertEquals(AbstractDualNodeTestCase.LOCAL, acct0.getBranch());
+      log.debug("Contents when re-reading from local: " + TestingUtil.printCache(localCache));
+
+      // Now call session.refresh and confirm we get the correct value
+      acct0 = dao0.getAccountWithRefresh(id);
+      assertNotNull(acct0);
+      assertEquals(AbstractDualNodeTestCase.REMOTE, acct0.getBranch());
+      log.debug("Contents after refreshing in remote: " + TestingUtil.printCache(localCache));
+
+      // Double check with a brand new session, in case the other session
+      // for some reason bypassed the 2nd level cache
+      ClassLoaderTestDAO dao0A = new ClassLoaderTestDAO(localFactory, localTM);
+      Account acct0A = dao0A.getAccount(id);
+      assertNotNull(acct0A);
+      assertEquals(AbstractDualNodeTestCase.REMOTE, acct0A.getBranch());
+      log.debug("Contents after creating a new session: " + TestingUtil.printCache(localCache));
+   }
+}

Added: core/trunk/cache-infinispan/src/test/java/org/hibernate/test/cache/infinispan/query/QueryRegionImplTestCase.java
===================================================================
--- core/trunk/cache-infinispan/src/test/java/org/hibernate/test/cache/infinispan/query/QueryRegionImplTestCase.java	                        (rev 0)
+++ core/trunk/cache-infinispan/src/test/java/org/hibernate/test/cache/infinispan/query/QueryRegionImplTestCase.java	2009-08-21 08:39:33 UTC (rev 17386)
@@ -0,0 +1,303 @@
+/*
+ * Hibernate, Relational Persistence for Idiomatic Java
+ *
+ * Copyright (c) 2007, Red Hat, Inc. and/or it's affiliates or third-party contributors as
+ * indicated by the @author tags or express copyright attribution
+ * statements applied by the authors.  All third-party contributions are
+ * distributed under license by Red Hat, Inc. and/or it's affiliates.
+ *
+ * This copyrighted material is made available to anyone wishing to use, modify,
+ * copy, or redistribute it subject to the terms and conditions of the GNU
+ * Lesser General Public License, as published by the Free Software Foundation.
+ *
+ * This program 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 distribution; if not, write to:
+ * Free Software Foundation, Inc.
+ * 51 Franklin Street, Fifth Floor
+ * Boston, MA  02110-1301  USA
+ */
+package org.hibernate.test.cache.infinispan.query;
+
+import java.util.Properties;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+import junit.framework.AssertionFailedError;
+
+import org.hibernate.cache.CacheDataDescription;
+import org.hibernate.cache.QueryResultsRegion;
+import org.hibernate.cache.Region;
+import org.hibernate.cache.StandardQueryCache;
+import org.hibernate.cache.infinispan.InfinispanRegionFactory;
+import org.hibernate.cfg.Configuration;
+import org.hibernate.test.cache.infinispan.AbstractGeneralDataRegionTestCase;
+import org.hibernate.test.cache.infinispan.util.CacheTestUtil;
+import org.infinispan.Cache;
+import org.infinispan.notifications.Listener;
+import org.infinispan.notifications.cachelistener.annotation.CacheEntryVisited;
+import org.infinispan.notifications.cachelistener.event.CacheEntryVisitedEvent;
+import org.infinispan.transaction.tm.BatchModeTransactionManager;
+import org.infinispan.util.concurrent.IsolationLevel;
+
+/**
+ * Tests of QueryResultRegionImpl.
+ * 
+ * @author Galder Zamarreño
+ * @since 3.5
+ */
+public class QueryRegionImplTestCase extends AbstractGeneralDataRegionTestCase {
+
+   // protected static final String REGION_NAME = "test/" + StandardQueryCache.class.getName();
+
+   /**
+    * Create a new EntityRegionImplTestCase.
+    * 
+    * @param name
+    */
+   public QueryRegionImplTestCase(String name) {
+      super(name);
+   }
+
+   @Override
+   protected Region createRegion(InfinispanRegionFactory regionFactory, String regionName, Properties properties, CacheDataDescription cdd) {
+      return regionFactory.buildQueryResultsRegion(regionName, properties);
+   }
+
+   @Override
+   protected String getStandardRegionName(String regionPrefix) {
+      return regionPrefix + "/" + StandardQueryCache.class.getName();
+   }
+
+   @Override
+   protected Cache getInfinispanCache(InfinispanRegionFactory regionFactory) {
+      return regionFactory.getCacheManager().getCache("local-query");
+   }
+   
+   @Override
+   protected Configuration createConfiguration() {
+      return CacheTestUtil.buildCustomQueryCacheConfiguration("test", "replicated-query");
+   }
+
+   public void testPutDoesNotBlockGet() throws Exception {
+      putDoesNotBlockGetTest();
+   }
+
+   private void putDoesNotBlockGetTest() throws Exception {
+      Configuration cfg = createConfiguration();
+      InfinispanRegionFactory regionFactory = CacheTestUtil.startRegionFactory(cfg, getCacheTestSupport());
+
+      // Sleep a bit to avoid concurrent FLUSH problem
+      avoidConcurrentFlush();
+
+      final QueryResultsRegion region = regionFactory.buildQueryResultsRegion(getStandardRegionName(REGION_PREFIX), cfg
+               .getProperties());
+
+      region.put(KEY, VALUE1);
+      assertEquals(VALUE1, region.get(KEY));
+
+      final CountDownLatch readerLatch = new CountDownLatch(1);
+      final CountDownLatch writerLatch = new CountDownLatch(1);
+      final CountDownLatch completionLatch = new CountDownLatch(1);
+      final ExceptionHolder holder = new ExceptionHolder();
+
+      Thread reader = new Thread() {
+         public void run() {
+            try {
+               BatchModeTransactionManager.getInstance().begin();
+               log.debug("Transaction began, get value for key");
+               assertTrue(VALUE2.equals(region.get(KEY)) == false);
+               BatchModeTransactionManager.getInstance().commit();
+            } catch (AssertionFailedError e) {
+               holder.a1 = e;
+               rollback();
+            } catch (Exception e) {
+               holder.e1 = e;
+               rollback();
+            } finally {
+               readerLatch.countDown();
+            }
+         }
+      };
+
+      Thread writer = new Thread() {
+         public void run() {
+            try {
+               BatchModeTransactionManager.getInstance().begin();
+               log.debug("Put value2");
+               region.put(KEY, VALUE2);
+               log.debug("Put finished for value2, await writer latch");
+               writerLatch.await();
+               log.debug("Writer latch finished");
+               BatchModeTransactionManager.getInstance().commit();
+               log.debug("Transaction committed");
+            } catch (Exception e) {
+               holder.e2 = e;
+               rollback();
+            } finally {
+               completionLatch.countDown();
+            }
+         }
+      };
+
+      reader.setDaemon(true);
+      writer.setDaemon(true);
+
+      writer.start();
+      assertFalse("Writer is blocking", completionLatch.await(100, TimeUnit.MILLISECONDS));
+
+      // Start the reader
+      reader.start();
+      assertTrue("Reader finished promptly", readerLatch.await(1000000000, TimeUnit.MILLISECONDS));
+
+      writerLatch.countDown();
+      assertTrue("Reader finished promptly", completionLatch.await(100, TimeUnit.MILLISECONDS));
+
+      assertEquals(VALUE2, region.get(KEY));
+
+      if (holder.a1 != null)
+         throw holder.a1;
+      else if (holder.a2 != null)
+         throw holder.a2;
+
+      assertEquals("writer saw no exceptions", null, holder.e1);
+      assertEquals("reader saw no exceptions", null, holder.e2);
+   }
+
+   public void testGetDoesNotBlockPut() throws Exception {
+      getDoesNotBlockPutTest();
+   }
+
+   // public void testGetDoesNotBlockPutPessimisticRepeatableRead() throws Exception {
+   // getDoesNotBlockPutTest();
+   // }
+
+   private void getDoesNotBlockPutTest() throws Exception {
+      Configuration cfg = createConfiguration();
+      InfinispanRegionFactory regionFactory = CacheTestUtil.startRegionFactory(cfg, getCacheTestSupport());
+
+      // Sleep a bit to avoid concurrent FLUSH problem
+      avoidConcurrentFlush();
+
+      final QueryResultsRegion region = regionFactory.buildQueryResultsRegion(getStandardRegionName(REGION_PREFIX), cfg
+               .getProperties());
+
+      region.put(KEY, VALUE1);
+      assertEquals(VALUE1, region.get(KEY));
+
+      // final Fqn rootFqn = getRegionFqn(getStandardRegionName(REGION_PREFIX), REGION_PREFIX);
+      final Cache jbc = getInfinispanCache(regionFactory);
+
+      final CountDownLatch blockerLatch = new CountDownLatch(1);
+      final CountDownLatch writerLatch = new CountDownLatch(1);
+      final CountDownLatch completionLatch = new CountDownLatch(1);
+      final ExceptionHolder holder = new ExceptionHolder();
+
+      Thread blocker = new Thread() {
+
+         public void run() {
+            // Fqn toBlock = new Fqn(rootFqn, KEY);
+            GetBlocker blocker = new GetBlocker(blockerLatch, KEY);
+            try {
+               jbc.addListener(blocker);
+
+               BatchModeTransactionManager.getInstance().begin();
+               region.get(KEY);
+               BatchModeTransactionManager.getInstance().commit();
+            } catch (Exception e) {
+               holder.e1 = e;
+               rollback();
+            } finally {
+               jbc.removeListener(blocker);
+            }
+         }
+      };
+
+      Thread writer = new Thread() {
+
+         public void run() {
+            try {
+               writerLatch.await();
+
+               BatchModeTransactionManager.getInstance().begin();
+               region.put(KEY, VALUE2);
+               BatchModeTransactionManager.getInstance().commit();
+            } catch (Exception e) {
+               holder.e2 = e;
+               rollback();
+            } finally {
+               completionLatch.countDown();
+            }
+         }
+      };
+
+      blocker.setDaemon(true);
+      writer.setDaemon(true);
+
+      boolean unblocked = false;
+      try {
+         blocker.start();
+         writer.start();
+
+         assertFalse("Blocker is blocking", completionLatch.await(100, TimeUnit.MILLISECONDS));
+         // Start the writer
+         writerLatch.countDown();
+         assertTrue("Writer finished promptly", completionLatch.await(100, TimeUnit.MILLISECONDS));
+
+         blockerLatch.countDown();
+         unblocked = true;
+
+         if (IsolationLevel.REPEATABLE_READ.equals(jbc.getConfiguration().getIsolationLevel())) {
+            assertEquals(VALUE1, region.get(KEY));
+         } else {
+            assertEquals(VALUE2, region.get(KEY));
+         }
+
+         if (holder.a1 != null)
+            throw holder.a1;
+         else if (holder.a2 != null)
+            throw holder.a2;
+
+         assertEquals("blocker saw no exceptions", null, holder.e1);
+         assertEquals("writer saw no exceptions", null, holder.e2);
+      } finally {
+         if (!unblocked)
+            blockerLatch.countDown();
+      }
+   }
+
+   @Listener
+   public class GetBlocker {
+
+      private CountDownLatch latch;
+      // private Fqn fqn;
+      private Object key;
+
+      GetBlocker(CountDownLatch latch, Object key) {
+         this.latch = latch;
+         this.key = key;
+      }
+
+      @CacheEntryVisited
+      public void nodeVisisted(CacheEntryVisitedEvent event) {
+         if (event.isPre() && event.getKey().equals(key)) {
+            try {
+               latch.await();
+            } catch (InterruptedException e) {
+               log.error("Interrupted waiting for latch", e);
+            }
+         }
+      }
+   }
+
+   private class ExceptionHolder {
+      Exception e1;
+      Exception e2;
+      AssertionFailedError a1;
+      AssertionFailedError a2;
+   }
+}

Added: core/trunk/cache-infinispan/src/test/java/org/hibernate/test/cache/infinispan/timestamp/TimestampsRegionImplTestCase.java
===================================================================
--- core/trunk/cache-infinispan/src/test/java/org/hibernate/test/cache/infinispan/timestamp/TimestampsRegionImplTestCase.java	                        (rev 0)
+++ core/trunk/cache-infinispan/src/test/java/org/hibernate/test/cache/infinispan/timestamp/TimestampsRegionImplTestCase.java	2009-08-21 08:39:33 UTC (rev 17386)
@@ -0,0 +1,62 @@
+/*
+ * Hibernate, Relational Persistence for Idiomatic Java
+ *
+ * Copyright (c) 2007, Red Hat, Inc. and/or it's affiliates or third-party contributors as
+ * indicated by the @author tags or express copyright attribution
+ * statements applied by the authors.  All third-party contributions are
+ * distributed under license by Red Hat, Inc. and/or it's affiliates.
+ *
+ * This copyrighted material is made available to anyone wishing to use, modify,
+ * copy, or redistribute it subject to the terms and conditions of the GNU
+ * Lesser General Public License, as published by the Free Software Foundation.
+ *
+ * This program 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 distribution; if not, write to:
+ * Free Software Foundation, Inc.
+ * 51 Franklin Street, Fifth Floor
+ * Boston, MA  02110-1301  USA
+ */
+package org.hibernate.test.cache.infinispan.timestamp;
+
+import java.util.Properties;
+
+import org.hibernate.cache.CacheDataDescription;
+import org.hibernate.cache.Region;
+import org.hibernate.cache.UpdateTimestampsCache;
+import org.hibernate.cache.infinispan.InfinispanRegionFactory;
+import org.hibernate.test.cache.infinispan.AbstractGeneralDataRegionTestCase;
+import org.infinispan.Cache;
+
+/**
+ * Tests of TimestampsRegionImpl.
+ * 
+ * @author Galder Zamarreño
+ * @since 3.5
+ */
+public class TimestampsRegionImplTestCase extends AbstractGeneralDataRegionTestCase {
+    
+    public TimestampsRegionImplTestCase(String name) {
+        super(name);
+    }
+
+    @Override
+    protected String getStandardRegionName(String regionPrefix) {
+        return regionPrefix + "/" + UpdateTimestampsCache.class.getName();
+    }
+
+   @Override
+   protected Region createRegion(InfinispanRegionFactory regionFactory, String regionName, Properties properties, CacheDataDescription cdd) {
+      return regionFactory.buildTimestampsRegion(regionName, properties);
+   }
+
+   @Override
+   protected Cache getInfinispanCache(InfinispanRegionFactory regionFactory) {
+      return regionFactory.getCacheManager().getCache("timestamps");
+   }
+
+}

Added: core/trunk/cache-infinispan/src/test/java/org/hibernate/test/cache/infinispan/tm/XaConnectionProvider.java
===================================================================
--- core/trunk/cache-infinispan/src/test/java/org/hibernate/test/cache/infinispan/tm/XaConnectionProvider.java	                        (rev 0)
+++ core/trunk/cache-infinispan/src/test/java/org/hibernate/test/cache/infinispan/tm/XaConnectionProvider.java	2009-08-21 08:39:33 UTC (rev 17386)
@@ -0,0 +1,78 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2009, Red Hat, Inc. and/or it's affiliates, 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.hibernate.test.cache.infinispan.tm;
+
+import java.sql.Connection;
+import java.sql.SQLException;
+import java.util.Properties;
+
+import org.hibernate.HibernateException;
+import org.hibernate.connection.ConnectionProvider;
+import org.hibernate.connection.ConnectionProviderFactory;
+
+/**
+ * XaConnectionProvider.
+ * 
+ * @author Galder Zamarreño
+ * @since 3.5
+ */
+public class XaConnectionProvider implements ConnectionProvider {
+   private static ConnectionProvider actualConnectionProvider = ConnectionProviderFactory.newConnectionProvider();
+   private boolean isTransactional;
+
+   public static ConnectionProvider getActualConnectionProvider() {
+      return actualConnectionProvider;
+   }
+
+   public void configure(Properties props) throws HibernateException {
+   }
+
+   public Connection getConnection() throws SQLException {
+      XaTransactionImpl currentTransaction = XaTransactionManagerImpl.getInstance().getCurrentTransaction();
+      if (currentTransaction == null) {
+         isTransactional = false;
+         return actualConnectionProvider.getConnection();
+      } else {
+         isTransactional = true;
+         Connection connection = currentTransaction.getEnlistedConnection();
+         if (connection == null) {
+            connection = actualConnectionProvider.getConnection();
+            currentTransaction.enlistConnection(connection);
+         }
+         return connection;
+      }
+   }
+
+   public void closeConnection(Connection conn) throws SQLException {
+      if (!isTransactional) {
+         conn.close();
+      }
+   }
+
+   public void close() throws HibernateException {
+      actualConnectionProvider.close();
+   }
+
+   public boolean supportsAggressiveRelease() {
+      return true;
+   }
+}

Added: core/trunk/cache-infinispan/src/test/java/org/hibernate/test/cache/infinispan/tm/XaTransactionImpl.java
===================================================================
--- core/trunk/cache-infinispan/src/test/java/org/hibernate/test/cache/infinispan/tm/XaTransactionImpl.java	                        (rev 0)
+++ core/trunk/cache-infinispan/src/test/java/org/hibernate/test/cache/infinispan/tm/XaTransactionImpl.java	2009-08-21 08:39:33 UTC (rev 17386)
@@ -0,0 +1,247 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2009, Red Hat, Inc. and/or it's affiliates, 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.hibernate.test.cache.infinispan.tm;
+
+import java.sql.Connection;
+import java.sql.SQLException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import javax.transaction.HeuristicMixedException;
+import javax.transaction.HeuristicRollbackException;
+import javax.transaction.RollbackException;
+import javax.transaction.Status;
+import javax.transaction.Synchronization;
+import javax.transaction.SystemException;
+import javax.transaction.Transaction;
+import javax.transaction.xa.XAException;
+import javax.transaction.xa.XAResource;
+import javax.transaction.xa.Xid;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * XaResourceCapableTransactionImpl.
+ * 
+ * @author Galder Zamarreño
+ * @since 3.5
+ */
+public class XaTransactionImpl implements Transaction {
+   private static final Logger log = LoggerFactory.getLogger(XaTransactionImpl.class);
+   private int status;
+   private LinkedList synchronizations;
+   private Connection connection; // the only resource we care about is jdbc connection
+   private final XaTransactionManagerImpl jtaTransactionManager;
+   private List<XAResource> enlistedResources = new ArrayList<XAResource>();
+   private Xid xid = new XaResourceCapableTransactionXid();
+
+   public XaTransactionImpl(XaTransactionManagerImpl jtaTransactionManager) {
+      this.jtaTransactionManager = jtaTransactionManager;
+      this.status = Status.STATUS_ACTIVE;
+   }
+
+   public int getStatus() {
+      return status;
+   }
+
+   public void commit() throws RollbackException, HeuristicMixedException, HeuristicRollbackException,
+            IllegalStateException, SystemException {
+
+      if (status == Status.STATUS_MARKED_ROLLBACK) {
+         log.trace("on commit, status was marked for rollback-only");
+         rollback();
+      } else {
+         status = Status.STATUS_PREPARING;
+
+         for (int i = 0; i < synchronizations.size(); i++) {
+            Synchronization s = (Synchronization) synchronizations.get(i);
+            s.beforeCompletion();
+         }
+
+//         if (!runXaResourcePrepare()) {
+//            status = Status.STATUS_ROLLING_BACK;
+//         } else {
+//            status = Status.STATUS_PREPARED;
+//         }
+
+         status = Status.STATUS_COMMITTING;
+
+         if (connection != null) {
+            try {
+               connection.commit();
+               connection.close();
+            } catch (SQLException sqle) {
+               status = Status.STATUS_UNKNOWN;
+               throw new SystemException();
+            }
+         }
+
+//         runXaResourceCommitTx();
+
+         status = Status.STATUS_COMMITTED;
+
+         for (int i = 0; i < synchronizations.size(); i++) {
+            Synchronization s = (Synchronization) synchronizations.get(i);
+            s.afterCompletion(status);
+         }
+
+         // status = Status.STATUS_NO_TRANSACTION;
+         jtaTransactionManager.endCurrent(this);
+      }
+   }
+
+   public void rollback() throws IllegalStateException, SystemException {
+//      status = Status.STATUS_ROLLING_BACK;
+//      runXaResourceRollback();
+      status = Status.STATUS_ROLLEDBACK;
+
+      if (connection != null) {
+         try {
+            connection.rollback();
+            connection.close();
+         } catch (SQLException sqle) {
+            status = Status.STATUS_UNKNOWN;
+            throw new SystemException();
+         }
+      }
+
+      for (int i = 0; i < synchronizations.size(); i++) {
+         Synchronization s = (Synchronization) synchronizations.get(i);
+         s.afterCompletion(status);
+      }
+
+      // status = Status.STATUS_NO_TRANSACTION;
+      jtaTransactionManager.endCurrent(this);
+   }
+
+   public void setRollbackOnly() throws IllegalStateException, SystemException {
+      status = Status.STATUS_MARKED_ROLLBACK;
+   }
+
+   public void registerSynchronization(Synchronization synchronization) throws RollbackException,
+            IllegalStateException, SystemException {
+      // todo : find the spec-allowable statuses during which synch can be registered...
+      if (synchronizations == null) {
+         synchronizations = new LinkedList();
+      }
+      synchronizations.add(synchronization);
+   }
+
+   public void enlistConnection(Connection connection) {
+      if (this.connection != null) {
+         throw new IllegalStateException("Connection already registered");
+      }
+      this.connection = connection;
+   }
+
+   public Connection getEnlistedConnection() {
+      return connection;
+   }
+
+   public boolean enlistResource(XAResource xaResource) throws RollbackException, IllegalStateException,
+            SystemException {
+      enlistedResources.add(xaResource);
+      try {
+         xaResource.start(xid, 0);
+      } catch (XAException e) {
+         log.error("Got an exception", e);
+         throw new SystemException(e.getMessage());
+      }
+      return true;
+   }
+
+   public boolean delistResource(XAResource xaResource, int i) throws IllegalStateException, SystemException {
+      throw new SystemException("not supported");
+   }
+   
+   public Collection<XAResource> getEnlistedResources() {
+      return enlistedResources;
+   }
+   
+   private boolean runXaResourcePrepare() throws SystemException {
+      Collection<XAResource> resources = getEnlistedResources();
+      for (XAResource res : resources) {
+         try {
+            res.prepare(xid);
+         } catch (XAException e) {
+            log.trace("The resource wants to rollback!", e);
+            return false;
+         } catch (Throwable th) {
+            log.error("Unexpected error from resource manager!", th);
+            throw new SystemException(th.getMessage());
+         }
+      }
+      return true;
+   }
+   
+   private void runXaResourceRollback() {
+      Collection<XAResource> resources = getEnlistedResources();
+      for (XAResource res : resources) {
+         try {
+            res.rollback(xid);
+         } catch (XAException e) {
+            log.warn("Error while rolling back",e);
+         }
+      }
+   }
+
+   private boolean runXaResourceCommitTx() throws HeuristicMixedException {
+      Collection<XAResource> resources = getEnlistedResources();
+      for (XAResource res : resources) {
+         try {
+            res.commit(xid, false);//todo we only support one phase commit for now, change this!!!
+         } catch (XAException e) {
+            log.warn("exception while committing",e);
+            throw new HeuristicMixedException(e.getMessage());
+         }
+      }
+      return true;
+   }
+   
+   private static class XaResourceCapableTransactionXid implements Xid {
+      private static AtomicInteger txIdCounter = new AtomicInteger(0);
+      private int id = txIdCounter.incrementAndGet();
+
+      public int getFormatId() {
+         return id;
+      }
+
+      public byte[] getGlobalTransactionId() {
+         throw new IllegalStateException("TODO - please implement me!!!"); //todo implement!!!
+      }
+
+      public byte[] getBranchQualifier() {
+         throw new IllegalStateException("TODO - please implement me!!!"); //todo implement!!!
+      }
+
+      @Override
+      public String toString() {
+         return getClass().getSimpleName() + "{" +
+               "id=" + id +
+               '}';
+      }
+   }
+}

Added: core/trunk/cache-infinispan/src/test/java/org/hibernate/test/cache/infinispan/tm/XaTransactionManagerImpl.java
===================================================================
--- core/trunk/cache-infinispan/src/test/java/org/hibernate/test/cache/infinispan/tm/XaTransactionManagerImpl.java	                        (rev 0)
+++ core/trunk/cache-infinispan/src/test/java/org/hibernate/test/cache/infinispan/tm/XaTransactionManagerImpl.java	2009-08-21 08:39:33 UTC (rev 17386)
@@ -0,0 +1,105 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2009, Red Hat, Inc. and/or it's affiliates, 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.hibernate.test.cache.infinispan.tm;
+
+import javax.transaction.HeuristicMixedException;
+import javax.transaction.HeuristicRollbackException;
+import javax.transaction.InvalidTransactionException;
+import javax.transaction.NotSupportedException;
+import javax.transaction.RollbackException;
+import javax.transaction.Status;
+import javax.transaction.SystemException;
+import javax.transaction.Transaction;
+import javax.transaction.TransactionManager;
+
+/**
+ * XaResourceCapableTransactionManagerImpl.
+ * 
+ * @author Galder Zamarreño
+ * @since 3.5
+ */
+public class XaTransactionManagerImpl implements TransactionManager {
+   private static final XaTransactionManagerImpl INSTANCE = new XaTransactionManagerImpl();
+   private XaTransactionImpl currentTransaction;
+
+   public static XaTransactionManagerImpl getInstance() {
+      return INSTANCE;
+   }
+
+   public int getStatus() throws SystemException {
+      return currentTransaction == null ? Status.STATUS_NO_TRANSACTION : currentTransaction.getStatus();
+   }
+
+   public Transaction getTransaction() throws SystemException {
+      return currentTransaction;
+   }
+
+   public XaTransactionImpl getCurrentTransaction() {
+      return currentTransaction;
+   }
+
+   public void begin() throws NotSupportedException, SystemException {
+      currentTransaction = new XaTransactionImpl(this);
+   }
+
+   public Transaction suspend() throws SystemException {
+      Transaction suspended = currentTransaction;
+      currentTransaction = null;
+      return suspended;
+   }
+
+   public void resume(Transaction transaction) throws InvalidTransactionException, IllegalStateException,
+            SystemException {
+      currentTransaction = (XaTransactionImpl) transaction;
+   }
+
+   public void commit() throws RollbackException, HeuristicMixedException, HeuristicRollbackException,
+            SecurityException, IllegalStateException, SystemException {
+      if (currentTransaction == null) {
+         throw new IllegalStateException("no current transaction to commit");
+      }
+      currentTransaction.commit();
+   }
+
+   public void rollback() throws IllegalStateException, SecurityException, SystemException {
+      if (currentTransaction == null) {
+         throw new IllegalStateException("no current transaction");
+      }
+      currentTransaction.rollback();
+   }
+
+   public void setRollbackOnly() throws IllegalStateException, SystemException {
+      if (currentTransaction == null) {
+         throw new IllegalStateException("no current transaction");
+      }
+      currentTransaction.setRollbackOnly();
+   }
+
+   public void setTransactionTimeout(int i) throws SystemException {
+   }
+
+   void endCurrent(Transaction transaction) {
+      if (transaction == currentTransaction) {
+         currentTransaction = null;
+      }
+   }
+}

Added: core/trunk/cache-infinispan/src/test/java/org/hibernate/test/cache/infinispan/tm/XaTransactionManagerLookup.java
===================================================================
--- core/trunk/cache-infinispan/src/test/java/org/hibernate/test/cache/infinispan/tm/XaTransactionManagerLookup.java	                        (rev 0)
+++ core/trunk/cache-infinispan/src/test/java/org/hibernate/test/cache/infinispan/tm/XaTransactionManagerLookup.java	2009-08-21 08:39:33 UTC (rev 17386)
@@ -0,0 +1,52 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2009, Red Hat, Inc. and/or it's affiliates, 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.hibernate.test.cache.infinispan.tm;
+
+import java.util.Properties;
+
+import javax.transaction.Transaction;
+import javax.transaction.TransactionManager;
+
+import org.hibernate.HibernateException;
+import org.hibernate.transaction.TransactionManagerLookup;
+
+/**
+ * XaResourceCapableTransactionManagerLookup.
+ * 
+ * @author Galder Zamarreño
+ * @since 3.5
+ */
+public class XaTransactionManagerLookup implements TransactionManagerLookup {
+
+   public Object getTransactionIdentifier(Transaction transaction) {
+      return transaction;
+   }
+
+   public TransactionManager getTransactionManager(Properties props) throws HibernateException {
+      return XaTransactionManagerImpl.getInstance();
+   }
+
+   public String getUserTransactionName() {
+      throw new UnsupportedOperationException( "jndi currently not implemented for these tests" );
+   }
+
+}

Added: core/trunk/cache-infinispan/src/test/java/org/hibernate/test/cache/infinispan/util/BatchModeTransactionManagerLookup.java
===================================================================
--- core/trunk/cache-infinispan/src/test/java/org/hibernate/test/cache/infinispan/util/BatchModeTransactionManagerLookup.java	                        (rev 0)
+++ core/trunk/cache-infinispan/src/test/java/org/hibernate/test/cache/infinispan/util/BatchModeTransactionManagerLookup.java	2009-08-21 08:39:33 UTC (rev 17386)
@@ -0,0 +1,61 @@
+/*
+ * Hibernate, Relational Persistence for Idiomatic Java
+ *
+ * Copyright (c) 2007, Red Hat, Inc. and/or it's affiliates or third-party contributors as
+ * indicated by the @author tags or express copyright attribution
+ * statements applied by the authors.  All third-party contributions are
+ * distributed under license by Red Hat, Inc. and/or it's affiliates.
+ *
+ * This copyrighted material is made available to anyone wishing to use, modify,
+ * copy, or redistribute it subject to the terms and conditions of the GNU
+ * Lesser General Public License, as published by the Free Software Foundation.
+ *
+ * This program 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 distribution; if not, write to:
+ * Free Software Foundation, Inc.
+ * 51 Franklin Street, Fifth Floor
+ * Boston, MA  02110-1301  USA
+ */
+package org.hibernate.test.cache.infinispan.util;
+
+import java.util.Properties;
+
+import javax.transaction.TransactionManager;
+import javax.transaction.Transaction;
+
+import org.hibernate.HibernateException;
+import org.hibernate.transaction.TransactionManagerLookup;
+import org.infinispan.transaction.tm.BatchModeTransactionManager;
+
+/**
+ * Uses the JBoss Cache BatchModeTransactionManager. Should not be used in
+ * any tests that simulate usage of database connections.
+ * 
+ * @author Brian Stansberry
+ */
+public class BatchModeTransactionManagerLookup
+    implements TransactionManagerLookup {
+
+    public TransactionManager getTransactionManager(Properties props) throws HibernateException {
+        try {
+            return BatchModeTransactionManager.getInstance();
+        }
+        catch (Exception e) {
+            throw new HibernateException("Failed getting BatchModeTransactionManager", e);
+        }
+    }
+
+    public String getUserTransactionName() {
+        throw new UnsupportedOperationException();
+    }
+
+	public Object getTransactionIdentifier(Transaction transaction) {
+		return transaction;
+	}
+
+}


Property changes on: core/trunk/cache-infinispan/src/test/java/org/hibernate/test/cache/infinispan/util/BatchModeTransactionManagerLookup.java
___________________________________________________________________
Name: svn:executable
   + *

Added: core/trunk/cache-infinispan/src/test/java/org/hibernate/test/cache/infinispan/util/CacheTestSupport.java
===================================================================
--- core/trunk/cache-infinispan/src/test/java/org/hibernate/test/cache/infinispan/util/CacheTestSupport.java	                        (rev 0)
+++ core/trunk/cache-infinispan/src/test/java/org/hibernate/test/cache/infinispan/util/CacheTestSupport.java	2009-08-21 08:39:33 UTC (rev 17386)
@@ -0,0 +1,151 @@
+/*
+ * Hibernate, Relational Persistence for Idiomatic Java
+ *
+ * Copyright (c) 2007, Red Hat, Inc. and/or it's affiliates or third-party contributors as
+ * indicated by the @author tags or express copyright attribution
+ * statements applied by the authors.  All third-party contributions are
+ * distributed under license by Red Hat, Inc. and/or it's affiliates.
+ *
+ * This copyrighted material is made available to anyone wishing to use, modify,
+ * copy, or redistribute it subject to the terms and conditions of the GNU
+ * Lesser General Public License, as published by the Free Software Foundation.
+ *
+ * This program 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 distribution; if not, write to:
+ * Free Software Foundation, Inc.
+ * 51 Franklin Street, Fifth Floor
+ * Boston, MA  02110-1301  USA
+ */
+package org.hibernate.test.cache.infinispan.util;
+
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Set;
+
+import org.slf4j.Logger;
+
+import org.hibernate.cache.RegionFactory;
+import org.infinispan.Cache;
+
+/**
+ * Support class for tracking and cleaning up objects used in tests.
+ * 
+ * @author <a href="brian.stansberry at jboss.com">Brian Stansberry</a>
+ */
+public class CacheTestSupport {
+    
+    private static final String PREFER_IPV4STACK = "java.net.preferIPv4Stack";
+    
+    private Logger log;
+    
+    private Set<Cache> caches = new HashSet();
+    private Set<RegionFactory> factories = new HashSet();
+    private Exception exception;
+    private String preferIPv4Stack;
+ 
+    public CacheTestSupport(Logger log) {
+       this.log = log;
+    }
+    
+    public void registerCache(Cache cache) {
+        caches.add(cache);
+    }
+    
+    public void registerFactory(RegionFactory factory) {
+        factories.add(factory);
+    }
+ 
+    public void unregisterCache(Cache cache) {
+        caches.remove(cache);
+    }
+    
+    public void unregisterFactory(RegionFactory factory) {
+        factories.remove(factory);
+    }
+    
+    public void setUp() throws Exception {   
+        
+        // Try to ensure we use IPv4; otherwise cluster formation is very slow 
+        preferIPv4Stack = System.getProperty(PREFER_IPV4STACK);
+        System.setProperty(PREFER_IPV4STACK, "true");
+
+        cleanUp();
+        throwStoredException();
+    }
+
+    public void tearDown() throws Exception {       
+        
+        if (preferIPv4Stack == null)
+            System.clearProperty(PREFER_IPV4STACK);
+        else 
+            System.setProperty(PREFER_IPV4STACK, preferIPv4Stack);
+        
+        cleanUp();
+        throwStoredException();
+    }
+    
+    public void avoidConcurrentFlush() {
+       // JG 2.6.1 has a problem where calling flush more than once too quickly
+       // can result in several second delays
+       sleep(100);
+    }
+    
+    private void sleep(long ms) {
+        try {
+            Thread.sleep(ms);
+        }
+        catch (InterruptedException e) {
+            log.warn("Interrupted during sleep", e);
+        }
+    }
+
+    private void cleanUp() {
+        for (Iterator it = factories.iterator(); it.hasNext(); ) {
+            try {
+                ((RegionFactory) it.next()).stop();
+            }
+            catch (Exception e) {
+                storeException(e);
+            }
+            finally {
+                it.remove();
+            }
+        }        
+        factories.clear();
+        
+        for (Iterator it = caches.iterator(); it.hasNext(); ) {
+            try {
+                Cache cache = (Cache) it.next();
+                cache.stop();
+            }
+            catch (Exception e) {
+                storeException(e);
+            }
+            finally {
+                it.remove();
+            }
+            avoidConcurrentFlush();
+        }        
+        caches.clear();
+    }
+    
+    private void storeException(Exception e) {
+        if (this.exception == null) {
+            this.exception = e;
+        }
+    }
+    
+    private void throwStoredException() throws Exception {
+        if (exception != null) {
+            Exception toThrow = exception;
+            exception = null;
+            throw toThrow;
+        }
+    }
+
+}

Added: core/trunk/cache-infinispan/src/test/java/org/hibernate/test/cache/infinispan/util/CacheTestUtil.java
===================================================================
--- core/trunk/cache-infinispan/src/test/java/org/hibernate/test/cache/infinispan/util/CacheTestUtil.java	                        (rev 0)
+++ core/trunk/cache-infinispan/src/test/java/org/hibernate/test/cache/infinispan/util/CacheTestUtil.java	2009-08-21 08:39:33 UTC (rev 17386)
@@ -0,0 +1,145 @@
+/*
+ * Hibernate, Relational Persistence for Idiomatic Java
+ *
+ * Copyright (c) 2007, Red Hat, Inc. and/or it's affiliates or third-party contributors as
+ * indicated by the @author tags or express copyright attribution
+ * statements applied by the authors.  All third-party contributions are
+ * distributed under license by Red Hat, Inc. and/or it's affiliates.
+ *
+ * This copyrighted material is made available to anyone wishing to use, modify,
+ * copy, or redistribute it subject to the terms and conditions of the GNU
+ * Lesser General Public License, as published by the Free Software Foundation.
+ *
+ * This program 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 distribution; if not, write to:
+ * Free Software Foundation, Inc.
+ * 51 Franklin Street, Fifth Floor
+ * Boston, MA  02110-1301  USA
+ */
+package org.hibernate.test.cache.infinispan.util;
+
+import java.util.Enumeration;
+import java.util.HashSet;
+import java.util.Properties;
+import java.util.Set;
+
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+
+import org.hibernate.cache.infinispan.InfinispanRegionFactory;
+import org.hibernate.cfg.Configuration;
+import org.hibernate.cfg.Environment;
+import org.hibernate.cfg.Settings;
+
+/**
+ * Utilities for cache testing.
+ * 
+ * @author <a href="brian.stansberry at jboss.com">Brian Stansberry</a>
+ */
+public class CacheTestUtil {
+
+   public static Configuration buildConfiguration(String regionPrefix, Class regionFactory, boolean use2ndLevel, boolean useQueries) {
+      Configuration cfg = new Configuration();
+      cfg.setProperty(Environment.GENERATE_STATISTICS, "true");
+      cfg.setProperty(Environment.USE_STRUCTURED_CACHE, "true");
+      cfg.setProperty(Environment.TRANSACTION_MANAGER_STRATEGY, BatchModeTransactionManagerLookup.class.getName());
+
+      cfg.setProperty(Environment.CACHE_REGION_FACTORY, regionFactory.getName());
+      cfg.setProperty(Environment.CACHE_REGION_PREFIX, regionPrefix);
+      cfg.setProperty(Environment.USE_SECOND_LEVEL_CACHE, String.valueOf(use2ndLevel));
+      cfg.setProperty(Environment.USE_QUERY_CACHE, String.valueOf(useQueries));
+
+      return cfg;
+   }
+
+   public static Configuration buildLocalOnlyConfiguration(String regionPrefix, boolean use2ndLevel, boolean useQueries) {
+      Configuration cfg = buildConfiguration(regionPrefix, InfinispanRegionFactory.class, use2ndLevel, useQueries);
+      cfg.setProperty(InfinispanRegionFactory.INFINISPAN_CONFIG_RESOURCE_PROP,
+               InfinispanRegionFactory.DEF_INFINISPAN_CONFIG_RESOURCE);
+      return cfg;
+   }
+   
+   public static Configuration buildCustomQueryCacheConfiguration(String regionPrefix, String queryCacheName) {
+      Configuration cfg = buildConfiguration(regionPrefix, InfinispanRegionFactory.class, true, true);
+      cfg.setProperty(InfinispanRegionFactory.QUERY_CACHE_RESOURCE_PROP, queryCacheName);
+      return cfg;
+   }
+
+   public static InfinispanRegionFactory startRegionFactory(Configuration cfg) throws ClassNotFoundException,
+            InstantiationException, IllegalAccessException {
+
+      Settings settings = cfg.buildSettings();
+      Properties properties = cfg.getProperties();
+
+      String factoryType = cfg.getProperty(Environment.CACHE_REGION_FACTORY);
+      Class factoryClass = Thread.currentThread().getContextClassLoader().loadClass(factoryType);
+      InfinispanRegionFactory regionFactory = (InfinispanRegionFactory) factoryClass.newInstance();
+
+      regionFactory.start(settings, properties);
+
+      return regionFactory;
+   }
+
+   public static InfinispanRegionFactory startRegionFactory(Configuration cfg, CacheTestSupport testSupport)
+            throws ClassNotFoundException, InstantiationException, IllegalAccessException {
+      InfinispanRegionFactory factory = startRegionFactory(cfg);
+      testSupport.registerFactory(factory);
+      return factory;
+   }
+
+   public static void stopRegionFactory(InfinispanRegionFactory factory, CacheTestSupport testSupport) {
+      factory.stop();
+      testSupport.unregisterFactory(factory);
+   }
+
+   /**
+    * Supports easy creation of a TestSuite where a subclass' "FailureExpected" version of a base
+    * test is included in the suite, while the base test is excluded. E.g. test class FooTestCase
+    * includes method testBar(), while test class SubFooTestCase extends FooTestCase includes method
+    * testBarFailureExcluded(). Passing SubFooTestCase.class to this method will return a suite that
+    * does not include testBar().
+    * 
+    * FIXME Move this to UnitTestCase
+    */
+   public static TestSuite createFailureExpectedSuite(Class testClass) {
+
+      TestSuite allTests = new TestSuite(testClass);
+      Set failureExpected = new HashSet();
+      Enumeration tests = allTests.tests();
+      while (tests.hasMoreElements()) {
+         Test t = (Test) tests.nextElement();
+         if (t instanceof TestCase) {
+            String name = ((TestCase) t).getName();
+            if (name.endsWith("FailureExpected"))
+               failureExpected.add(name);
+         }
+      }
+
+      TestSuite result = new TestSuite();
+      tests = allTests.tests();
+      while (tests.hasMoreElements()) {
+         Test t = (Test) tests.nextElement();
+         if (t instanceof TestCase) {
+            String name = ((TestCase) t).getName();
+            if (!failureExpected.contains(name + "FailureExpected")) {
+               result.addTest(t);
+            }
+         }
+      }
+
+      return result;
+   }
+
+   /**
+    * Prevent instantiation.
+    */
+   private CacheTestUtil() {
+   }
+
+}

Added: core/trunk/cache-infinispan/src/test/resources/hibernate.properties
===================================================================
--- core/trunk/cache-infinispan/src/test/resources/hibernate.properties	                        (rev 0)
+++ core/trunk/cache-infinispan/src/test/resources/hibernate.properties	2009-08-21 08:39:33 UTC (rev 17386)
@@ -0,0 +1,40 @@
+################################################################################
+# Hibernate, Relational Persistence for Idiomatic Java                         #
+#                                                                              #
+# Copyright (c) 2007, Red Hat, Inc. and/or it's affiliates or third-party contributors as    #
+# indicated by the @author tags or express copyright attribution               #
+# statements applied by the authors.  All third-party contributions are        #
+# distributed under license by Red Hat, Inc. and/or it's affiliates.                         #
+#                                                                              #
+# This copyrighted material is made available to anyone wishing to use, modify,#
+# copy, or redistribute it subject to the terms and conditions of the GNU      #
+# Lesser General Public License, as published by the Free Software Foundation. #
+#                                                                              #
+# This program 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 distribution; if not, write to:                              #
+# Free Software Foundation, Inc.                                               #
+# 51 Franklin Street, Fifth Floor                                              #
+# Boston, MA  02110-1301  USA                                                  #
+################################################################################
+hibernate.dialect org.hibernate.dialect.HSQLDialect
+hibernate.connection.driver_class org.hsqldb.jdbcDriver
+hibernate.connection.url jdbc:hsqldb:mem:/test
+hibernate.connection.username sa
+hibernate.connection.password
+
+hibernate.connection.pool_size 5
+
+hibernate.format_sql true
+
+hibernate.max_fetch_depth 5
+
+hibernate.generate_statistics true
+
+hibernate.cache.use_second_level_cache true
+hibernate.cache.use_query_cache true
+hibernate.cache.region.factory_class org.hibernate.cache.infinispan.InfinispanRegionFactory
\ No newline at end of file


Property changes on: core/trunk/cache-infinispan/src/test/resources/hibernate.properties
___________________________________________________________________
Name: svn:executable
   + *

Added: core/trunk/cache-infinispan/src/test/resources/log4j.properties
===================================================================
--- core/trunk/cache-infinispan/src/test/resources/log4j.properties	                        (rev 0)
+++ core/trunk/cache-infinispan/src/test/resources/log4j.properties	2009-08-21 08:39:33 UTC (rev 17386)
@@ -0,0 +1,37 @@
+################################################################################
+# Hibernate, Relational Persistence for Idiomatic Java                         #
+#                                                                              #
+# Copyright (c) 2007, Red Hat, Inc. and/or it's affiliates or third-party contributors as    #
+# indicated by the @author tags or express copyright attribution               #
+# statements applied by the authors.  All third-party contributions are        #
+# distributed under license by Red Hat, Inc. and/or it's affiliates.                         #
+#                                                                              #
+# This copyrighted material is made available to anyone wishing to use, modify,#
+# copy, or redistribute it subject to the terms and conditions of the GNU      #
+# Lesser General Public License, as published by the Free Software Foundation. #
+#                                                                              #
+# This program 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 distribution; if not, write to:                              #
+# Free Software Foundation, Inc.                                               #
+# 51 Franklin Street, Fifth Floor                                              #
+# Boston, MA  02110-1301  USA                                                  #
+################################################################################
+log4j.appender.stdout=org.apache.log4j.ConsoleAppender
+log4j.appender.stdout.Target=System.out
+log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
+log4j.appender.stdout.layout.ConversionPattern=%d{ABSOLUTE} %5p [%t] %c{1}:%L - %m%n
+
+
+log4j.rootLogger=info, stdout
+
+#log4j.logger.org.hibernate.test=info
+log4j.logger.org.hibernate.test=trace
+log4j.logger.org.hibernate.cache=trace
+log4j.logger.org.hibernate.SQL=debug
+#log4j.logger.org.jgroups=info
+#log4j.logger.org.infinispan=trace
\ No newline at end of file


Property changes on: core/trunk/cache-infinispan/src/test/resources/log4j.properties
___________________________________________________________________
Name: svn:executable
   + *

Added: core/trunk/cache-infinispan/src/test/resources/org/hibernate/test/cache/infinispan/functional/Contact.hbm.xml
===================================================================
--- core/trunk/cache-infinispan/src/test/resources/org/hibernate/test/cache/infinispan/functional/Contact.hbm.xml	                        (rev 0)
+++ core/trunk/cache-infinispan/src/test/resources/org/hibernate/test/cache/infinispan/functional/Contact.hbm.xml	2009-08-21 08:39:33 UTC (rev 17386)
@@ -0,0 +1,37 @@
+<?xml version="1.0"?>
+<!DOCTYPE hibernate-mapping PUBLIC 
+	"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
+	"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
+
+   <!--
+      ~ Hibernate, Relational Persistence for Idiomatic Java ~ ~
+      Copyright (c) 2007, Red Hat, Inc. and/or it's affiliates or third-party
+      contributors as ~ indicated by the @author tags or express
+      copyright attribution ~ statements applied by the authors.  All
+      third-party contributions are ~ distributed under license by Red
+      Hat Middleware LLC. ~ ~ This copyrighted material is made
+      available to anyone wishing to use, modify, ~ copy, or
+      redistribute it subject to the terms and conditions of the GNU ~
+      Lesser General Public License, as published by the Free Software
+      Foundation. ~ ~ This program 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 distribution; if not,
+      write to: ~ Free Software Foundation, Inc. ~ 51 Franklin Street,
+      Fifth Floor ~ Boston, MA 02110-1301 USA
+   -->
+<hibernate-mapping
+   package="org.hibernate.test.cache.infinispan.functional">
+
+   <class name="Contact" table="Contacts">
+      <id name="id">
+         <generator class="increment" />
+      </id>
+      <property name="name" not-null="true" />
+      <property name="tlf" not-null="true" />
+      <many-to-one name="customer" column="cust_id" class="Customer" />
+   </class>
+
+</hibernate-mapping>
\ No newline at end of file


Property changes on: core/trunk/cache-infinispan/src/test/resources/org/hibernate/test/cache/infinispan/functional/Contact.hbm.xml
___________________________________________________________________
Name: svn:executable
   + *

Added: core/trunk/cache-infinispan/src/test/resources/org/hibernate/test/cache/infinispan/functional/Customer.hbm.xml
===================================================================
--- core/trunk/cache-infinispan/src/test/resources/org/hibernate/test/cache/infinispan/functional/Customer.hbm.xml	                        (rev 0)
+++ core/trunk/cache-infinispan/src/test/resources/org/hibernate/test/cache/infinispan/functional/Customer.hbm.xml	2009-08-21 08:39:33 UTC (rev 17386)
@@ -0,0 +1,40 @@
+<?xml version="1.0"?>
+<!DOCTYPE hibernate-mapping PUBLIC 
+   "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
+   "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
+
+   <!--
+      ~ Hibernate, Relational Persistence for Idiomatic Java ~ ~
+      Copyright (c) 2007, Red Hat, Inc. and/or it's affiliates or third-party
+      contributors as ~ indicated by the @author tags or express
+      copyright attribution ~ statements applied by the authors.  All
+      third-party contributions are ~ distributed under license by Red
+      Hat Middleware LLC. ~ ~ This copyrighted material is made
+      available to anyone wishing to use, modify, ~ copy, or
+      redistribute it subject to the terms and conditions of the GNU ~
+      Lesser General Public License, as published by the Free Software
+      Foundation. ~ ~ This program 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 distribution; if not,
+      write to: ~ Free Software Foundation, Inc. ~ 51 Franklin Street,
+      Fifth Floor ~ Boston, MA 02110-1301 USA
+   -->
+<hibernate-mapping
+   package="org.hibernate.test.cache.infinispan.functional">
+
+   <class name="Customer" table="Customers">
+      <id name="id">
+         <generator class="increment" />
+      </id>
+      <property name="name" not-null="true" />
+      <set name="contacts" inverse="true" cascade="save-update">
+         <key column="cust_id" />
+         <one-to-many class="Contact" />
+      </set>
+
+   </class>
+
+</hibernate-mapping>
\ No newline at end of file

Added: core/trunk/cache-infinispan/src/test/resources/org/hibernate/test/cache/infinispan/functional/Item.hbm.xml
===================================================================
--- core/trunk/cache-infinispan/src/test/resources/org/hibernate/test/cache/infinispan/functional/Item.hbm.xml	                        (rev 0)
+++ core/trunk/cache-infinispan/src/test/resources/org/hibernate/test/cache/infinispan/functional/Item.hbm.xml	2009-08-21 08:39:33 UTC (rev 17386)
@@ -0,0 +1,49 @@
+<?xml version="1.0"?>
+<!DOCTYPE hibernate-mapping PUBLIC 
+	"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
+	"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
+
+   <!--
+      ~ Hibernate, Relational Persistence for Idiomatic Java ~ ~
+      Copyright (c) 2007, Red Hat, Inc. and/or it's affiliates or third-party
+      contributors as ~ indicated by the @author tags or express
+      copyright attribution ~ statements applied by the authors.  All
+      third-party contributions are ~ distributed under license by Red
+      Hat Middleware LLC. ~ ~ This copyrighted material is made
+      available to anyone wishing to use, modify, ~ copy, or
+      redistribute it subject to the terms and conditions of the GNU ~
+      Lesser General Public License, as published by the Free Software
+      Foundation. ~ ~ This program 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 distribution; if not,
+      write to: ~ Free Software Foundation, Inc. ~ 51 Franklin Street,
+      Fifth Floor ~ Boston, MA 02110-1301 USA
+   -->
+<hibernate-mapping package="org.hibernate.test.cache.infinispan.functional">
+
+   <class name="Item" table="Items">
+      <id name="id">
+         <generator class="increment" />
+      </id>
+      <property name="name" not-null="true" />
+      <property name="description" not-null="true" />
+      <many-to-one name="owner" column="owner_id" class="Item" />
+      <set name="items" inverse="true">
+         <key column="owner_id" />
+         <one-to-many class="Item" />
+      </set>
+   </class>
+
+   <class name="VersionedItem" table="VersionedItems">
+      <id name="id">
+         <generator class="increment" />
+      </id>
+      <version name="version" type="long" />
+      <property name="name" not-null="true" />
+      <property name="description" not-null="true" />
+   </class>
+
+</hibernate-mapping>
\ No newline at end of file


Property changes on: core/trunk/cache-infinispan/src/test/resources/org/hibernate/test/cache/infinispan/functional/Item.hbm.xml
___________________________________________________________________
Name: svn:executable
   + *

Added: core/trunk/cache-infinispan/src/test/resources/org/hibernate/test/cache/infinispan/functional/classloader/Account.hbm.xml
===================================================================
--- core/trunk/cache-infinispan/src/test/resources/org/hibernate/test/cache/infinispan/functional/classloader/Account.hbm.xml	                        (rev 0)
+++ core/trunk/cache-infinispan/src/test/resources/org/hibernate/test/cache/infinispan/functional/classloader/Account.hbm.xml	2009-08-21 08:39:33 UTC (rev 17386)
@@ -0,0 +1,46 @@
+<?xml version="1.0"?>
+<!DOCTYPE hibernate-mapping PUBLIC 
+	"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
+	"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
+
+<!--
+  ~ Hibernate, Relational Persistence for Idiomatic Java
+  ~
+  ~ Copyright (c) 2008, Red Hat, Inc. and/or it's affiliates or third-party contributors as
+  ~ indicated by the @author tags or express copyright attribution
+  ~ statements applied by the authors.  All third-party contributions are
+  ~ distributed under license by Red Hat, Inc. and/or it's affiliates.
+  ~
+  ~ This copyrighted material is made available to anyone wishing to use, modify,
+  ~ copy, or redistribute it subject to the terms and conditions of the GNU
+  ~ Lesser General Public License, as published by the Free Software Foundation.
+  ~
+  ~ This program 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 distribution; if not, write to:
+  ~ Free Software Foundation, Inc.
+  ~ 51 Franklin Street, Fifth Floor
+  ~ Boston, MA  02110-1301  USA
+  -->
+<hibernate-mapping
+	package="org.hibernate.test.cache.infinispan.functional.classloader">
+
+	<class name="Account" table="Accounts">
+	   
+	   <cache usage="transactional"/>
+	   
+		<id name="id">
+			<generator class="assigned"/>
+		</id>
+		
+		<property name="branch" not-null="true"/>
+		<property name="balance" not-null="true"/>		
+      <property name="accountHolder" type="serializable" not-null="true"/>
+      
+	</class>
+
+</hibernate-mapping>


Property changes on: core/trunk/cache-infinispan/src/test/resources/org/hibernate/test/cache/infinispan/functional/classloader/Account.hbm.xml
___________________________________________________________________
Name: svn:executable
   + *



More information about the hibernate-commits mailing list