Author: galder.zamarreno(a)jboss.com
Date: 2009-08-05 14:31:51 -0400 (Wed, 05 Aug 2009)
New Revision: 17234
Added:
core/branches/INFINISPAN/cache-infinispan/.settings/
core/branches/INFINISPAN/cache-infinispan/src/main/resources/org/
core/branches/INFINISPAN/cache-infinispan/src/main/resources/org/hibernate/
core/branches/INFINISPAN/cache-infinispan/src/main/resources/org/hibernate/cache/
core/branches/INFINISPAN/cache-infinispan/src/main/resources/org/hibernate/cache/infinispan/
core/branches/INFINISPAN/cache-infinispan/src/main/resources/org/hibernate/cache/infinispan/builder/
core/branches/INFINISPAN/cache-infinispan/src/main/resources/org/hibernate/cache/infinispan/builder/ispn4-configs.xml
core/branches/INFINISPAN/cache-infinispan/src/test/java/org/hibernate/test/cache/infinispan/functional/cluster/
core/branches/INFINISPAN/cache-infinispan/src/test/java/org/hibernate/test/cache/infinispan/functional/cluster/AbstractDualNodeTestCase.java
core/branches/INFINISPAN/cache-infinispan/src/test/java/org/hibernate/test/cache/infinispan/functional/cluster/ClusterAwareRegionFactory.java
core/branches/INFINISPAN/cache-infinispan/src/test/java/org/hibernate/test/cache/infinispan/functional/cluster/Contact.hbm.xml
core/branches/INFINISPAN/cache-infinispan/src/test/java/org/hibernate/test/cache/infinispan/functional/cluster/Contact.java
core/branches/INFINISPAN/cache-infinispan/src/test/java/org/hibernate/test/cache/infinispan/functional/cluster/Customer.java
core/branches/INFINISPAN/cache-infinispan/src/test/java/org/hibernate/test/cache/infinispan/functional/cluster/DualNodeConnectionProviderImpl.java
core/branches/INFINISPAN/cache-infinispan/src/test/java/org/hibernate/test/cache/infinispan/functional/cluster/DualNodeJtaTransactionImpl.java
core/branches/INFINISPAN/cache-infinispan/src/test/java/org/hibernate/test/cache/infinispan/functional/cluster/DualNodeJtaTransactionManagerImpl.java
core/branches/INFINISPAN/cache-infinispan/src/test/java/org/hibernate/test/cache/infinispan/functional/cluster/DualNodeTransactionManagerLookup.java
core/branches/INFINISPAN/cache-infinispan/src/test/java/org/hibernate/test/cache/infinispan/functional/cluster/EntityCollectionInvalidationTestCase.java
core/branches/INFINISPAN/cache-infinispan/src/test/java/org/hibernate/test/cache/infinispan/functional/util/
Removed:
core/branches/INFINISPAN/cache-infinispan/src/test/java/org/hibernate/cache/
Modified:
core/branches/INFINISPAN/cache-infinispan/src/main/java/org/hibernate/cache/infinispan/InfinispanRegionFactory.java
core/branches/INFINISPAN/cache-infinispan/src/main/java/org/hibernate/cache/infinispan/impl/BaseRegion.java
core/branches/INFINISPAN/cache-infinispan/src/test/java/org/hibernate/test/cache/infinispan/functional/AbstractInfinispanTestCase.java
core/branches/INFINISPAN/cache-infinispan/src/test/java/org/hibernate/test/cache/infinispan/functional/BasicTransactionalTestCase.java
Log:
[ISPN-6] Added more tests and sample infinispan configuiration files. More work still to
come.
Modified:
core/branches/INFINISPAN/cache-infinispan/src/main/java/org/hibernate/cache/infinispan/InfinispanRegionFactory.java
===================================================================
---
core/branches/INFINISPAN/cache-infinispan/src/main/java/org/hibernate/cache/infinispan/InfinispanRegionFactory.java 2009-08-05
18:10:41 UTC (rev 17233)
+++
core/branches/INFINISPAN/cache-infinispan/src/main/java/org/hibernate/cache/infinispan/InfinispanRegionFactory.java 2009-08-05
18:31:51 UTC (rev 17234)
@@ -14,103 +14,123 @@
import org.hibernate.cache.infinispan.query.InfinispanQueryResultsRegion;
import org.hibernate.cache.infinispan.timestamp.InfinispanTimestampsRegion;
import org.hibernate.cfg.Settings;
+import org.hibernate.util.PropertiesHelper;
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>-... cache
regions.
+ * A {@link RegionFactory} for <a
href="http://www.jboss.org/infinispan">Infinispan</a>-... cache
+ * regions.
*
* @author Chris Bredesen
*/
public class InfinispanRegionFactory implements RegionFactory {
- private static final Logger log =
LoggerFactory.getLogger(InfinispanRegionFactory.class);
+
+ private static final Logger log =
LoggerFactory.getLogger(InfinispanRegionFactory.class);
+
+ public static final String INFINISPAN_CONFIG_RESOURCE_PROP =
"hibernate.cache.infinispan.cfg";
+
+ public static final String DEF_INFINISPAN_CONFIG_RESOURCE =
"org/hibernate/cache/infinispan/builder/ispn4-configs.xml";
+
+ private CacheManager manager;
- final CacheManager cacheManager = new DefaultCacheManager( false );
+ /**
+ * Create a new instance using the default configuration.
+ */
+ public InfinispanRegionFactory() {
+ }
- /**
- * 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) {
+ }
- /**
- * 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 +
"]");
+ // TODO: Clarify whether for collections, we should just pick the entity name
without the collection name from the CacheManager
+ return new InfinispanCollectionRegion(manager.getCache(regionName), regionName,
metadata);
+ }
- /**
- * {@inheritDoc}
- */
- public CollectionRegion buildCollectionRegion( String regionName, Properties
properties,
- CacheDataDescription metadata ) throws CacheException {
- log.debug( "Building collection cache region [" + regionName + "]"
);
- return new InfinispanCollectionRegion( cacheManager.getCache( regionName ),
regionName,
- metadata );
- }
+ /**
+ * {@inheritDoc}
+ */
+ public EntityRegion buildEntityRegion(String regionName, Properties properties,
+ CacheDataDescription metadata) throws CacheException {
+ log.debug("Building entity cache region [" + regionName +
"]");
+ return new InfinispanEntityRegion(manager.getCache(regionName), regionName,
metadata);
+ }
- /**
- * {@inheritDoc}
- */
- public EntityRegion buildEntityRegion( String regionName, Properties properties,
- CacheDataDescription metadata ) throws CacheException {
- log.debug( "Building entity cache region [" + regionName + "]" );
- return new InfinispanEntityRegion( cacheManager.getCache( regionName ), regionName,
- metadata );
- }
+ /**
+ * {@inheritDoc}
+ */
+ public QueryResultsRegion buildQueryResultsRegion(String regionName, Properties
properties)
+ throws CacheException {
+ log.debug("Building query results cache region [" + regionName +
"]");
+ return new InfinispanQueryResultsRegion(manager.getCache(regionName), regionName);
+ }
- /**
- * {@inheritDoc}
- */
- public QueryResultsRegion buildQueryResultsRegion( String regionName, Properties
properties )
- throws CacheException {
- log.debug( "Building query results cache region [" + regionName +
"]" );
- return new InfinispanQueryResultsRegion( cacheManager.getCache(regionName),
regionName);
- }
+ /**
+ * {@inheritDoc}
+ */
+ public TimestampsRegion buildTimestampsRegion(String regionName, Properties
properties)
+ throws CacheException {
+ log.debug("Building timestamps cache region [" + regionName +
"]");
+ return new InfinispanTimestampsRegion(manager.getCache(regionName), regionName);
+ }
- /**
- * {@inheritDoc}
- */
- public TimestampsRegion buildTimestampsRegion( String regionName, Properties properties
)
- throws CacheException {
- log.debug( "Building timestamps cache region [" + regionName + "]"
);
- return new InfinispanTimestampsRegion( cacheManager.getCache( regionName ), regionName
);
- }
+ /**
+ * {@inheritDoc}
+ */
+ public boolean isMinimalPutsEnabledByDefault() {
+ return false;
+ }
- /**
- * {@inheritDoc}
- */
- public boolean isMinimalPutsEnabledByDefault() {
- return false;
- }
+ /**
+ * {@inheritDoc}
+ */
+ public long nextTimestamp() {
+ return 0;
+ }
+
+ public void setCacheManager(CacheManager manager) {
+ this.manager = manager;
+ }
- /**
- * {@inheritDoc}
- */
- public long nextTimestamp() {
- return 0;
- }
+ public CacheManager getCacheManager() {
+ return manager;
+ }
- /**
- * {@inheritDoc}
- */
- public void start( Settings settings, Properties properties ) throws CacheException {
- log.debug( "Starting Infinispan CacheManager" );
- this.cacheManager.start();
- }
+ /**
+ * {@inheritDoc}
+ */
+ public void start(Settings settings, Properties properties) throws CacheException {
+ log.debug("Starting Infinispan CacheManager");
+ try {
+ String configLoc = PropertiesHelper.getString(INFINISPAN_CONFIG_RESOURCE_PROP,
properties, DEF_INFINISPAN_CONFIG_RESOURCE);
+ manager = new DefaultCacheManager(configLoc);
+ } 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" );
- this.cacheManager.stop();
- }
+ /**
+ * {@inheritDoc}
+ */
+ public void stop() {
+ log.debug("Stopping Infinispan CacheManager");
+ manager.stop();
+ }
}
\ No newline at end of file
Modified:
core/branches/INFINISPAN/cache-infinispan/src/main/java/org/hibernate/cache/infinispan/impl/BaseRegion.java
===================================================================
---
core/branches/INFINISPAN/cache-infinispan/src/main/java/org/hibernate/cache/infinispan/impl/BaseRegion.java 2009-08-05
18:10:41 UTC (rev 17233)
+++
core/branches/INFINISPAN/cache-infinispan/src/main/java/org/hibernate/cache/infinispan/impl/BaseRegion.java 2009-08-05
18:31:51 UTC (rev 17234)
@@ -1,6 +1,5 @@
package org.hibernate.cache.infinispan.impl;
-import java.util.Collections;
import java.util.Map;
import org.hibernate.cache.CacheException;
@@ -8,69 +7,69 @@
import org.infinispan.Cache;
/**
- * 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.
+ * 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
*/
abstract class BaseRegion implements Region {
- private final Cache<Object, Object> cache;
- private final String name;
+ private final Cache<Object, Object> cache;
+ private final String name;
- public BaseRegion( Cache<Object, Object> cache, String name ) {
- this.cache = cache;
- this.name = name;
- }
-
- public Cache<Object, Object> getCache() {
- return cache;
- }
+ public BaseRegion(Cache<Object, Object> cache, String name) {
+ this.cache = cache;
+ this.name = name;
+ }
- public String getName() {
- return name;
- }
+ public Cache<Object, Object> getCache() {
+ return cache;
+ }
- public long getElementCountInMemory() {
- return cache.size();
- }
+ public String getName() {
+ return name;
+ }
- /**
- * Not supported.
- *
- * @return -1
- */
- public long getElementCountOnDisk() {
- return -1;
- }
+ public long getElementCountInMemory() {
+ return cache.size();
+ }
- /**
- * Not supported.
- *
- * @return -1
- */
- public long getSizeInMemory() {
- return -1;
- }
+ /**
+ * Not supported.
+ *
+ * @return -1
+ */
+ public long getElementCountOnDisk() {
+ return -1;
+ }
- public int getTimeout() {
- // TODO Auto-generated method stub
- return 0;
- }
+ /**
+ * Not supported.
+ *
+ * @return -1
+ */
+ public long getSizeInMemory() {
+ return -1;
+ }
- public long nextTimestamp() {
- // TODO Auto-generated method stub
- return 0;
- }
+ public int getTimeout() {
+ // TODO Auto-generated method stub
+ return 0;
+ }
- @SuppressWarnings("unchecked")
- public Map toMap() {
- return Collections.EMPTY_MAP;
- }
+ public long nextTimestamp() {
+ // TODO Auto-generated method stub
+ return 0;
+ }
- public void destroy() throws CacheException {
- // TODO see if we need to do this even in spite of RF.shutdown()
- }
+ @SuppressWarnings("unchecked")
+ public Map toMap() {
+ return cache;
+ }
+ public void destroy() throws CacheException {
+ // TODO see if we need to do this even in spite of RF.shutdown()
+ }
+
}
\ No newline at end of file
Added:
core/branches/INFINISPAN/cache-infinispan/src/main/resources/org/hibernate/cache/infinispan/builder/ispn4-configs.xml
===================================================================
---
core/branches/INFINISPAN/cache-infinispan/src/main/resources/org/hibernate/cache/infinispan/builder/ispn4-configs.xml
(rev 0)
+++
core/branches/INFINISPAN/cache-infinispan/src/main/resources/org/hibernate/cache/infinispan/builder/ispn4-configs.xml 2009-08-05
18:31:51 UTC (rev 17234)
@@ -0,0 +1,63 @@
+<?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. -->
+ <!-- TODO: Change to udp.xml once streaming transfer requirement has been
removed. -->
+ <property name="configurationFile"
value="flush-udp.xml"/>
+ <!-- See the JGroupsTransport javadocs for more flags -->
+ </transport>
+ <serialization
marshallerClass="org.infinispan.marshall.VersionAwareMarshaller"
version="4.0"/>
+ </global>
+
+ <!-- Default configuration is appropriate for entity/collection caching. -->
+ <default>
+ <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="FIFO"/>
+ <expiration maxIdle="100000"/>
+ <lazyDeserialization enabled="true"/>
+ </default>
+
+ <!-- A config appropriate for query caching. Does not replicate
+ queries. DO NOT STORE TIMESTAMPS IN THIS CACHE.
+ -->
+ <namedCache name="org.hibernate.cache.StandardQueryCache">
+ <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="FIFO"/>
+ <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="org.hibernate.cache.UpdateTimestampsCache">
+ <clustering mode="REPL">
+ <stateRetrieval fetchInMemoryState="true"
timeout="20000"/>
+ <async/>
+ </clustering>
+ <locking isolationLevel="READ_COMMITTED"
concurrencyLevel="1000" lockAcquisitionTimeout="15000"/>
+ <!-- Don't ever evict modification timestamps -->
+ <lazyDeserialization enabled="true"/>
+ </namedCache>
+
+</infinispan>
\ No newline at end of file
Modified:
core/branches/INFINISPAN/cache-infinispan/src/test/java/org/hibernate/test/cache/infinispan/functional/AbstractInfinispanTestCase.java
===================================================================
---
core/branches/INFINISPAN/cache-infinispan/src/test/java/org/hibernate/test/cache/infinispan/functional/AbstractInfinispanTestCase.java 2009-08-05
18:10:41 UTC (rev 17233)
+++
core/branches/INFINISPAN/cache-infinispan/src/test/java/org/hibernate/test/cache/infinispan/functional/AbstractInfinispanTestCase.java 2009-08-05
18:31:51 UTC (rev 17234)
@@ -1,94 +1,104 @@
package org.hibernate.test.cache.infinispan.functional;
+import java.util.Map;
+
import org.hibernate.Session;
import org.hibernate.junit.functional.FunctionalTestCase;
import org.hibernate.stat.SecondLevelCacheStatistics;
import org.hibernate.stat.Statistics;
public abstract class AbstractInfinispanTestCase extends FunctionalTestCase {
- private final String cacheConcurrencyStrategy;
-
- public AbstractInfinispanTestCase( 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;
- }
+ private final String cacheConcurrencyStrategy;
- 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 testQueryCache() {
- Item item = new Item( "chris", "Chris's Item" );
-
- Session s = openSession();
- s.getTransaction().begin();
- s.persist( item );
- s.getTransaction().commit();
- s.close();
+ public AbstractInfinispanTestCase(String string, String cacheConcurrencyStrategy) {
+ super(string);
+ this.cacheConcurrencyStrategy = cacheConcurrencyStrategy;
+ }
- s = openSession();
- s.createQuery( "from Item" ).setCacheable( true ).list();
- s.close();
+ public String[] getMappings() {
+ return new String[] { "cache/infinispan/functional/Item.hbm.xml" };
+ }
- 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();
- }
-
- public void testCollectionCache() {
- Item item = new Item( "chris", "Chris's Item" );
- Item another = new Item( "another", "Owned Item" );
- item.addItem( another );
+ @Override
+ public String getCacheConcurrencyStrategy() {
+ return cacheConcurrencyStrategy;
+ }
- Session s = openSession();
- s.getTransaction().begin();
- s.persist( item );
- s.persist( another );
- s.getTransaction().commit();
- s.close();
+ public void testEntityCache() {
+ Item item = new Item("chris", "Chris's Item");
- s = openSession();
- Statistics stats = s.getSessionFactory().getStatistics();
- Item loaded = (Item) s.load( Item.class, item.getId() );
- assertEquals( 1, loaded.getItems().size() );
- s.close();
+ Session s = openSession();
+ Statistics stats = s.getSessionFactory().getStatistics();
+ s.getTransaction().begin();
+ s.persist(item);
+ s.getTransaction().commit();
+ 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();
- }
+ 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 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();
+ }
+
+ 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 testEmptySecondLevelCacheEntry() throws Exception {
+ getSessions().evictEntity(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
Modified:
core/branches/INFINISPAN/cache-infinispan/src/test/java/org/hibernate/test/cache/infinispan/functional/BasicTransactionalTestCase.java
===================================================================
---
core/branches/INFINISPAN/cache-infinispan/src/test/java/org/hibernate/test/cache/infinispan/functional/BasicTransactionalTestCase.java 2009-08-05
18:10:41 UTC (rev 17233)
+++
core/branches/INFINISPAN/cache-infinispan/src/test/java/org/hibernate/test/cache/infinispan/functional/BasicTransactionalTestCase.java 2009-08-05
18:31:51 UTC (rev 17234)
@@ -1,9 +1,114 @@
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;
+
public class BasicTransactionalTestCase extends AbstractInfinispanTestCase {
- public BasicTransactionalTestCase( String string ) {
- super( string, "transactional" );
- }
-
+ public BasicTransactionalTestCase(String string) {
+ super(string, "transactional");
+ }
+
+ 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();
+ }
}
Added:
core/branches/INFINISPAN/cache-infinispan/src/test/java/org/hibernate/test/cache/infinispan/functional/cluster/AbstractDualNodeTestCase.java
===================================================================
---
core/branches/INFINISPAN/cache-infinispan/src/test/java/org/hibernate/test/cache/infinispan/functional/cluster/AbstractDualNodeTestCase.java
(rev 0)
+++
core/branches/INFINISPAN/cache-infinispan/src/test/java/org/hibernate/test/cache/infinispan/functional/cluster/AbstractDualNodeTestCase.java 2009-08-05
18:31:51 UTC (rev 17234)
@@ -0,0 +1,236 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2009, Red Hat Middleware LLC, and individual contributors
+ * as indicated by the @author tags. See the copyright.txt file in the
+ * distribution for a full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site:
http://www.fsf.org.
+ */
+package org.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
+ */
+public 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/cluster/Contact.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/branches/INFINISPAN/cache-infinispan/src/test/java/org/hibernate/test/cache/infinispan/functional/cluster/ClusterAwareRegionFactory.java
===================================================================
---
core/branches/INFINISPAN/cache-infinispan/src/test/java/org/hibernate/test/cache/infinispan/functional/cluster/ClusterAwareRegionFactory.java
(rev 0)
+++
core/branches/INFINISPAN/cache-infinispan/src/test/java/org/hibernate/test/cache/infinispan/functional/cluster/ClusterAwareRegionFactory.java 2009-08-05
18:31:51 UTC (rev 17234)
@@ -0,0 +1,122 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2009, Red Hat Middleware LLC, and individual contributors
+ * as indicated by the @author tags. See the copyright.txt file in the
+ * distribution for a full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site:
http://www.fsf.org.
+ */
+package org.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
+ */
+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/branches/INFINISPAN/cache-infinispan/src/test/java/org/hibernate/test/cache/infinispan/functional/cluster/Contact.hbm.xml
===================================================================
---
core/branches/INFINISPAN/cache-infinispan/src/test/java/org/hibernate/test/cache/infinispan/functional/cluster/Contact.hbm.xml
(rev 0)
+++
core/branches/INFINISPAN/cache-infinispan/src/test/java/org/hibernate/test/cache/infinispan/functional/cluster/Contact.hbm.xml 2009-08-05
18:31:51 UTC (rev 17234)
@@ -0,0 +1,53 @@
+<?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 Middleware LLC 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.cluster">
+
+ <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>
+
+ <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
Property changes on:
core/branches/INFINISPAN/cache-infinispan/src/test/java/org/hibernate/test/cache/infinispan/functional/cluster/Contact.hbm.xml
___________________________________________________________________
Name: svn:executable
+ *
Added:
core/branches/INFINISPAN/cache-infinispan/src/test/java/org/hibernate/test/cache/infinispan/functional/cluster/Contact.java
===================================================================
---
core/branches/INFINISPAN/cache-infinispan/src/test/java/org/hibernate/test/cache/infinispan/functional/cluster/Contact.java
(rev 0)
+++
core/branches/INFINISPAN/cache-infinispan/src/test/java/org/hibernate/test/cache/infinispan/functional/cluster/Contact.java 2009-08-05
18:31:51 UTC (rev 17234)
@@ -0,0 +1,90 @@
+/*
+ * Hibernate, Relational Persistence for Idiomatic Java
+ *
+ * Copyright (c) 2008, Red Hat Middleware LLC 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
+ */
+package org.hibernate.test.cache.infinispan.functional.cluster;
+
+import java.io.Serializable;
+
+/**
+ * Entity that has a many-to-one relationship to a Customer
+ */
+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/branches/INFINISPAN/cache-infinispan/src/test/java/org/hibernate/test/cache/infinispan/functional/cluster/Contact.java
___________________________________________________________________
Name: svn:executable
+ *
Added:
core/branches/INFINISPAN/cache-infinispan/src/test/java/org/hibernate/test/cache/infinispan/functional/cluster/Customer.java
===================================================================
---
core/branches/INFINISPAN/cache-infinispan/src/test/java/org/hibernate/test/cache/infinispan/functional/cluster/Customer.java
(rev 0)
+++
core/branches/INFINISPAN/cache-infinispan/src/test/java/org/hibernate/test/cache/infinispan/functional/cluster/Customer.java 2009-08-05
18:31:51 UTC (rev 17234)
@@ -0,0 +1,67 @@
+/*
+ * Hibernate, Relational Persistence for Idiomatic Java
+ *
+ * Copyright (c) 2008, Red Hat Middleware LLC 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
+ */
+package org.hibernate.test.cache.infinispan.functional.cluster;
+
+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/branches/INFINISPAN/cache-infinispan/src/test/java/org/hibernate/test/cache/infinispan/functional/cluster/Customer.java
___________________________________________________________________
Name: svn:executable
+ *
Added:
core/branches/INFINISPAN/cache-infinispan/src/test/java/org/hibernate/test/cache/infinispan/functional/cluster/DualNodeConnectionProviderImpl.java
===================================================================
---
core/branches/INFINISPAN/cache-infinispan/src/test/java/org/hibernate/test/cache/infinispan/functional/cluster/DualNodeConnectionProviderImpl.java
(rev 0)
+++
core/branches/INFINISPAN/cache-infinispan/src/test/java/org/hibernate/test/cache/infinispan/functional/cluster/DualNodeConnectionProviderImpl.java 2009-08-05
18:31:51 UTC (rev 17234)
@@ -0,0 +1,85 @@
+/*
+ * Hibernate, Relational Persistence for Idiomatic Java
+ *
+ * Copyright (c) 2007, Red Hat Middleware LLC 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
+ */
+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/branches/INFINISPAN/cache-infinispan/src/test/java/org/hibernate/test/cache/infinispan/functional/cluster/DualNodeJtaTransactionImpl.java
===================================================================
---
core/branches/INFINISPAN/cache-infinispan/src/test/java/org/hibernate/test/cache/infinispan/functional/cluster/DualNodeJtaTransactionImpl.java
(rev 0)
+++
core/branches/INFINISPAN/cache-infinispan/src/test/java/org/hibernate/test/cache/infinispan/functional/cluster/DualNodeJtaTransactionImpl.java 2009-08-05
18:31:51 UTC (rev 17234)
@@ -0,0 +1,159 @@
+/*
+ * Hibernate, Relational Persistence for Idiomatic Java
+ *
+ * Copyright (c) 2007, Red Hat Middleware LLC 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
+ */
+package org.hibernate.test.cache.infinispan.functional.cluster;
+
+import java.sql.Connection;
+import java.sql.SQLException;
+import java.util.LinkedList;
+
+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.XAResource;
+
+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;
+
+ 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();
+ }
+
+ status = Status.STATUS_COMMITTING;
+
+ if (connection != null) {
+ try {
+ connection.commit();
+ connection.close();
+ } catch (SQLException sqle) {
+ status = Status.STATUS_UNKNOWN;
+ throw new SystemException();
+ }
+ }
+
+ 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_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 {
+ return false;
+ }
+
+ public boolean delistResource(XAResource xaResource, int i) throws
IllegalStateException,
+ SystemException {
+ return false;
+ }
+}
Added:
core/branches/INFINISPAN/cache-infinispan/src/test/java/org/hibernate/test/cache/infinispan/functional/cluster/DualNodeJtaTransactionManagerImpl.java
===================================================================
---
core/branches/INFINISPAN/cache-infinispan/src/test/java/org/hibernate/test/cache/infinispan/functional/cluster/DualNodeJtaTransactionManagerImpl.java
(rev 0)
+++
core/branches/INFINISPAN/cache-infinispan/src/test/java/org/hibernate/test/cache/infinispan/functional/cluster/DualNodeJtaTransactionManagerImpl.java 2009-08-05
18:31:51 UTC (rev 17234)
@@ -0,0 +1,158 @@
+/*
+ * Hibernate, Relational Persistence for Idiomatic Java
+ *
+ * Copyright (c) 2007, Red Hat Middleware LLC 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
+ */
+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/branches/INFINISPAN/cache-infinispan/src/test/java/org/hibernate/test/cache/infinispan/functional/cluster/DualNodeTransactionManagerLookup.java
===================================================================
---
core/branches/INFINISPAN/cache-infinispan/src/test/java/org/hibernate/test/cache/infinispan/functional/cluster/DualNodeTransactionManagerLookup.java
(rev 0)
+++
core/branches/INFINISPAN/cache-infinispan/src/test/java/org/hibernate/test/cache/infinispan/functional/cluster/DualNodeTransactionManagerLookup.java 2009-08-05
18:31:51 UTC (rev 17234)
@@ -0,0 +1,55 @@
+/*
+ * Hibernate, Relational Persistence for Idiomatic Java
+ *
+ * Copyright (c) 2007, Red Hat Middleware LLC 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
+ */
+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/branches/INFINISPAN/cache-infinispan/src/test/java/org/hibernate/test/cache/infinispan/functional/cluster/EntityCollectionInvalidationTestCase.java
===================================================================
---
core/branches/INFINISPAN/cache-infinispan/src/test/java/org/hibernate/test/cache/infinispan/functional/cluster/EntityCollectionInvalidationTestCase.java
(rev 0)
+++
core/branches/INFINISPAN/cache-infinispan/src/test/java/org/hibernate/test/cache/infinispan/functional/cluster/EntityCollectionInvalidationTestCase.java 2009-08-05
18:31:51 UTC (rev 17234)
@@ -0,0 +1,358 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2009, Red Hat Middleware LLC, and individual contributors
+ * as indicated by the @author tags. See the copyright.txt file in the
+ * distribution for a full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site:
http://www.fsf.org.
+ */
+package org.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.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;
+
+/**
+ * EntityReplicationTestCase.
+ *
+ * @author Galder Zamarreño
+ */
+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);
+ 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());
+ assertTrue(localContactCache.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.name.equals("Kabir")) {
+ contacts.remove(c);
+ } else {
+ contactIds.add(c.getId());
+ }
+ }
+ ids.contactIds = contactIds;
+ customer.setContacts(contacts);
+ 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.cluster.";
+ 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);
+ }
+ }
+ }
+ }
+
+ private class IdContainer {
+ Integer customerId;
+ Set<Integer> contactIds;
+ }
+
+}