HHH-10030 Add read-write cache concurrency strategy to Infinispan 2LC

* AccessType.READ_WRITE is now supported cache concurrency strategy
* Added checks that we're caching in local or invalidation cache (distributed and replicated cache does not work ATM)
* Refactored test-suite: Running on both transactional and read-write caches (these should yield the same results)
** CustomParemeterized runner is used for that
** Moved all entities used in functional tests to one package
** Removed already disabled tests related to class loaders (not needed since Infinispan 5.1)

(cherry picked from commit 3689924d74)
This commit is contained in:
Radim Vansa 2015-08-26 16:52:23 +02:00 committed by Steve Ebersole
parent b63da4bcdc
commit 64f91f1a15
122 changed files with 7193 additions and 8506 deletions

View File

@ -104,12 +104,12 @@ public class InfinispanRegionFactory implements RegionFactory {
*/ */
public static final String INFINISPAN_CONFIG_RESOURCE_PROP = "hibernate.cache.infinispan.cfg"; public static final String INFINISPAN_CONFIG_RESOURCE_PROP = "hibernate.cache.infinispan.cfg";
/** /**
* Property name that controls whether Infinispan statistics are enabled. * Property name that controls whether Infinispan statistics are enabled.
* The property value is expected to be a boolean true or false, and it * The property value is expected to be a boolean true or false, and it
* overrides statistic configuration in base Infinispan configuration, * overrides statistic configuration in base Infinispan configuration,
* if provided. * if provided.
*/ */
public static final String INFINISPAN_GLOBAL_STATISTICS_PROP = "hibernate.cache.infinispan.statistics"; public static final String INFINISPAN_GLOBAL_STATISTICS_PROP = "hibernate.cache.infinispan.statistics";
/** /**

View File

@ -0,0 +1,157 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
package org.hibernate.cache.infinispan.access;
import org.hibernate.cache.CacheException;
import org.hibernate.cache.spi.access.SoftLock;
import org.hibernate.engine.spi.SessionImplementor;
/**
* Defines the strategy for 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 org.hibernate.cache.spi.access.EntityRegionAccessStrategy}
* and {@link org.hibernate.cache.spi.access.CollectionRegionAccessStrategy} implementations.
*
* @author Radim Vansa &lt;rvansa@redhat.com&gt;
*/
public interface AccessDelegate {
Object get(SessionImplementor session, Object key, long txTimestamp) throws CacheException;
/**
* Attempt to cache an object, after loading from the database.
*
* @param session Current session
* @param key The item key
* @param value The item
* @param txTimestamp a timestamp prior to the transaction start time
* @param version the item version number
* @return <tt>true</tt> if the object was successfully cached
*/
boolean putFromLoad(SessionImplementor session, Object key, Object value, long txTimestamp, Object version);
/**
* Attempt to cache an object, after loading from the database, explicitly
* specifying the minimalPut behavior.
*
* @param session Current session.
* @param key The item key
* @param value The item
* @param txTimestamp a timestamp prior to the transaction start time
* @param version the item version number
* @param minimalPutOverride Explicit minimalPut flag
* @return <tt>true</tt> if the object was successfully cached
* @throws org.hibernate.cache.CacheException Propogated from underlying {@link org.hibernate.cache.spi.Region}
*/
boolean putFromLoad(SessionImplementor session, Object key, Object value, long txTimestamp, Object version, boolean minimalPutOverride)
throws CacheException;
/**
* Called after an item has been inserted (before the transaction completes),
* instead of calling evict().
*
* @param session Current session
* @param key The item key
* @param value The item
* @param version The item's version value
* @return Were the contents of the cache actual changed by this operation?
* @throws CacheException if the insert fails
*/
boolean insert(SessionImplementor session, Object key, Object value, Object version) throws CacheException;
/**
* Called after an item has been updated (before the transaction completes),
* instead of calling evict().
*
* @param session Current session
* @param key The item key
* @param value The item
* @param currentVersion The item's current version value
* @param previousVersion The item's previous version value
* @return Whether the contents of the cache actual changed by this operation
* @throws CacheException if the update fails
*/
boolean update(SessionImplementor session, Object key, Object value, Object currentVersion, Object previousVersion)
throws CacheException;
/**
* Called after an item has become stale (before the transaction completes).
*
* @param session Current session
* @param key The key of the item to remove
* @throws CacheException if removing the cached item fails
*/
void remove(SessionImplementor session, Object key) throws CacheException;
/**
* Called to evict data from the entire region
*
* @throws CacheException if eviction the region fails
*/
void removeAll() throws CacheException;
/**
* Forcibly evict an item from the cache immediately without regard for transaction
* isolation.
*
* @param key The key of the item to remove
* @throws CacheException if evicting the item fails
*/
void evict(Object key) throws CacheException;
/**
* Forcibly evict all items from the cache immediately without regard for transaction
* isolation.
*
* @throws CacheException if evicting items fails
*/
void evictAll() throws CacheException;
/**
* Called when we have finished the attempted update/delete (which may or
* may not have been successful), after transaction completion. This method
* is used by "asynchronous" concurrency strategies.
*
*
* @param session
* @param key The item key
* @throws org.hibernate.cache.CacheException Propogated from underlying {@link org.hibernate.cache.spi.Region}
*/
void unlockItem(SessionImplementor session, Object key) throws CacheException;
/**
* Called after an item has been inserted (after the transaction completes),
* instead of calling release().
* This method is used by "asynchronous" concurrency strategies.
*
*
* @param session
* @param key The item key
* @param value The item
* @param version The item's version value
* @return Were the contents of the cache actual changed by this operation?
* @throws CacheException Propagated from underlying {@link org.hibernate.cache.spi.Region}
*/
boolean afterInsert(SessionImplementor session, Object key, Object value, Object version);
/**
* Called after an item has been updated (after the transaction completes),
* instead of calling release(). This method is used by "asynchronous"
* concurrency strategies.
*
*
* @param session
* @param key The item key
* @param value The item
* @param currentVersion The item's current version value
* @param previousVersion The item's previous version value
* @param lock The lock previously obtained from {@link #lockItem}
* @return Were the contents of the cache actual changed by this operation?
* @throws CacheException Propagated from underlying {@link org.hibernate.cache.spi.Region}
*/
boolean afterUpdate(SessionImplementor session, Object key, Object value, Object currentVersion, Object previousVersion, SoftLock lock);
}

View File

@ -9,37 +9,31 @@ package org.hibernate.cache.infinispan.access;
import org.hibernate.cache.CacheException; import org.hibernate.cache.CacheException;
import org.hibernate.cache.infinispan.impl.BaseRegion; import org.hibernate.cache.infinispan.impl.BaseRegion;
import org.hibernate.cache.infinispan.util.Caches; import org.hibernate.cache.infinispan.util.Caches;
import org.hibernate.cache.spi.access.SoftLock;
import org.hibernate.engine.spi.SessionImplementor; import org.hibernate.engine.spi.SessionImplementor;
import org.infinispan.AdvancedCache; import org.infinispan.AdvancedCache;
import org.infinispan.util.logging.Log; import org.infinispan.util.logging.Log;
import org.infinispan.util.logging.LogFactory; import org.infinispan.util.logging.LogFactory;
/** /**
* 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 org.hibernate.cache.spi.access.EntityRegionAccessStrategy}
* and {@link org.hibernate.cache.spi.access.CollectionRegionAccessStrategy} implementations.
* *
* @author Brian Stansberry * @author Brian Stansberry
* @author Galder Zamarreño * @author Galder Zamarreño
* @since 3.5 * @since 3.5
*/ */
public abstract class TransactionalAccessDelegate { public abstract class InvalidationCacheAccessDelegate implements AccessDelegate {
protected static final Log log = LogFactory.getLog( TransactionalAccessDelegate.class ); protected static final Log log = LogFactory.getLog( InvalidationCacheAccessDelegate.class );
protected static final boolean TRACE_ENABLED = log.isTraceEnabled(); protected static final boolean TRACE_ENABLED = log.isTraceEnabled();
protected final AdvancedCache cache; protected final AdvancedCache cache;
protected final BaseRegion region; protected final BaseRegion region;
protected final PutFromLoadValidator putValidator; protected final PutFromLoadValidator putValidator;
protected final AdvancedCache<Object, Object> writeCache; protected final AdvancedCache<Object, Object> writeCache;
public static TransactionalAccessDelegate create(BaseRegion region, PutFromLoadValidator validator) { public static InvalidationCacheAccessDelegate create(BaseRegion region, PutFromLoadValidator validator) {
if (region.getCache().getCacheConfiguration().transaction().transactionMode().isTransactional()) { if (region.getCache().getCacheConfiguration().transaction().transactionMode().isTransactional()) {
return new TxTransactionalAccessDelegate(region, validator); return new TxInvalidationCacheAccessDelegate(region, validator);
} }
else { else {
return new NonTxTransactionalAccessDelegate(region, validator); return new NonTxInvalidationCacheAccessDelegate(region, validator);
} }
} }
@ -50,7 +44,7 @@ public abstract class TransactionalAccessDelegate {
* @param validator put from load validator * @param validator put from load validator
*/ */
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
protected TransactionalAccessDelegate(BaseRegion region, PutFromLoadValidator validator) { protected InvalidationCacheAccessDelegate(BaseRegion region, PutFromLoadValidator validator) {
this.region = region; this.region = region;
this.cache = region.getCache(); this.cache = region.getCache();
this.putValidator = validator; this.putValidator = validator;
@ -67,6 +61,7 @@ public abstract class TransactionalAccessDelegate {
* @return the cached object or <tt>null</tt> * @return the cached object or <tt>null</tt>
* @throws CacheException if the cache retrieval failed * @throws CacheException if the cache retrieval failed
*/ */
@Override
@SuppressWarnings("UnusedParameters") @SuppressWarnings("UnusedParameters")
public Object get(SessionImplementor session, Object key, long txTimestamp) throws CacheException { public Object get(SessionImplementor session, Object key, long txTimestamp) throws CacheException {
if ( !region.checkValid() ) { if ( !region.checkValid() ) {
@ -79,16 +74,7 @@ public abstract class TransactionalAccessDelegate {
return val; return val;
} }
/** @Override
* Attempt to cache an object, after loading from the database.
*
* @param session Current session
* @param key The item key
* @param value The item
* @param txTimestamp a timestamp prior to the transaction start time
* @param version the item version number
* @return <tt>true</tt> if the object was successfully cached
*/
public boolean putFromLoad(SessionImplementor session, Object key, Object value, long txTimestamp, Object version) { public boolean putFromLoad(SessionImplementor session, Object key, Object value, long txTimestamp, Object version) {
return putFromLoad(session, key, value, txTimestamp, version, false ); return putFromLoad(session, key, value, txTimestamp, version, false );
} }
@ -106,6 +92,7 @@ public abstract class TransactionalAccessDelegate {
* @return <tt>true</tt> if the object was successfully cached * @return <tt>true</tt> if the object was successfully cached
* @throws CacheException if storing the object failed * @throws CacheException if storing the object failed
*/ */
@Override
@SuppressWarnings("UnusedParameters") @SuppressWarnings("UnusedParameters")
public boolean putFromLoad(SessionImplementor session, Object key, Object value, long txTimestamp, Object version, boolean minimalPutOverride) public boolean putFromLoad(SessionImplementor session, Object key, Object value, long txTimestamp, Object version, boolean minimalPutOverride)
throws CacheException { throws CacheException {
@ -143,41 +130,7 @@ public abstract class TransactionalAccessDelegate {
return true; return true;
} }
/** @Override
* Called after an item has been inserted (before the transaction completes),
* instead of calling evict().
*
* @param session Current session
* @param key The item key
* @param value The item
* @param version The item's version value
* @return Were the contents of the cache actual changed by this operation?
* @throws CacheException if the insert fails
*/
public abstract boolean insert(SessionImplementor session, Object key, Object value, Object version) throws CacheException;
/**
* Called after an item has been updated (before the transaction completes),
* instead of calling evict().
*
* @param session Current session
* @param key The item key
* @param value The item
* @param currentVersion The item's current version value
* @param previousVersion The item's previous version value
* @return Whether the contents of the cache actual changed by this operation
* @throws CacheException if the update fails
*/
public abstract boolean update(SessionImplementor session, Object key, Object value, Object currentVersion, Object previousVersion)
throws CacheException;
/**
* Called after an item has become stale (before the transaction completes).
*
* @param session Current session
* @param key The key of the item to remove
* @throws CacheException if removing the cached item fails
*/
public void remove(SessionImplementor session, Object key) throws CacheException { public void remove(SessionImplementor session, Object key) throws CacheException {
if ( !putValidator.beginInvalidatingKey(session, key)) { if ( !putValidator.beginInvalidatingKey(session, key)) {
throw new CacheException( throw new CacheException(
@ -196,11 +149,7 @@ public abstract class TransactionalAccessDelegate {
} }
} }
/** @Override
* Called to evict data from the entire region
*
* @throws CacheException if eviction the region fails
*/
public void removeAll() throws CacheException { public void removeAll() throws CacheException {
try { try {
if (!putValidator.beginInvalidatingRegion()) { if (!putValidator.beginInvalidatingRegion()) {
@ -213,23 +162,12 @@ public abstract class TransactionalAccessDelegate {
} }
} }
/** @Override
* Forcibly evict an item from the cache immediately without regard for transaction
* isolation.
*
* @param key The key of the item to remove
* @throws CacheException if evicting the item fails
*/
public void evict(Object key) throws CacheException { public void evict(Object key) throws CacheException {
writeCache.remove( key ); writeCache.remove( key );
} }
/** @Override
* Forcibly evict all items from the cache immediately without regard for transaction
* isolation.
*
* @throws CacheException if evicting items fails
*/
public void evictAll() throws CacheException { public void evictAll() throws CacheException {
try { try {
if (!putValidator.beginInvalidatingRegion()) { if (!putValidator.beginInvalidatingRegion()) {
@ -245,16 +183,7 @@ public abstract class TransactionalAccessDelegate {
} }
} }
/** @Override
* Called when we have finished the attempted update/delete (which may or
* may not have been successful), after transaction completion. This method
* is used by "asynchronous" concurrency strategies.
*
*
* @param session
* @param key The item key
* @throws org.hibernate.cache.CacheException Propogated from underlying {@link org.hibernate.cache.spi.Region}
*/
public void unlockItem(SessionImplementor session, Object key) throws CacheException { public void unlockItem(SessionImplementor session, Object key) throws CacheException {
if ( !putValidator.endInvalidatingKey(session, key) ) { if ( !putValidator.endInvalidatingKey(session, key) ) {
// TODO: localization // TODO: localization
@ -262,50 +191,4 @@ public abstract class TransactionalAccessDelegate {
+ region.getName() + "; the key won't be cached until invalidation expires."); + region.getName() + "; the key won't be cached until invalidation expires.");
} }
} }
/**
* Called after an item has been inserted (after the transaction completes),
* instead of calling release().
* This method is used by "asynchronous" concurrency strategies.
*
*
* @param session
* @param key The item key
* @param value The item
* @param version The item's version value
* @return Were the contents of the cache actual changed by this operation?
* @throws CacheException Propagated from underlying {@link org.hibernate.cache.spi.Region}
*/
public boolean afterInsert(SessionImplementor session, Object key, Object value, Object version) {
if ( !putValidator.endInvalidatingKey(session, key) ) {
// TODO: localization
log.warn("Failed to end invalidating pending putFromLoad calls for key " + key + " from region "
+ region.getName() + "; the key won't be cached until invalidation expires.");
}
return false;
}
/**
* Called after an item has been updated (after the transaction completes),
* instead of calling release(). This method is used by "asynchronous"
* concurrency strategies.
*
*
* @param session
* @param key The item key
* @param value The item
* @param currentVersion The item's current version value
* @param previousVersion The item's previous version value
* @param lock The lock previously obtained from {@link #lockItem}
* @return Were the contents of the cache actual changed by this operation?
* @throws CacheException Propagated from underlying {@link org.hibernate.cache.spi.Region}
*/
public boolean afterUpdate(SessionImplementor session, Object key, Object value, Object currentVersion, Object previousVersion, SoftLock lock) {
if ( !putValidator.endInvalidatingKey(session, key) ) {
// TODO: localization
log.warn("Failed to end invalidating pending putFromLoad calls for key " + key + " from region "
+ region.getName() + "; the key won't be cached until invalidation expires.");
}
return false;
}
} }

View File

@ -0,0 +1,136 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
package org.hibernate.cache.infinispan.access;
import org.hibernate.cache.CacheException;
import org.hibernate.cache.infinispan.impl.BaseRegion;
import org.hibernate.cache.spi.access.SoftLock;
import org.hibernate.engine.spi.SessionImplementor;
import org.hibernate.resource.transaction.TransactionCoordinator;
import org.hibernate.resource.transaction.spi.TransactionStatus;
import javax.transaction.Status;
import javax.transaction.SystemException;
import javax.transaction.TransactionManager;
/**
* Delegate for non-transactional caches
*
* @author Radim Vansa &lt;rvansa@redhat.com&gt;
*/
public class NonTxInvalidationCacheAccessDelegate extends InvalidationCacheAccessDelegate {
public NonTxInvalidationCacheAccessDelegate(BaseRegion region, PutFromLoadValidator validator) {
super(region, validator);
}
@Override
@SuppressWarnings("UnusedParameters")
public boolean insert(SessionImplementor session, Object key, Object value, Object version) throws CacheException {
if ( !region.checkValid() ) {
return false;
}
// We need to be invalidating even for regular writes; if we were not and the write was followed by eviction
// (or any other invalidation), naked put that was started after the eviction ended but before this insert
// ended could insert the stale entry into the cache (since the entry was removed by eviction).
if ( !putValidator.beginInvalidatingWithPFER(session, key, value)) {
throw new CacheException(
"Failed to invalidate pending putFromLoad calls for key " + key + " from region " + region.getName()
);
}
putValidator.setCurrentSession(session);
try {
writeCache.remove(key);
}
finally {
putValidator.resetCurrentSession();
}
return true;
}
@Override
@SuppressWarnings("UnusedParameters")
public boolean update(SessionImplementor session, Object key, Object value, Object currentVersion, Object previousVersion)
throws CacheException {
// We update whether or not the region is valid. Other nodes
// may have already restored the region so they need to
// be informed of the change.
// We need to be invalidating even for regular writes; if we were not and the write was followed by eviction
// (or any other invalidation), naked put that was started after the eviction ended but before this update
// ended could insert the stale entry into the cache (since the entry was removed by eviction).
if ( !putValidator.beginInvalidatingWithPFER(session, key, value)) {
throw new CacheException(
"Failed to invalidate pending putFromLoad calls for key " + key + " from region " + region.getName()
);
}
putValidator.setCurrentSession(session);
try {
writeCache.remove(key);
}
finally {
putValidator.resetCurrentSession();
}
return true;
}
protected boolean isCommitted(SessionImplementor session) {
if (session.isClosed()) {
// If the session has been closed before transaction ends, so we cannot find out
// if the transaction was successful and if we can do the PFER.
// As this can happen only in JTA environment, we can query the TransactionManager
// directly here.
TransactionManager tm = region.getTransactionManager();
if (tm != null) {
try {
switch (tm.getStatus()) {
case Status.STATUS_COMMITTED:
case Status.STATUS_COMMITTING:
return true;
default:
return false;
}
}
catch (SystemException e) {
log.debug("Failed to retrieve transaction status", e);
return false;
}
}
}
TransactionCoordinator tc = session.getTransactionCoordinator();
return tc != null && tc.getTransactionDriverControl().getStatus() == TransactionStatus.COMMITTED;
}
@Override
public void unlockItem(SessionImplementor session, Object key) throws CacheException {
if ( !putValidator.endInvalidatingKey(session, key, isCommitted(session)) ) {
// TODO: localization
log.warn("Failed to end invalidating pending putFromLoad calls for key " + key + " from region "
+ region.getName() + "; the key won't be cached until invalidation expires.");
}
}
@Override
public boolean afterInsert(SessionImplementor session, Object key, Object value, Object version) {
if ( !putValidator.endInvalidatingKey(session, key, isCommitted(session)) ) {
// TODO: localization
log.warn("Failed to end invalidating pending putFromLoad calls for key " + key + " from region "
+ region.getName() + "; the key won't be cached until invalidation expires.");
}
return false;
}
@Override
public boolean afterUpdate(SessionImplementor session, Object key, Object value, Object currentVersion, Object previousVersion, SoftLock lock) {
if ( !putValidator.endInvalidatingKey(session, key, isCommitted(session)) ) {
// TODO: localization
log.warn("Failed to end invalidating pending putFromLoad calls for key " + key + " from region "
+ region.getName() + "; the key won't be cached until invalidation expires.");
}
return false;
}
}

View File

@ -24,6 +24,7 @@ import org.hibernate.cache.spi.RegionFactory;
import org.hibernate.engine.spi.SessionImplementor; import org.hibernate.engine.spi.SessionImplementor;
import org.hibernate.resource.transaction.TransactionCoordinator; import org.hibernate.resource.transaction.TransactionCoordinator;
import org.infinispan.AdvancedCache; import org.infinispan.AdvancedCache;
import org.infinispan.configuration.cache.CacheMode;
import org.infinispan.configuration.cache.Configuration; import org.infinispan.configuration.cache.Configuration;
import org.infinispan.configuration.cache.ConfigurationBuilder; import org.infinispan.configuration.cache.ConfigurationBuilder;
import org.infinispan.interceptors.EntryWrappingInterceptor; import org.infinispan.interceptors.EntryWrappingInterceptor;
@ -34,8 +35,8 @@ import org.infinispan.util.logging.Log;
import org.infinispan.util.logging.LogFactory; import org.infinispan.util.logging.LogFactory;
/** /**
* Encapsulates logic to allow a {@link TransactionalAccessDelegate} to determine * Encapsulates logic to allow a {@link InvalidationCacheAccessDelegate} to determine
* whether a {@link TransactionalAccessDelegate#putFromLoad(org.hibernate.engine.spi.SessionImplementor, Object, Object, long, Object, boolean)} * whether a {@link InvalidationCacheAccessDelegate#putFromLoad(org.hibernate.engine.spi.SessionImplementor, Object, Object, long, Object, boolean)}
* call should be allowed to update the cache. A <code>putFromLoad</code> has * call should be allowed to update the cache. A <code>putFromLoad</code> has
* the potential to store stale data, since the data may have been removed from the * the potential to store stale data, since the data may have been removed from the
* database and the cache between the time when the data was read from the database * database and the cache between the time when the data was read from the database
@ -134,13 +135,10 @@ public class PutFromLoadValidator {
/** /**
* Creates a new put from load validator instance. * Creates a new put from load validator instance.
* * @param cache Cache instance on which to store pending put information.
* @param cache Cache instance on which to store pending put information. * @param cacheManager where to find a cache to store pending put information
* @param cacheManager where to find a cache to store pending put information */
*/ public PutFromLoadValidator(AdvancedCache cache, EmbeddedCacheManager cacheManager) {
public PutFromLoadValidator(AdvancedCache cache,
EmbeddedCacheManager cacheManager) {
Configuration cacheConfiguration = cache.getCacheConfiguration(); Configuration cacheConfiguration = cache.getCacheConfiguration();
Configuration pendingPutsConfiguration = cacheManager.getCacheConfiguration(InfinispanRegionFactory.PENDING_PUTS_CACHE_NAME); Configuration pendingPutsConfiguration = cacheManager.getCacheConfiguration(InfinispanRegionFactory.PENDING_PUTS_CACHE_NAME);
ConfigurationBuilder configurationBuilder = new ConfigurationBuilder(); ConfigurationBuilder configurationBuilder = new ConfigurationBuilder();
@ -155,11 +153,14 @@ public class PutFromLoadValidator {
else { else {
throw new IllegalArgumentException("Pending puts cache needs to have maxIdle expiration set!"); throw new IllegalArgumentException("Pending puts cache needs to have maxIdle expiration set!");
} }
CacheMode cacheMode = cache.getCacheConfiguration().clustering().cacheMode();
// Since we need to intercept both invalidations of entries that are in the cache and those // Since we need to intercept both invalidations of entries that are in the cache and those
// that are not, we need to use custom interceptor, not listeners (which fire only for present entries). // that are not, we need to use custom interceptor, not listeners (which fire only for present entries).
NonTxPutFromLoadInterceptor nonTxPutFromLoadInterceptor = null; NonTxPutFromLoadInterceptor nonTxPutFromLoadInterceptor = null;
if (cacheConfiguration.clustering().cacheMode().isClustered()) { if (cacheMode.isClustered()) {
if (!cacheMode.isInvalidation()) {
throw new IllegalArgumentException("PutFromLoadValidator in clustered caches requires invalidation mode.");
}
List<CommandInterceptor> interceptorChain = cache.getInterceptorChain(); List<CommandInterceptor> interceptorChain = cache.getInterceptorChain();
log.debug("Interceptor chain was: " + interceptorChain); log.debug("Interceptor chain was: " + interceptorChain);
int position = 0; int position = 0;

View File

@ -8,17 +8,16 @@ package org.hibernate.cache.infinispan.access;
import org.hibernate.cache.CacheException; import org.hibernate.cache.CacheException;
import org.hibernate.cache.infinispan.impl.BaseRegion; import org.hibernate.cache.infinispan.impl.BaseRegion;
import org.hibernate.cache.spi.access.SoftLock;
import org.hibernate.engine.spi.SessionImplementor; import org.hibernate.engine.spi.SessionImplementor;
import org.hibernate.resource.transaction.TransactionCoordinator;
import org.hibernate.resource.transaction.spi.TransactionStatus;
/** /**
* Delegate for non-transactional caches * Delegate for transactional caches
* *
* @author Radim Vansa &lt;rvansa@redhat.com&gt; * @author Radim Vansa &lt;rvansa@redhat.com&gt;
*/ */
public class NonTxTransactionalAccessDelegate extends TransactionalAccessDelegate { public class TxInvalidationCacheAccessDelegate extends InvalidationCacheAccessDelegate {
public NonTxTransactionalAccessDelegate(BaseRegion region, PutFromLoadValidator validator) { public TxInvalidationCacheAccessDelegate(BaseRegion region, PutFromLoadValidator validator) {
super(region, validator); super(region, validator);
} }
@ -32,14 +31,14 @@ public class NonTxTransactionalAccessDelegate extends TransactionalAccessDelegat
// We need to be invalidating even for regular writes; if we were not and the write was followed by eviction // We need to be invalidating even for regular writes; if we were not and the write was followed by eviction
// (or any other invalidation), naked put that was started after the eviction ended but before this insert // (or any other invalidation), naked put that was started after the eviction ended but before this insert
// ended could insert the stale entry into the cache (since the entry was removed by eviction). // ended could insert the stale entry into the cache (since the entry was removed by eviction).
if ( !putValidator.beginInvalidatingWithPFER(session, key, value)) { if ( !putValidator.beginInvalidatingKey(session, key)) {
throw new CacheException( throw new CacheException(
"Failed to invalidate pending putFromLoad calls for key " + key + " from region " + region.getName() "Failed to invalidate pending putFromLoad calls for key " + key + " from region " + region.getName()
); );
} }
putValidator.setCurrentSession(session); putValidator.setCurrentSession(session);
try { try {
writeCache.remove(key); writeCache.put(key, value);
} }
finally { finally {
putValidator.resetCurrentSession(); putValidator.resetCurrentSession();
@ -58,14 +57,14 @@ public class NonTxTransactionalAccessDelegate extends TransactionalAccessDelegat
// We need to be invalidating even for regular writes; if we were not and the write was followed by eviction // We need to be invalidating even for regular writes; if we were not and the write was followed by eviction
// (or any other invalidation), naked put that was started after the eviction ended but before this update // (or any other invalidation), naked put that was started after the eviction ended but before this update
// ended could insert the stale entry into the cache (since the entry was removed by eviction). // ended could insert the stale entry into the cache (since the entry was removed by eviction).
if ( !putValidator.beginInvalidatingWithPFER(session, key, value)) { if ( !putValidator.beginInvalidatingKey(session, key)) {
throw new CacheException( throw new CacheException(
"Failed to invalidate pending putFromLoad calls for key " + key + " from region " + region.getName() "Failed to invalidate pending putFromLoad calls for key " + key + " from region " + region.getName()
); );
} }
putValidator.setCurrentSession(session); putValidator.setCurrentSession(session);
try { try {
writeCache.remove(key); writeCache.put(key, value);
} }
finally { finally {
putValidator.resetCurrentSession(); putValidator.resetCurrentSession();
@ -74,13 +73,22 @@ public class NonTxTransactionalAccessDelegate extends TransactionalAccessDelegat
} }
@Override @Override
public void unlockItem(SessionImplementor session, Object key) throws CacheException { public boolean afterInsert(SessionImplementor session, Object key, Object value, Object version) {
TransactionCoordinator tc = session.getTransactionCoordinator(); if ( !putValidator.endInvalidatingKey(session, key) ) {
boolean doPFER = tc != null && tc.getTransactionDriverControl().getStatus() == TransactionStatus.COMMITTED;
if ( !putValidator.endInvalidatingKey(session, key, doPFER) ) {
// TODO: localization // TODO: localization
log.warn("Failed to end invalidating pending putFromLoad calls for key " + key + " from region " log.warn("Failed to end invalidating pending putFromLoad calls for key " + key + " from region "
+ region.getName() + "; the key won't be cached until invalidation expires."); + region.getName() + "; the key won't be cached until invalidation expires.");
} }
return false;
}
@Override
public boolean afterUpdate(SessionImplementor session, Object key, Object value, Object currentVersion, Object previousVersion, SoftLock lock) {
if ( !putValidator.endInvalidatingKey(session, key) ) {
// TODO: localization
log.warn("Failed to end invalidating pending putFromLoad calls for key " + key + " from region "
+ region.getName() + "; the key won't be cached until invalidation expires.");
}
return false;
} }
} }

View File

@ -1,73 +0,0 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
package org.hibernate.cache.infinispan.access;
import org.hibernate.cache.CacheException;
import org.hibernate.cache.infinispan.impl.BaseRegion;
import org.hibernate.engine.spi.SessionImplementor;
/**
* Delegate for transactional caches
*
* @author Radim Vansa &lt;rvansa@redhat.com&gt;
*/
public class TxTransactionalAccessDelegate extends TransactionalAccessDelegate {
public TxTransactionalAccessDelegate(BaseRegion region, PutFromLoadValidator validator) {
super(region, validator);
}
@Override
@SuppressWarnings("UnusedParameters")
public boolean insert(SessionImplementor session, Object key, Object value, Object version) throws CacheException {
if ( !region.checkValid() ) {
return false;
}
// We need to be invalidating even for regular writes; if we were not and the write was followed by eviction
// (or any other invalidation), naked put that was started after the eviction ended but before this insert
// ended could insert the stale entry into the cache (since the entry was removed by eviction).
if ( !putValidator.beginInvalidatingKey(session, key)) {
throw new CacheException(
"Failed to invalidate pending putFromLoad calls for key " + key + " from region " + region.getName()
);
}
putValidator.setCurrentSession(session);
try {
writeCache.put(key, value);
}
finally {
putValidator.resetCurrentSession();
}
return true;
}
@Override
@SuppressWarnings("UnusedParameters")
public boolean update(SessionImplementor session, Object key, Object value, Object currentVersion, Object previousVersion)
throws CacheException {
// We update whether or not the region is valid. Other nodes
// may have already restored the region so they need to
// be informed of the change.
// We need to be invalidating even for regular writes; if we were not and the write was followed by eviction
// (or any other invalidation), naked put that was started after the eviction ended but before this update
// ended could insert the stale entry into the cache (since the entry was removed by eviction).
if ( !putValidator.beginInvalidatingKey(session, key)) {
throw new CacheException(
"Failed to invalidate pending putFromLoad calls for key " + key + " from region " + region.getName()
);
}
putValidator.setCurrentSession(session);
try {
writeCache.put(key, value);
}
finally {
putValidator.resetCurrentSession();
}
return true;
}
}

View File

@ -7,7 +7,7 @@
package org.hibernate.cache.infinispan.collection; package org.hibernate.cache.infinispan.collection;
import org.hibernate.cache.CacheException; import org.hibernate.cache.CacheException;
import org.hibernate.cache.infinispan.access.TransactionalAccessDelegate; import org.hibernate.cache.infinispan.access.AccessDelegate;
import org.hibernate.cache.spi.CollectionRegion; import org.hibernate.cache.spi.CollectionRegion;
import org.hibernate.cache.spi.access.CollectionRegionAccessStrategy; import org.hibernate.cache.spi.access.CollectionRegionAccessStrategy;
import org.hibernate.cache.spi.access.SoftLock; import org.hibernate.cache.spi.access.SoftLock;
@ -16,21 +16,19 @@ import org.hibernate.engine.spi.SessionImplementor;
import org.hibernate.persister.collection.CollectionPersister; import org.hibernate.persister.collection.CollectionPersister;
/** /**
* Transactional collection region access for Infinispan. * Collection region access for Infinispan.
* *
* @author Chris Bredesen * @author Chris Bredesen
* @author Galder Zamarreño * @author Galder Zamarreño
* @since 3.5 * @since 3.5
*/ */
class TransactionalAccess implements CollectionRegionAccessStrategy { class CollectionAccess implements CollectionRegionAccessStrategy {
private final CollectionRegionImpl region; private final CollectionRegionImpl region;
private final AccessDelegate delegate;
private final TransactionalAccessDelegate delegate; CollectionAccess(CollectionRegionImpl region, AccessDelegate delegate) {
TransactionalAccess(CollectionRegionImpl region) {
this.region = region; this.region = region;
this.delegate = TransactionalAccessDelegate.create( region, region.getPutFromLoadValidator() ); this.delegate = delegate;
} }
public void evict(Object key) throws CacheException { public void evict(Object key) throws CacheException {

View File

@ -7,7 +7,8 @@
package org.hibernate.cache.infinispan.collection; package org.hibernate.cache.infinispan.collection;
import org.hibernate.cache.CacheException; import org.hibernate.cache.CacheException;
import org.hibernate.cache.infinispan.access.PutFromLoadValidator; import org.hibernate.cache.infinispan.access.AccessDelegate;
import org.hibernate.cache.infinispan.access.InvalidationCacheAccessDelegate;
import org.hibernate.cache.infinispan.impl.BaseTransactionalDataRegion; import org.hibernate.cache.infinispan.impl.BaseTransactionalDataRegion;
import org.hibernate.cache.spi.CacheDataDescription; import org.hibernate.cache.spi.CacheDataDescription;
import org.hibernate.cache.spi.CacheKeysFactory; import org.hibernate.cache.spi.CacheKeysFactory;
@ -27,17 +28,16 @@ import javax.transaction.TransactionManager;
* @since 3.5 * @since 3.5
*/ */
public class CollectionRegionImpl extends BaseTransactionalDataRegion implements CollectionRegion { public class CollectionRegionImpl extends BaseTransactionalDataRegion implements CollectionRegion {
/**
/** * Construct a collection region
* Construct a collection region *
* * @param cache instance to store collection instances
* @param cache instance to store collection instances * @param name of collection type
* @param name of collection type
* @param transactionManager * @param transactionManager
* @param metadata for the collection type * @param metadata for the collection type
* @param factory for the region * @param factory for the region
* @param cacheKeysFactory factory for cache keys * @param cacheKeysFactory factory for cache keys
*/ */
public CollectionRegionImpl( public CollectionRegionImpl(
AdvancedCache cache, String name, TransactionManager transactionManager, AdvancedCache cache, String name, TransactionManager transactionManager,
CacheDataDescription metadata, RegionFactory factory, CacheKeysFactory cacheKeysFactory) { CacheDataDescription metadata, RegionFactory factory, CacheKeysFactory cacheKeysFactory) {
@ -46,16 +46,16 @@ public class CollectionRegionImpl extends BaseTransactionalDataRegion implements
@Override @Override
public CollectionRegionAccessStrategy buildAccessStrategy(AccessType accessType) throws CacheException { public CollectionRegionAccessStrategy buildAccessStrategy(AccessType accessType) throws CacheException {
if ( AccessType.READ_ONLY.equals( accessType ) checkAccessType( accessType );
|| AccessType.TRANSACTIONAL.equals( accessType ) ) { getValidator();
return new TransactionalAccess( this ); AccessDelegate delegate = InvalidationCacheAccessDelegate.create(this, getValidator());
switch ( accessType ) {
case READ_ONLY:
case READ_WRITE:
case TRANSACTIONAL:
return new CollectionAccess( this, delegate );
default:
throw new CacheException( "Unsupported access type [" + accessType.getExternalName() + "]" );
} }
throw new CacheException( "Unsupported access type [" + accessType.getExternalName() + "]" );
} }
public PutFromLoadValidator getPutFromLoadValidator() {
return new PutFromLoadValidator( cache );
}
} }

View File

@ -7,7 +7,7 @@
package org.hibernate.cache.infinispan.entity; package org.hibernate.cache.infinispan.entity;
import org.hibernate.cache.CacheException; import org.hibernate.cache.CacheException;
import org.hibernate.cache.infinispan.access.PutFromLoadValidator; import org.hibernate.cache.infinispan.access.InvalidationCacheAccessDelegate;
import org.hibernate.cache.infinispan.impl.BaseTransactionalDataRegion; import org.hibernate.cache.infinispan.impl.BaseTransactionalDataRegion;
import org.hibernate.cache.spi.CacheDataDescription; import org.hibernate.cache.spi.CacheDataDescription;
import org.hibernate.cache.spi.CacheKeysFactory; import org.hibernate.cache.spi.CacheKeysFactory;
@ -28,17 +28,16 @@ import javax.transaction.TransactionManager;
* @since 3.5 * @since 3.5
*/ */
public class EntityRegionImpl extends BaseTransactionalDataRegion implements EntityRegion { public class EntityRegionImpl extends BaseTransactionalDataRegion implements EntityRegion {
/**
/** * Construct a entity region
* Construct a entity region *
* * @param cache instance to store entity instances
* @param cache instance to store entity instances * @param name of entity type
* @param name of entity type
* @param transactionManager * @param transactionManager
* @param metadata for the entity type * @param metadata for the entity type
* @param factory for the region * @param factory for the region
* @param cacheKeysFactory factory for cache keys * @param cacheKeysFactory factory for cache keys
*/ */
public EntityRegionImpl( public EntityRegionImpl(
AdvancedCache cache, String name, TransactionManager transactionManager, AdvancedCache cache, String name, TransactionManager transactionManager,
CacheDataDescription metadata, RegionFactory factory, CacheKeysFactory cacheKeysFactory) { CacheDataDescription metadata, RegionFactory factory, CacheKeysFactory cacheKeysFactory) {
@ -47,22 +46,19 @@ public class EntityRegionImpl extends BaseTransactionalDataRegion implements Ent
@Override @Override
public EntityRegionAccessStrategy buildAccessStrategy(AccessType accessType) throws CacheException { public EntityRegionAccessStrategy buildAccessStrategy(AccessType accessType) throws CacheException {
checkAccessType(accessType);
if ( !getCacheDataDescription().isMutable() ) {
accessType = AccessType.READ_ONLY;
}
InvalidationCacheAccessDelegate accessDelegate = InvalidationCacheAccessDelegate.create(this, getValidator());
switch ( accessType ) { switch ( accessType ) {
case READ_ONLY: case READ_ONLY:
return new ReadOnlyAccess( this ); return new ReadOnlyAccess( this, accessDelegate);
case READ_WRITE:
case TRANSACTIONAL: case TRANSACTIONAL:
if ( getCacheDataDescription().isMutable() ) { return new ReadWriteAccess( this, accessDelegate);
return new TransactionalAccess( this );
}
else {
return new ReadOnlyAccess( this );
}
default: default:
throw new CacheException( "Unsupported access type [" + accessType.getExternalName() + "]" ); throw new CacheException( "Unsupported access type [" + accessType.getExternalName() + "]" );
} }
} }
public PutFromLoadValidator getPutFromLoadValidator() {
return new PutFromLoadValidator( cache );
}
} }

View File

@ -7,21 +7,66 @@
package org.hibernate.cache.infinispan.entity; package org.hibernate.cache.infinispan.entity;
import org.hibernate.cache.CacheException; import org.hibernate.cache.CacheException;
import org.hibernate.cache.infinispan.access.InvalidationCacheAccessDelegate;
import org.hibernate.cache.spi.EntityRegion;
import org.hibernate.cache.spi.access.EntityRegionAccessStrategy;
import org.hibernate.cache.spi.access.SoftLock; import org.hibernate.cache.spi.access.SoftLock;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.engine.spi.SessionImplementor; import org.hibernate.engine.spi.SessionImplementor;
import org.hibernate.persister.entity.EntityPersister;
/** /**
* A specialization of {@link TransactionalAccess} that ensures we never update data. Infinispan * A specialization of {@link ReadWriteAccess} that ensures we never update data.
* access is always transactional.
* *
* @author Chris Bredesen * @author Chris Bredesen
* @author Galder Zamarreño * @author Galder Zamarreño
* @since 3.5 * @since 3.5
*/ */
class ReadOnlyAccess extends TransactionalAccess { class ReadOnlyAccess implements EntityRegionAccessStrategy {
ReadOnlyAccess(EntityRegionImpl region) { protected final EntityRegionImpl region;
super( region ); protected final InvalidationCacheAccessDelegate delegate;
ReadOnlyAccess(EntityRegionImpl region, InvalidationCacheAccessDelegate delegate) {
this.region = region;
this.delegate = delegate;
}
public void evict(Object key) throws CacheException {
delegate.evict( key );
}
public void evictAll() throws CacheException {
delegate.evictAll();
}
public Object get(SessionImplementor session, Object key, long txTimestamp) throws CacheException {
return delegate.get( session, key, txTimestamp );
}
public EntityRegion getRegion() {
return this.region;
}
public boolean putFromLoad(SessionImplementor session, Object key, Object value, long txTimestamp, Object version) throws CacheException {
return delegate.putFromLoad( session, key, value, txTimestamp, version );
}
public boolean putFromLoad(SessionImplementor session, Object key, Object value, long txTimestamp, Object version, boolean minimalPutOverride)
throws CacheException {
return delegate.putFromLoad( session, key, value, txTimestamp, version, minimalPutOverride );
}
public void remove(SessionImplementor session, Object key) throws CacheException {
delegate.remove ( session, key );
}
public void removeAll() throws CacheException {
delegate.removeAll();
}
public boolean insert(SessionImplementor session, Object key, Object value, Object version) throws CacheException {
return delegate.insert( session, key, value, version );
} }
@Override @Override
@ -31,6 +76,25 @@ class ReadOnlyAccess extends TransactionalAccess {
throw new UnsupportedOperationException( "Illegal attempt to edit read only item" ); throw new UnsupportedOperationException( "Illegal attempt to edit read only item" );
} }
public SoftLock lockItem(SessionImplementor session, Object key, Object version) throws CacheException {
return null;
}
public SoftLock lockRegion() throws CacheException {
return null;
}
public void unlockItem(SessionImplementor session, Object key, SoftLock lock) throws CacheException {
delegate.unlockItem( session, key );
}
public void unlockRegion(SoftLock lock) throws CacheException {
}
public boolean afterInsert(SessionImplementor session, Object key, Object value, Object version) throws CacheException {
return delegate.afterInsert( session, key, value, version );
}
@Override @Override
public boolean afterUpdate( public boolean afterUpdate(
SessionImplementor session, Object key, Object value, Object currentVersion, SessionImplementor session, Object key, Object value, Object currentVersion,
@ -38,4 +102,13 @@ class ReadOnlyAccess extends TransactionalAccess {
throw new UnsupportedOperationException( "Illegal attempt to edit read only item" ); throw new UnsupportedOperationException( "Illegal attempt to edit read only item" );
} }
@Override
public Object generateCacheKey(Object id, EntityPersister persister, SessionFactoryImplementor factory, String tenantIdentifier) {
return region.getCacheKeysFactory().createEntityKey(id, persister, factory, tenantIdentifier);
}
@Override
public Object getCacheKeyId(Object cacheKey) {
return region.getCacheKeysFactory().getEntityId(cacheKey);
}
} }

View File

@ -0,0 +1,36 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
package org.hibernate.cache.infinispan.entity;
import org.hibernate.cache.CacheException;
import org.hibernate.cache.infinispan.access.InvalidationCacheAccessDelegate;
import org.hibernate.cache.spi.access.SoftLock;
import org.hibernate.engine.spi.SessionImplementor;
/**
* Read-write or transactional entity region access for Infinispan.
*
* @author Chris Bredesen
* @author Galder Zamarreño
* @since 3.5
*/
class ReadWriteAccess extends ReadOnlyAccess {
ReadWriteAccess(EntityRegionImpl region, InvalidationCacheAccessDelegate delegate) {
super(region, delegate);
}
public boolean update(SessionImplementor session, Object key, Object value, Object currentVersion, Object previousVersion)
throws CacheException {
return delegate.update( session, key, value, currentVersion, previousVersion );
}
public boolean afterUpdate(SessionImplementor session, Object key, Object value, Object currentVersion, Object previousVersion, SoftLock lock)
throws CacheException {
return delegate.afterUpdate( session, key, value, currentVersion, previousVersion, lock );
}
}

View File

@ -1,111 +0,0 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
package org.hibernate.cache.infinispan.entity;
import org.hibernate.cache.CacheException;
import org.hibernate.cache.infinispan.access.TransactionalAccessDelegate;
import org.hibernate.cache.spi.EntityRegion;
import org.hibernate.cache.spi.access.EntityRegionAccessStrategy;
import org.hibernate.cache.spi.access.SoftLock;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.engine.spi.SessionImplementor;
import org.hibernate.persister.entity.EntityPersister;
/**
* 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 = TransactionalAccessDelegate.create( region, region.getPutFromLoadValidator() );
}
public void evict(Object key) throws CacheException {
delegate.evict( key );
}
public void evictAll() throws CacheException {
delegate.evictAll();
}
public Object get(SessionImplementor session, Object key, long txTimestamp) throws CacheException {
return delegate.get( session, key, txTimestamp );
}
public EntityRegion getRegion() {
return this.region;
}
public boolean insert(SessionImplementor session, Object key, Object value, Object version) throws CacheException {
return delegate.insert( session, key, value, version );
}
public boolean putFromLoad(SessionImplementor session, Object key, Object value, long txTimestamp, Object version) throws CacheException {
return delegate.putFromLoad( session, key, value, txTimestamp, version );
}
public boolean putFromLoad(SessionImplementor session, Object key, Object value, long txTimestamp, Object version, boolean minimalPutOverride)
throws CacheException {
return delegate.putFromLoad( session, key, value, txTimestamp, version, minimalPutOverride );
}
public void remove(SessionImplementor session, Object key) throws CacheException {
delegate.remove ( session, key );
}
public void removeAll() throws CacheException {
delegate.removeAll();
}
public boolean update(SessionImplementor session, Object key, Object value, Object currentVersion, Object previousVersion)
throws CacheException {
return delegate.update( session, key, value, currentVersion, previousVersion );
}
public SoftLock lockItem(SessionImplementor session, Object key, Object version) throws CacheException {
return null;
}
public SoftLock lockRegion() throws CacheException {
return null;
}
public void unlockItem(SessionImplementor session, Object key, SoftLock lock) throws CacheException {
delegate.unlockItem( session, key );
}
public void unlockRegion(SoftLock lock) throws CacheException {
}
public boolean afterInsert(SessionImplementor session, Object key, Object value, Object version) throws CacheException {
return delegate.afterInsert( session, key, value, version );
}
public boolean afterUpdate(SessionImplementor session, Object key, Object value, Object currentVersion, Object previousVersion, SoftLock lock)
throws CacheException {
return delegate.afterUpdate( session, key, value, currentVersion, previousVersion, lock );
}
@Override
public Object generateCacheKey(Object id, EntityPersister persister, SessionFactoryImplementor factory, String tenantIdentifier) {
return region.getCacheKeysFactory().createEntityKey(id, persister, factory, tenantIdentifier);
}
@Override
public Object getCacheKeyId(Object cacheKey) {
return region.getCacheKeysFactory().getEntityId(cacheKey);
}
}

View File

@ -14,10 +14,12 @@ import javax.transaction.Transaction;
import javax.transaction.TransactionManager; import javax.transaction.TransactionManager;
import org.hibernate.cache.CacheException; import org.hibernate.cache.CacheException;
import org.hibernate.cache.infinispan.access.PutFromLoadValidator;
import org.hibernate.cache.infinispan.util.Caches; import org.hibernate.cache.infinispan.util.Caches;
import org.hibernate.cache.spi.Region; import org.hibernate.cache.spi.Region;
import org.hibernate.cache.spi.RegionFactory; import org.hibernate.cache.spi.RegionFactory;
import org.hibernate.cache.spi.access.AccessType;
import org.infinispan.AdvancedCache; import org.infinispan.AdvancedCache;
import org.infinispan.context.Flag; import org.infinispan.context.Flag;
import org.infinispan.util.logging.Log; import org.infinispan.util.logging.Log;
@ -35,7 +37,6 @@ import org.infinispan.util.logging.LogFactory;
public abstract class BaseRegion implements Region { public abstract class BaseRegion implements Region {
private static final Log log = LogFactory.getLog( BaseRegion.class ); private static final Log log = LogFactory.getLog( BaseRegion.class );
private Transaction currentTransaction;
private enum InvalidateState { private enum InvalidateState {
INVALID, CLEARING, VALID INVALID, CLEARING, VALID
@ -48,12 +49,13 @@ public abstract class BaseRegion implements Region {
private final Object invalidationMutex = new Object(); private final Object invalidationMutex = new Object();
private final AtomicReference<InvalidateState> invalidateState = private final AtomicReference<InvalidateState> invalidateState =
new AtomicReference<InvalidateState>( InvalidateState.VALID ); new AtomicReference<InvalidateState>( InvalidateState.VALID );
private volatile Transaction invalidateTransaction;
private final RegionFactory factory; private final RegionFactory factory;
protected final AdvancedCache cache; protected final AdvancedCache cache;
private PutFromLoadValidator validator;
/** /**
* Base region constructor. * Base region constructor.
* *
@ -110,7 +112,7 @@ public abstract class BaseRegion implements Region {
@Override @Override
public int getTimeout() { public int getTimeout() {
// 60 seconds // 60 seconds
return 600; return 60000;
} }
@Override @Override
@ -149,21 +151,7 @@ public abstract class BaseRegion implements Region {
synchronized (invalidationMutex) { synchronized (invalidationMutex) {
if ( invalidateState.compareAndSet( InvalidateState.INVALID, InvalidateState.CLEARING ) ) { if ( invalidateState.compareAndSet( InvalidateState.INVALID, InvalidateState.CLEARING ) ) {
try { try {
// If we're running inside a transaction, we need to remove elements one-by-one runInvalidation( getCurrentTransaction() != null );
// to clean the context as well (cache.clear() does not do that).
// When we don't have transaction, we can do a clear operation (since we don't
// case about context) and can't do the one-by-one remove: remove() on tx cache
// requires transactional context.
Transaction tx = getCurrentTransaction();
if ( tx != null ) {
log.tracef( "Transaction, clearing one element at the time" );
Caches.removeAll( localAndSkipLoadCache );
}
else {
log.tracef( "Non-transactional, clear in one go" );
localAndSkipLoadCache.clear();
}
log.tracef( "Transition state from CLEARING to VALID" ); log.tracef( "Transition state from CLEARING to VALID" );
invalidateState.compareAndSet( invalidateState.compareAndSet(
InvalidateState.CLEARING, InvalidateState.VALID InvalidateState.CLEARING, InvalidateState.VALID
@ -228,7 +216,7 @@ public abstract class BaseRegion implements Region {
if (log.isTraceEnabled()) { if (log.isTraceEnabled()) {
log.trace( "Invalidate region: " + name ); log.trace( "Invalidate region: " + name );
} }
invalidateState.set( InvalidateState.INVALID ); invalidateState.set(InvalidateState.INVALID);
} }
public TransactionManager getTransactionManager() { public TransactionManager getTransactionManager() {
@ -255,4 +243,35 @@ public abstract class BaseRegion implements Region {
} }
} }
protected void checkAccessType(AccessType accessType) {
if (accessType == AccessType.TRANSACTIONAL && !cache.getCacheConfiguration().transaction().transactionMode().isTransactional()) {
log.warn("Requesting TRANSACTIONAL cache concurrency strategy but the cache is not configured as transactional.");
}
else if (accessType == AccessType.READ_WRITE && cache.getCacheConfiguration().transaction().transactionMode().isTransactional()) {
log.warn("Requesting READ_WRITE cache concurrency strategy but the cache was configured as transactional.");
}
}
protected synchronized PutFromLoadValidator getValidator() {
if (validator == null) {
validator = new PutFromLoadValidator(cache);
}
return validator;
}
protected void runInvalidation(boolean inTransaction) {
// If we're running inside a transaction, we need to remove elements one-by-one
// to clean the context as well (cache.clear() does not do that).
// When we don't have transaction, we can do a clear operation (since we don't
// case about context) and can't do the one-by-one remove: remove() on tx cache
// requires transactional context.
if ( inTransaction ) {
log.tracef( "Transaction, clearing one element at the time" );
Caches.removeAll( localAndSkipLoadCache );
}
else {
log.tracef( "Non-transactional, clear in one go" );
localAndSkipLoadCache.clear();
}
}
} }

View File

@ -7,7 +7,9 @@
package org.hibernate.cache.infinispan.naturalid; package org.hibernate.cache.infinispan.naturalid;
import org.hibernate.cache.CacheException; import org.hibernate.cache.CacheException;
import org.hibernate.cache.infinispan.access.AccessDelegate;
import org.hibernate.cache.infinispan.access.PutFromLoadValidator; import org.hibernate.cache.infinispan.access.PutFromLoadValidator;
import org.hibernate.cache.infinispan.access.InvalidationCacheAccessDelegate;
import org.hibernate.cache.infinispan.impl.BaseTransactionalDataRegion; import org.hibernate.cache.infinispan.impl.BaseTransactionalDataRegion;
import org.hibernate.cache.spi.CacheDataDescription; import org.hibernate.cache.spi.CacheDataDescription;
import org.hibernate.cache.spi.CacheKeysFactory; import org.hibernate.cache.spi.CacheKeysFactory;
@ -46,18 +48,19 @@ public class NaturalIdRegionImpl extends BaseTransactionalDataRegion
@Override @Override
public NaturalIdRegionAccessStrategy buildAccessStrategy(AccessType accessType) throws CacheException { public NaturalIdRegionAccessStrategy buildAccessStrategy(AccessType accessType) throws CacheException {
checkAccessType( accessType );
if (!getCacheDataDescription().isMutable()) {
accessType = AccessType.READ_ONLY;
}
AccessDelegate delegate = InvalidationCacheAccessDelegate.create( this, getValidator());
switch ( accessType ) { switch ( accessType ) {
case READ_ONLY: case READ_ONLY:
return new ReadOnlyAccess( this ); return new ReadOnlyAccess( this, delegate );
case READ_WRITE:
case TRANSACTIONAL: case TRANSACTIONAL:
return new TransactionalAccess( this ); return new ReadWriteAccess( this, delegate );
default: default:
throw new CacheException( "Unsupported access type [" + accessType.getExternalName() + "]" ); throw new CacheException( "Unsupported access type [" + accessType.getExternalName() + "]" );
} }
} }
public PutFromLoadValidator getPutFromLoadValidator() {
return new PutFromLoadValidator( cache );
}
} }

View File

@ -7,16 +7,29 @@
package org.hibernate.cache.infinispan.naturalid; package org.hibernate.cache.infinispan.naturalid;
import org.hibernate.cache.CacheException; import org.hibernate.cache.CacheException;
import org.hibernate.cache.infinispan.access.AccessDelegate;
import org.hibernate.cache.spi.NaturalIdRegion;
import org.hibernate.cache.spi.access.NaturalIdRegionAccessStrategy;
import org.hibernate.cache.spi.access.SoftLock; import org.hibernate.cache.spi.access.SoftLock;
import org.hibernate.engine.spi.SessionImplementor; import org.hibernate.engine.spi.SessionImplementor;
import org.hibernate.persister.entity.EntityPersister;
/** /**
* @author Strong Liu <stliu@hibernate.org> * @author Strong Liu <stliu@hibernate.org>
*/ */
class ReadOnlyAccess extends TransactionalAccess { class ReadOnlyAccess implements NaturalIdRegionAccessStrategy {
ReadOnlyAccess(NaturalIdRegionImpl naturalIdRegion) { protected final NaturalIdRegionImpl region;
super( naturalIdRegion ); protected final AccessDelegate delegate;
ReadOnlyAccess(NaturalIdRegionImpl region, AccessDelegate delegate) {
this.region = region;
this.delegate = delegate;
}
@Override
public boolean insert(SessionImplementor session, Object key, Object value) throws CacheException {
return delegate.insert( session, key, value, null );
} }
@Override @Override
@ -24,9 +37,83 @@ class ReadOnlyAccess extends TransactionalAccess {
throw new UnsupportedOperationException( "Illegal attempt to edit read only item" ); throw new UnsupportedOperationException( "Illegal attempt to edit read only item" );
} }
@Override
public NaturalIdRegion getRegion() {
return region;
}
@Override
public void evict(Object key) throws CacheException {
delegate.evict( key );
}
@Override
public void evictAll() throws CacheException {
delegate.evictAll();
}
@Override
public Object get(SessionImplementor session, Object key, long txTimestamp) throws CacheException {
return delegate.get( session, key, txTimestamp );
}
@Override
public boolean putFromLoad(SessionImplementor session, Object key, Object value, long txTimestamp, Object version) throws CacheException {
return delegate.putFromLoad( session, key, value, txTimestamp, version );
}
@Override
public boolean putFromLoad(SessionImplementor session, Object key, Object value, long txTimestamp, Object version, boolean minimalPutOverride)
throws CacheException {
return delegate.putFromLoad( session, key, value, txTimestamp, version, minimalPutOverride );
}
@Override
public void remove(SessionImplementor session, Object key) throws CacheException {
delegate.remove( session, key );
}
@Override
public void removeAll() throws CacheException {
delegate.removeAll();
}
@Override
public SoftLock lockItem(SessionImplementor session, Object key, Object version) throws CacheException {
return null;
}
@Override
public SoftLock lockRegion() throws CacheException {
return null;
}
@Override
public void unlockItem(SessionImplementor session, Object key, SoftLock lock) throws CacheException {
delegate.unlockItem( session, key );
}
@Override
public void unlockRegion(SoftLock lock) throws CacheException {
}
@Override
public boolean afterInsert(SessionImplementor session, Object key, Object value) throws CacheException {
return delegate.afterInsert( session, key, value, null );
}
@Override @Override
public boolean afterUpdate(SessionImplementor session, Object key, Object value, SoftLock lock) throws CacheException { public boolean afterUpdate(SessionImplementor session, Object key, Object value, SoftLock lock) throws CacheException {
throw new UnsupportedOperationException( "Illegal attempt to edit read only item" ); throw new UnsupportedOperationException( "Illegal attempt to edit read only item" );
} }
@Override
public Object generateCacheKey(Object[] naturalIdValues, EntityPersister persister, SessionImplementor session) {
return region.getCacheKeysFactory().createNaturalIdKey(naturalIdValues, persister, session);
}
@Override
public Object[] getNaturalIdValues(Object cacheKey) {
return region.getCacheKeysFactory().getNaturalIdValues(cacheKey);
}
} }

View File

@ -0,0 +1,33 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
package org.hibernate.cache.infinispan.naturalid;
import org.hibernate.cache.CacheException;
import org.hibernate.cache.infinispan.access.AccessDelegate;
import org.hibernate.cache.spi.access.SoftLock;
import org.hibernate.engine.spi.SessionImplementor;
/**
* @author Strong Liu <stliu@hibernate.org>
*/
class ReadWriteAccess extends ReadOnlyAccess {
ReadWriteAccess(NaturalIdRegionImpl region, AccessDelegate delegate) {
super(region, delegate);
}
@Override
public boolean update(SessionImplementor session, Object key, Object value) throws CacheException {
return delegate.update( session, key, value, null, null );
}
@Override
public boolean afterUpdate(SessionImplementor session, Object key, Object value, SoftLock lock) throws CacheException {
return delegate.afterUpdate( session, key, value, null, null, lock );
}
}

View File

@ -1,118 +0,0 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
package org.hibernate.cache.infinispan.naturalid;
import org.hibernate.cache.CacheException;
import org.hibernate.cache.infinispan.access.TransactionalAccessDelegate;
import org.hibernate.cache.spi.NaturalIdRegion;
import org.hibernate.cache.spi.access.NaturalIdRegionAccessStrategy;
import org.hibernate.cache.spi.access.SoftLock;
import org.hibernate.engine.spi.SessionImplementor;
import org.hibernate.persister.entity.EntityPersister;
/**
* @author Strong Liu <stliu@hibernate.org>
*/
class TransactionalAccess implements NaturalIdRegionAccessStrategy {
private final NaturalIdRegionImpl region;
private final TransactionalAccessDelegate delegate;
TransactionalAccess(NaturalIdRegionImpl region) {
this.region = region;
this.delegate = TransactionalAccessDelegate.create( region, region.getPutFromLoadValidator() );
}
@Override
public boolean insert(SessionImplementor session, Object key, Object value) throws CacheException {
return delegate.insert( session, key, value, null );
}
@Override
public boolean update(SessionImplementor session, Object key, Object value) throws CacheException {
return delegate.update( session, key, value, null, null );
}
@Override
public NaturalIdRegion getRegion() {
return region;
}
@Override
public void evict(Object key) throws CacheException {
delegate.evict( key );
}
@Override
public void evictAll() throws CacheException {
delegate.evictAll();
}
@Override
public Object get(SessionImplementor session, Object key, long txTimestamp) throws CacheException {
return delegate.get( session, key, txTimestamp );
}
@Override
public boolean putFromLoad(SessionImplementor session, Object key, Object value, long txTimestamp, Object version) throws CacheException {
return delegate.putFromLoad( session, key, value, txTimestamp, version );
}
@Override
public boolean putFromLoad(SessionImplementor session, Object key, Object value, long txTimestamp, Object version, boolean minimalPutOverride)
throws CacheException {
return delegate.putFromLoad( session, key, value, txTimestamp, version, minimalPutOverride );
}
@Override
public void remove(SessionImplementor session, Object key) throws CacheException {
delegate.remove( session, key );
}
@Override
public void removeAll() throws CacheException {
delegate.removeAll();
}
@Override
public SoftLock lockItem(SessionImplementor session, Object key, Object version) throws CacheException {
return null;
}
@Override
public SoftLock lockRegion() throws CacheException {
return null;
}
@Override
public void unlockItem(SessionImplementor session, Object key, SoftLock lock) throws CacheException {
delegate.unlockItem( session, key );
}
@Override
public void unlockRegion(SoftLock lock) throws CacheException {
}
@Override
public boolean afterInsert(SessionImplementor session, Object key, Object value) throws CacheException {
return delegate.afterInsert( session, key, value, null );
}
@Override
public boolean afterUpdate(SessionImplementor session, Object key, Object value, SoftLock lock) throws CacheException {
return delegate.afterUpdate( session, key, value, null, null, lock );
}
@Override
public Object generateCacheKey(Object[] naturalIdValues, EntityPersister persister, SessionImplementor session) {
return region.getCacheKeysFactory().createNaturalIdKey(naturalIdValues, persister, session);
}
@Override
public Object[] getNaturalIdValues(Object cacheKey) {
return region.getCacheKeysFactory().getNaturalIdValues(cacheKey);
}
}

View File

@ -124,7 +124,7 @@ public class Caches {
* @return a cache that ignores return values * @return a cache that ignores return values
*/ */
public static AdvancedCache ignoreReturnValuesCache(AdvancedCache cache) { public static AdvancedCache ignoreReturnValuesCache(AdvancedCache cache) {
return cache.withFlags( Flag.SKIP_CACHE_LOAD, Flag.SKIP_REMOTE_LOOKUP ); return cache.withFlags( Flag.SKIP_CACHE_LOAD, Flag.SKIP_REMOTE_LOOKUP, Flag.IGNORE_RETURN_VALUES );
} }
/** /**
@ -139,7 +139,7 @@ public class Caches {
public static AdvancedCache ignoreReturnValuesCache( public static AdvancedCache ignoreReturnValuesCache(
AdvancedCache cache, Flag extraFlag) { AdvancedCache cache, Flag extraFlag) {
return cache.withFlags( return cache.withFlags(
Flag.SKIP_CACHE_LOAD, Flag.SKIP_REMOTE_LOOKUP, extraFlag Flag.SKIP_CACHE_LOAD, Flag.SKIP_REMOTE_LOOKUP, Flag.IGNORE_RETURN_VALUES, extraFlag
); );
} }
@ -270,6 +270,11 @@ public class Caches {
.clustering().cacheMode().isClustered(); .clustering().cacheMode().isClustered();
} }
public static boolean isTransactionalCache(AdvancedCache cache) {
return cache.getCacheConfiguration().transaction().transactionMode().isTransactional();
}
public static void removeAll(AdvancedCache cache) { public static void removeAll(AdvancedCache cache) {
CloseableIterator it = keys(cache).iterator(); CloseableIterator it = keys(cache).iterator();
try { try {

View File

@ -20,40 +20,23 @@
<!-- Default configuration is appropriate for entity/collection caching. --> <!-- Default configuration is appropriate for entity/collection caching. -->
<invalidation-cache name="entity" mode="SYNC" remote-timeout="20000"> <invalidation-cache name="entity" mode="SYNC" remote-timeout="20000">
<locking isolation="READ_COMMITTED" concurrency-level="1000" acquire-timeout="15000" striping="false"/> <locking concurrency-level="1000" acquire-timeout="15000"/>
<transaction mode="NON_XA" locking="OPTIMISTIC" auto-commit="false"/> <transaction mode="NONE" />
<eviction max-entries="10000" strategy="LRU"/> <eviction max-entries="10000" strategy="LRU"/>
<expiration max-idle="100000" interval="5000"/> <expiration max-idle="100000" interval="5000"/>
</invalidation-cache> </invalidation-cache>
<!-- Default configuration for immutable entities --> <!-- Default configuration for immutable entities -->
<invalidation-cache name="immutable-entity" mode="SYNC" remote-timeout="20000"> <invalidation-cache name="immutable-entity" mode="SYNC" remote-timeout="20000">
<locking isolation="READ_COMMITTED" concurrency-level="1000" acquire-timeout="15000" striping="false"/> <locking concurrency-level="1000" acquire-timeout="15000"/>
<transaction mode="NONE"/> <transaction mode="NONE"/>
<eviction max-entries="10000" strategy="LRU"/> <eviction max-entries="10000" strategy="LRU"/>
<expiration max-idle="100000" interval="5000"/> <expiration max-idle="100000" interval="5000"/>
</invalidation-cache> </invalidation-cache>
<!-- Default configuration is appropriate for entity/collection caching. -->
<invalidation-cache name="entity-repeatable" mode="SYNC" remote-timeout="20000">
<locking isolation="REPEATABLE_READ" concurrency-level="1000" acquire-timeout="15000" striping="false"/>
<transaction mode="NON_XA" locking="OPTIMISTIC" auto-commit="false"/>
<eviction max-entries="10000" strategy="LRU"/>
<expiration max-idle="100000" interval="5000"/>
</invalidation-cache>
<!-- An alternative configuration for entity/collection caching that uses replication instead of invalidation -->
<replicated-cache name="replicated-entity" mode="SYNC" remote-timeout="20000">
<locking isolation="READ_COMMITTED" concurrency-level="1000" acquire-timeout="15000" striping="false"/>
<transaction mode="NON_XA" locking="OPTIMISTIC" auto-commit="false"/>
<eviction max-entries="10000" strategy="LRU"/>
<expiration max-idle="100000" interval="5000"/>
<state-transfer enabled="false"/>
</replicated-cache>
<!-- A config appropriate for query caching. Does not replicate queries. --> <!-- A config appropriate for query caching. Does not replicate queries. -->
<local-cache name="local-query"> <local-cache name="local-query">
<locking isolation="READ_COMMITTED" concurrency-level="1000" acquire-timeout="15000" striping="false"/> <locking concurrency-level="1000" acquire-timeout="15000"/>
<transaction mode="NONE" /> <transaction mode="NONE" />
<eviction max-entries="10000" strategy="LRU"/> <eviction max-entries="10000" strategy="LRU"/>
<expiration max-idle="100000" interval="5000"/> <expiration max-idle="100000" interval="5000"/>
@ -61,7 +44,7 @@
<!-- A query cache that replicates queries. Replication is asynchronous. --> <!-- A query cache that replicates queries. Replication is asynchronous. -->
<replicated-cache name="replicated-query" mode="ASYNC"> <replicated-cache name="replicated-query" mode="ASYNC">
<locking isolation="READ_COMMITTED" concurrency-level="1000" acquire-timeout="15000" striping="false"/> <locking concurrency-level="1000" acquire-timeout="15000"/>
<transaction mode="NONE" /> <transaction mode="NONE" />
<eviction max-entries="10000" strategy="LRU"/> <eviction max-entries="10000" strategy="LRU"/>
<expiration max-idle="100000" interval="5000"/> <expiration max-idle="100000" interval="5000"/>
@ -71,7 +54,7 @@
is required if query caching is used, even if the query cache is required if query caching is used, even if the query cache
itself is configured with CacheMode=LOCAL. --> itself is configured with CacheMode=LOCAL. -->
<replicated-cache name="timestamps" mode="ASYNC"> <replicated-cache name="timestamps" mode="ASYNC">
<locking isolation="READ_COMMITTED" concurrency-level="1000" acquire-timeout="15000" striping="false"/> <locking concurrency-level="1000" acquire-timeout="15000"/>
<!-- Explicitly non transactional --> <!-- Explicitly non transactional -->
<transaction mode="NONE"/> <transaction mode="NONE"/>
<!-- Don't ever evict modification timestamps --> <!-- Don't ever evict modification timestamps -->

View File

@ -30,7 +30,7 @@ import static org.junit.Assert.assertTrue;
* @author Galder Zamarreño * @author Galder Zamarreño
* @since 3.5 * @since 3.5
*/ */
public abstract class AbstractEntityCollectionRegionTestCase extends AbstractRegionImplTestCase { public abstract class AbstractEntityCollectionRegionTest extends AbstractRegionImplTest {
protected static CacheDataDescription MUTABLE_NON_VERSIONED = new CacheDataDescriptionImpl(true, false, ComparableComparator.INSTANCE, null); protected static CacheDataDescription MUTABLE_NON_VERSIONED = new CacheDataDescriptionImpl(true, false, ComparableComparator.INSTANCE, null);
@Test @Test
@ -43,7 +43,8 @@ public abstract class AbstractEntityCollectionRegionTestCase extends AbstractReg
"test", "test",
InfinispanRegionFactory.class, InfinispanRegionFactory.class,
true, true,
false false,
jtaPlatform
); );
ssrb.applySetting( InfinispanRegionFactory.ENTITY_CACHE_RESOURCE_PROP, "entity" ); ssrb.applySetting( InfinispanRegionFactory.ENTITY_CACHE_RESOURCE_PROP, "entity" );
final StandardServiceRegistry registry = ssrb.build(); final StandardServiceRegistry registry = ssrb.build();
@ -72,7 +73,8 @@ public abstract class AbstractEntityCollectionRegionTestCase extends AbstractReg
"test", "test",
InfinispanRegionFactory.class, InfinispanRegionFactory.class,
true, true,
false false,
jtaPlatform
); );
final StandardServiceRegistry registry = ssrb.build(); final StandardServiceRegistry registry = ssrb.build();
try { try {
@ -101,7 +103,8 @@ public abstract class AbstractEntityCollectionRegionTestCase extends AbstractReg
"test", "test",
InfinispanRegionFactory.class, InfinispanRegionFactory.class,
true, true,
false false,
jtaPlatform
); );
final StandardServiceRegistry registry = ssrb.build(); final StandardServiceRegistry registry = ssrb.build();
try { try {

View File

@ -0,0 +1,80 @@
package org.hibernate.test.cache.infinispan;
import org.hibernate.cache.internal.CacheDataDescriptionImpl;
import org.hibernate.cache.spi.CacheDataDescription;
import org.hibernate.cache.spi.access.AccessType;
import org.hibernate.cache.spi.access.RegionAccessStrategy;
import org.hibernate.cache.spi.access.SoftLock;
import org.hibernate.engine.spi.SessionImplementor;
import org.hibernate.internal.util.compare.ComparableComparator;
import org.hibernate.test.cache.infinispan.util.InfinispanTestingSetup;
import org.hibernate.test.cache.infinispan.util.TestingKeyFactory;
import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import static org.junit.Assert.assertNull;
import static org.mockito.Mockito.mock;
/**
* @author Radim Vansa &lt;rvansa@redhat.com&gt;
*/
public abstract class AbstractExtraAPITest<S extends RegionAccessStrategy> extends AbstractNonFunctionalTest {
@Rule
public InfinispanTestingSetup infinispanTestIdentifier = new InfinispanTestingSetup();
public static final String REGION_NAME = "test/com.foo.test";
public static final Object KEY = TestingKeyFactory.generateCollectionCacheKey( "KEY" );
public static final CacheDataDescription CACHE_DATA_DESCRIPTION
= new CacheDataDescriptionImpl(true, true, ComparableComparator.INSTANCE, null);
protected static final SessionImplementor SESSION = mock(SessionImplementor.class);
protected S accessStrategy;
protected NodeEnvironment environment;
@Before
public final void prepareLocalAccessStrategy() throws Exception {
environment = new NodeEnvironment( createStandardServiceRegistryBuilder() );
environment.prepare();
// Sleep a bit to avoid concurrent FLUSH problem
avoidConcurrentFlush();
accessStrategy = getAccessStrategy();
}
protected abstract S getAccessStrategy();
protected abstract AccessType getAccessType();
@After
public final void releaseLocalAccessStrategy() throws Exception {
if ( environment != null ) {
environment.release();
}
}
@Test
public void testLockItem() {
assertNull( accessStrategy.lockItem(SESSION, KEY, Integer.valueOf( 1 ) ) );
}
@Test
public void testLockRegion() {
assertNull( accessStrategy.lockRegion() );
}
@Test
public void testUnlockItem() {
accessStrategy.unlockItem(SESSION, KEY, new MockSoftLock() );
}
@Test
public void testUnlockRegion() {
accessStrategy.unlockItem(SESSION, KEY, new MockSoftLock() );
}
public static class MockSoftLock implements SoftLock {
}
}

View File

@ -12,7 +12,6 @@ import java.util.Properties;
import java.util.Set; import java.util.Set;
import java.util.concurrent.Callable; import java.util.concurrent.Callable;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import java.util.function.BiConsumer;
import org.hibernate.Session; import org.hibernate.Session;
import org.hibernate.SessionFactory; import org.hibernate.SessionFactory;
@ -29,18 +28,12 @@ import org.hibernate.cache.spi.Region;
import org.hibernate.cache.spi.RegionFactory; import org.hibernate.cache.spi.RegionFactory;
import org.hibernate.cfg.AvailableSettings; import org.hibernate.cfg.AvailableSettings;
import org.hibernate.engine.spi.SessionImplementor; import org.hibernate.engine.spi.SessionImplementor;
import org.hibernate.resource.transaction.backend.jta.internal.JtaTransactionCoordinatorBuilderImpl;
import org.hibernate.test.cache.infinispan.functional.SingleNodeTestCase;
import org.hibernate.test.cache.infinispan.util.BatchModeJtaPlatform;
import org.hibernate.test.cache.infinispan.util.CacheTestUtil; import org.hibernate.test.cache.infinispan.util.CacheTestUtil;
import org.hibernate.test.cache.infinispan.util.TestInfinispanRegionFactory;
import org.infinispan.AdvancedCache; import org.infinispan.AdvancedCache;
import org.infinispan.commons.util.CloseableIterable;
import org.infinispan.transaction.tm.BatchModeTransactionManager;
import org.jboss.logging.Logger; import org.jboss.logging.Logger;
import org.junit.Test; import org.junit.Test;
import javax.transaction.TransactionManager;
import static org.hibernate.test.cache.infinispan.util.CacheTestUtil.assertEqualsEventually; import static org.hibernate.test.cache.infinispan.util.CacheTestUtil.assertEqualsEventually;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull; import static org.junit.Assert.assertNull;
@ -51,8 +44,8 @@ import static org.junit.Assert.assertNull;
* @author Galder Zamarreño * @author Galder Zamarreño
* @since 3.5 * @since 3.5
*/ */
public abstract class AbstractGeneralDataRegionTestCase extends AbstractRegionImplTestCase { public abstract class AbstractGeneralDataRegionTest extends AbstractRegionImplTest {
private static final Logger log = Logger.getLogger( AbstractGeneralDataRegionTestCase.class ); private static final Logger log = Logger.getLogger( AbstractGeneralDataRegionTest.class );
protected static final String KEY = "Key"; protected static final String KEY = "Key";
@ -60,17 +53,6 @@ public abstract class AbstractGeneralDataRegionTestCase extends AbstractRegionIm
protected static final String VALUE2 = "value2"; protected static final String VALUE2 = "value2";
protected static final String VALUE3 = "value3"; protected static final String VALUE3 = "value3";
protected TransactionManager tm = BatchModeTransactionManager.getInstance();
protected StandardServiceRegistryBuilder createStandardServiceRegistryBuilder() {
return CacheTestUtil.buildBaselineStandardServiceRegistryBuilder(
"test",
InfinispanRegionFactory.class,
false,
true
);
}
@Override @Override
protected void putInRegion(Region region, Object key, Object value) { protected void putInRegion(Region region, Object key, Object value) {
((GeneralDataRegion) region).put(null, key, value ); ((GeneralDataRegion) region).put(null, key, value );
@ -92,9 +74,7 @@ public abstract class AbstractGeneralDataRegionTestCase extends AbstractRegionIm
protected void withSessionFactoriesAndRegions(int num, SFRConsumer consumer) throws Exception { protected void withSessionFactoriesAndRegions(int num, SFRConsumer consumer) throws Exception {
StandardServiceRegistryBuilder ssrb = createStandardServiceRegistryBuilder() StandardServiceRegistryBuilder ssrb = createStandardServiceRegistryBuilder()
.applySetting(AvailableSettings.CACHE_REGION_FACTORY, SingleNodeTestCase.TestInfinispanRegionFactory.class.getName()) .applySetting(AvailableSettings.CACHE_REGION_FACTORY, TestInfinispanRegionFactory.class.getName());
.applySetting(AvailableSettings.JTA_PLATFORM, BatchModeJtaPlatform.class.getName())
.applySetting(AvailableSettings.TRANSACTION_COORDINATOR_STRATEGY, JtaTransactionCoordinatorBuilderImpl.class.getName());
Properties properties = CacheTestUtil.toProperties( ssrb.getSettings() ); Properties properties = CacheTestUtil.toProperties( ssrb.getSettings() );
List<StandardServiceRegistry> registries = new ArrayList<>(); List<StandardServiceRegistry> registries = new ArrayList<>();
List<SessionFactory> sessionFactories = new ArrayList<>(); List<SessionFactory> sessionFactories = new ArrayList<>();
@ -168,7 +148,7 @@ public abstract class AbstractGeneralDataRegionTestCase extends AbstractRegionIm
region.evict(KEY); region.evict(KEY);
} }
protected abstract String getStandardRegionName(String regionPrefix); protected abstract String getStandardRegionName(String regionPrefix);
/** /**
* Test method for {@link QueryResultsRegion#evictAll()}. * Test method for {@link QueryResultsRegion#evictAll()}.

View File

@ -0,0 +1,169 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
package org.hibernate.test.cache.infinispan;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.Callable;
import org.hibernate.Session;
import org.hibernate.Transaction;
import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
import org.hibernate.cache.infinispan.util.Caches;
import org.hibernate.engine.spi.SessionImplementor;
import org.hibernate.engine.transaction.jta.platform.spi.JtaPlatform;
import org.hibernate.resource.transaction.spi.TransactionStatus;
import org.hibernate.test.cache.infinispan.util.BatchModeJtaPlatform;
import org.hibernate.test.cache.infinispan.util.CacheTestUtil;
import org.hibernate.test.cache.infinispan.util.InfinispanTestingSetup;
import org.hibernate.test.cache.infinispan.util.TestInfinispanRegionFactory;
import org.hibernate.testing.junit4.CustomParameterized;
import org.infinispan.Cache;
import org.jboss.logging.Logger;
import org.junit.After;
import org.junit.Before;
import org.hibernate.cache.spi.RegionFactory;
import org.hibernate.test.cache.infinispan.util.CacheTestSupport;
import org.junit.Rule;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import javax.transaction.TransactionManager;
/**
* Base class for all non-functional tests of Infinispan integration.
*
* @author Galder Zamarreño
* @since 3.5
*/
@RunWith(CustomParameterized.class)
public abstract class AbstractNonFunctionalTest extends org.hibernate.testing.junit4.BaseUnitTestCase {
private static final Logger log = Logger.getLogger(AbstractNonFunctionalTest.class);
@Rule
public InfinispanTestingSetup infinispanTestIdentifier = new InfinispanTestingSetup();
@Parameterized.Parameters(name = "{0}")
public static List<Object[]> getJtaParameters() {
return Arrays.asList(
new Object[] { "JTA", BatchModeJtaPlatform.class },
new Object[] { "non-JTA", null });
}
@Parameterized.Parameter(value = 0)
public String mode;
@Parameterized.Parameter(value = 1)
public Class<? extends JtaPlatform> jtaPlatform;
public static final String REGION_PREFIX = "test";
private static final String PREFER_IPV4STACK = "java.net.preferIPv4Stack";
private String preferIPv4Stack;
private static final String JGROUPS_CFG_FILE = "hibernate.cache.infinispan.jgroups_cfg";
private String jgroupsCfgFile;
private CacheTestSupport testSupport = new CacheTestSupport();
@Before
public void prepareCacheSupport() throws Exception {
preferIPv4Stack = System.getProperty(PREFER_IPV4STACK);
System.setProperty(PREFER_IPV4STACK, "true");
jgroupsCfgFile = System.getProperty(JGROUPS_CFG_FILE);
System.setProperty(JGROUPS_CFG_FILE, "2lc-test-tcp.xml");
testSupport.setUp();
}
@After
public void releaseCachSupport() throws Exception {
testSupport.tearDown();
if (preferIPv4Stack == null) {
System.clearProperty(PREFER_IPV4STACK);
} else {
System.setProperty(PREFER_IPV4STACK, preferIPv4Stack);
}
if (jgroupsCfgFile == null)
System.clearProperty(JGROUPS_CFG_FILE);
else
System.setProperty(JGROUPS_CFG_FILE, jgroupsCfgFile);
}
protected <T> T withTx(NodeEnvironment environment, SessionImplementor session, Callable<T> callable) throws Exception {
if (jtaPlatform != null) {
TransactionManager tm = environment.getServiceRegistry().getService(JtaPlatform.class).retrieveTransactionManager();
return Caches.withinTx(tm, callable);
} else {
Transaction transaction = ((Session) session).beginTransaction();
boolean rollingBack = false;
try {
T retval = callable.call();
if (transaction.getStatus() == TransactionStatus.ACTIVE) {
transaction.commit();
} else {
rollingBack = true;
transaction.rollback();
}
return retval;
} catch (Exception e) {
if (!rollingBack) {
try {
transaction.rollback();
} catch (Exception suppressed) {
e.addSuppressed(suppressed);
}
}
throw e;
}
}
}
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();
}
protected StandardServiceRegistryBuilder createStandardServiceRegistryBuilder() {
final StandardServiceRegistryBuilder ssrb = CacheTestUtil.buildBaselineStandardServiceRegistryBuilder(
REGION_PREFIX, getRegionFactoryClass(), true, false, jtaPlatform);
return ssrb;
}
protected Class<? extends RegionFactory> getRegionFactoryClass() {
return TestInfinispanRegionFactory.class;
}
}

View File

@ -1,99 +0,0 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
package org.hibernate.test.cache.infinispan;
import java.util.Set;
import org.hibernate.test.cache.infinispan.util.InfinispanTestingSetup;
import org.infinispan.Cache;
import org.jboss.logging.Logger;
import org.junit.After;
import org.junit.Before;
import org.hibernate.cache.spi.RegionFactory;
import org.hibernate.test.cache.infinispan.util.CacheTestSupport;
import org.junit.Rule;
/**
* Base class for all non-functional tests of Infinispan integration.
*
* @author Galder Zamarreño
* @since 3.5
*/
public abstract class AbstractNonFunctionalTestCase extends org.hibernate.testing.junit4.BaseUnitTestCase {
private static final Logger log = Logger.getLogger(AbstractNonFunctionalTestCase.class);
@Rule
public InfinispanTestingSetup infinispanTestIdentifier = new InfinispanTestingSetup();
public static final String REGION_PREFIX = "test";
private static final String PREFER_IPV4STACK = "java.net.preferIPv4Stack";
private String preferIPv4Stack;
private static final String JGROUPS_CFG_FILE = "hibernate.cache.infinispan.jgroups_cfg";
private String jgroupsCfgFile;
private CacheTestSupport testSupport = new CacheTestSupport();
@Before
public void prepareCacheSupport() throws Exception {
preferIPv4Stack = System.getProperty(PREFER_IPV4STACK);
System.setProperty(PREFER_IPV4STACK, "true");
jgroupsCfgFile = System.getProperty(JGROUPS_CFG_FILE);
System.setProperty(JGROUPS_CFG_FILE, "2lc-test-tcp.xml");
testSupport.setUp();
}
@After
public void releaseCachSupport() throws Exception {
testSupport.tearDown();
if (preferIPv4Stack == null) {
System.clearProperty(PREFER_IPV4STACK);
} else {
System.setProperty(PREFER_IPV4STACK, preferIPv4Stack);
}
if (jgroupsCfgFile == null)
System.clearProperty(JGROUPS_CFG_FILE);
else
System.setProperty(JGROUPS_CFG_FILE, jgroupsCfgFile);
}
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();
}
}

View File

@ -0,0 +1,360 @@
package org.hibernate.test.cache.infinispan;
import junit.framework.AssertionFailedError;
import org.hibernate.Session;
import org.hibernate.Transaction;
import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
import org.hibernate.cache.infinispan.impl.BaseRegion;
import org.hibernate.cache.infinispan.util.Caches;
import org.hibernate.cache.internal.CacheDataDescriptionImpl;
import org.hibernate.cache.spi.CacheDataDescription;
import org.hibernate.cache.spi.access.AccessType;
import org.hibernate.cache.spi.access.RegionAccessStrategy;
import org.hibernate.cache.spi.access.SoftLock;
import org.hibernate.engine.spi.SessionImplementor;
import org.hibernate.engine.transaction.internal.TransactionImpl;
import org.hibernate.internal.util.compare.ComparableComparator;
import org.hibernate.resource.transaction.TransactionCoordinator;
import org.hibernate.resource.transaction.backend.jdbc.internal.JdbcResourceLocalTransactionCoordinatorBuilderImpl;
import org.hibernate.resource.transaction.backend.jdbc.spi.JdbcResourceTransactionAccess;
import org.hibernate.resource.transaction.spi.TransactionCoordinatorOwner;
import org.hibernate.test.cache.infinispan.util.BatchModeJtaPlatform;
import org.hibernate.test.cache.infinispan.util.BatchModeTransactionCoordinator;
import org.hibernate.test.cache.infinispan.util.InfinispanTestingSetup;
import org.hibernate.test.cache.infinispan.util.JdbcResourceTransactionMock;
import org.hibernate.test.cache.infinispan.util.TestSynchronization;
import org.infinispan.Cache;
import org.infinispan.test.TestingUtil;
import org.jboss.logging.Logger;
import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import javax.transaction.RollbackException;
import javax.transaction.SystemException;
import javax.transaction.TransactionManager;
import java.util.Arrays;
import java.util.concurrent.CountDownLatch;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
/**
* @author Radim Vansa &lt;rvansa@redhat.com&gt;
*/
public abstract class AbstractRegionAccessStrategyTest<R extends BaseRegion, S extends RegionAccessStrategy>
extends AbstractNonFunctionalTest {
protected final Logger log = Logger.getLogger(getClass());
@Rule
public InfinispanTestingSetup infinispanTestIdentifier = new InfinispanTestingSetup();
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";
public static final CacheDataDescription CACHE_DATA_DESCRIPTION
= new CacheDataDescriptionImpl(true, true, ComparableComparator.INSTANCE, null);
protected NodeEnvironment localEnvironment;
protected R localRegion;
protected S localAccessStrategy;
protected NodeEnvironment remoteEnvironment;
protected R remoteRegion;
protected S remoteAccessStrategy;
protected boolean transactional;
protected boolean invalidation;
protected boolean synchronous;
protected Exception node1Exception;
protected Exception node2Exception;
protected AssertionFailedError node1Failure;
protected AssertionFailedError node2Failure;
protected abstract AccessType getAccessType();
@Before
public void prepareResources() throws Exception {
// to mimic exactly the old code results, both environments here are exactly the same...
StandardServiceRegistryBuilder ssrb = createStandardServiceRegistryBuilder();
localEnvironment = new NodeEnvironment( ssrb );
localEnvironment.prepare();
localRegion = getRegion(localEnvironment);
localAccessStrategy = getAccessStrategy(localRegion);
transactional = Caches.isTransactionalCache(localRegion.getCache());
invalidation = Caches.isInvalidationCache(localRegion.getCache());
synchronous = Caches.isSynchronousCache(localRegion.getCache());
// Sleep a bit to avoid concurrent FLUSH problem
avoidConcurrentFlush();
remoteEnvironment = new NodeEnvironment( ssrb );
remoteEnvironment.prepare();
remoteRegion = getRegion(remoteEnvironment);
remoteAccessStrategy = getAccessStrategy(remoteRegion);
waitForClusterToForm(localRegion.getCache(), remoteRegion.getCache());
}
private interface SessionMock extends Session, SessionImplementor {
}
private interface NonJtaTransactionCoordinator extends TransactionCoordinatorOwner, JdbcResourceTransactionAccess {
}
protected SessionImplementor mockedSession() {
SessionMock session = mock(SessionMock.class);
when(session.isClosed()).thenReturn(false);
if (jtaPlatform == BatchModeJtaPlatform.class) {
BatchModeTransactionCoordinator txCoord = new BatchModeTransactionCoordinator();
when(session.getTransactionCoordinator()).thenReturn(txCoord);
when(session.beginTransaction()).then(invocation -> {
Transaction tx = txCoord.newTransaction();
tx.begin();
return tx;
});
} else if (jtaPlatform == null) {
NonJtaTransactionCoordinator txOwner = mock(NonJtaTransactionCoordinator.class);
when(txOwner.getResourceLocalTransaction()).thenReturn(new JdbcResourceTransactionMock());
TransactionCoordinator txCoord = JdbcResourceLocalTransactionCoordinatorBuilderImpl.INSTANCE
.buildTransactionCoordinator(txOwner, null);
when(session.getTransactionCoordinator()).thenReturn(txCoord);
when(session.beginTransaction()).then(invocation -> {
Transaction tx = new TransactionImpl(txCoord);
tx.begin();
return tx;
});
} else {
throw new IllegalStateException("Unknown JtaPlatform: " + jtaPlatform);
}
return session;
}
protected abstract S getAccessStrategy(R region);
@Test
public void testRemove() throws Exception {
evictOrRemoveTest( false );
}
@Test
public void testEvict() throws Exception {
evictOrRemoveTest( true );
}
protected abstract R getRegion(NodeEnvironment environment);
protected void waitForClusterToForm(Cache... caches) {
TestingUtil.blockUntilViewsReceived(10000, Arrays.asList(caches));
}
@After
public void releaseResources() throws Exception {
try {
if (localEnvironment != null) {
localEnvironment.release();
}
}
finally {
if (remoteEnvironment != null) {
remoteEnvironment.release();
}
}
}
protected boolean isTransactional() {
return transactional;
}
protected boolean isUsingInvalidation() {
return invalidation;
}
protected boolean isSynchronous() {
return synchronous;
}
protected void evictOrRemoveTest(final boolean evict) throws Exception {
final Object KEY = generateNextKey();
assertEquals(0, localRegion.getCache().size());
assertEquals(0, remoteRegion.getCache().size());
assertNull("local is clean", localAccessStrategy.get(mockedSession(), KEY, System.currentTimeMillis()));
assertNull("remote is clean", remoteAccessStrategy.get(mockedSession(), KEY, System.currentTimeMillis()));
localAccessStrategy.putFromLoad(mockedSession(), KEY, VALUE1, System.currentTimeMillis(), new Integer(1));
assertEquals(VALUE1, localAccessStrategy.get(mockedSession(), KEY, System.currentTimeMillis()));
remoteAccessStrategy.putFromLoad(mockedSession(), KEY, VALUE1, System.currentTimeMillis(), new Integer(1));
assertEquals(VALUE1, remoteAccessStrategy.get(mockedSession(), KEY, System.currentTimeMillis()));
SessionImplementor session = mockedSession();
withTx(localEnvironment, session, () -> {
if (evict) {
localAccessStrategy.evict(KEY);
}
else {
doRemove(localRegion.getTransactionManager(), localAccessStrategy, session, KEY);
}
return null;
});
assertNull(localAccessStrategy.get(mockedSession(), KEY, System.currentTimeMillis()));
assertEquals(0, localRegion.getCache().size());
assertNull(remoteAccessStrategy.get(mockedSession(), KEY, System.currentTimeMillis()));
assertEquals(0, remoteRegion.getCache().size());
}
protected void doRemove(TransactionManager tm, S strategy, SessionImplementor session, Object key) throws SystemException, RollbackException {
SoftLock softLock = strategy.lockItem(session, key, null);
strategy.remove(session, key);
session.getTransactionCoordinator().getLocalSynchronizations().registerSynchronization(
new TestSynchronization.UnlockItem(strategy, session, key, softLock));
}
@Test
public void testRemoveAll() throws Exception {
evictOrRemoveAllTest(false);
}
@Test
public void testEvictAll() throws Exception {
evictOrRemoveAllTest(true);
}
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);
}
}
@Test
public void testPutFromLoad() throws Exception {
putFromLoadTest( false );
}
@Test
public void testPutFromLoadMinimal() throws Exception {
putFromLoadTest( true );
}
protected abstract void putFromLoadTest(boolean useMinimalAPI) throws Exception;
protected abstract Object generateNextKey();
protected void evictOrRemoveAllTest(final boolean evict) throws Exception {
final Object KEY = generateNextKey();
assertEquals(0, localRegion.getCache().size());
assertEquals(0, remoteRegion.getCache().size());
assertNull("local is clean", localAccessStrategy.get(mockedSession(), KEY, System.currentTimeMillis()));
assertNull("remote is clean", remoteAccessStrategy.get(mockedSession(), KEY, System.currentTimeMillis()));
localAccessStrategy.putFromLoad(mockedSession(), KEY, VALUE1, System.currentTimeMillis(), new Integer(1));
assertEquals(VALUE1, localAccessStrategy.get(mockedSession(), KEY, System.currentTimeMillis()));
remoteAccessStrategy.putFromLoad(mockedSession(), KEY, VALUE1, System.currentTimeMillis(), new Integer(1));
assertEquals(VALUE1, remoteAccessStrategy.get(mockedSession(), KEY, System.currentTimeMillis()));
// Wait for async propagation
sleep(250);
withTx(localEnvironment, mockedSession(), () -> {
if (evict) {
localAccessStrategy.evictAll();
} else {
SoftLock softLock = localAccessStrategy.lockRegion();
localAccessStrategy.removeAll();
localAccessStrategy.unlockRegion(softLock);
}
return null;
});
// This should re-establish the region root node in the optimistic case
assertNull(localAccessStrategy.get(mockedSession(), KEY, System.currentTimeMillis()));
assertEquals(0, localRegion.getCache().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
assertNull(remoteAccessStrategy.get(mockedSession(), KEY, System.currentTimeMillis()));
assertEquals(0, remoteRegion.getCache().size());
// Wait for async propagation of EndInvalidationCommand before executing naked put
sleep(250);
// Test whether the get above messes up the optimistic version
remoteAccessStrategy.putFromLoad(mockedSession(), KEY, VALUE1, System.currentTimeMillis(), new Integer(1));
assertEquals(VALUE1, remoteAccessStrategy.get(mockedSession(), KEY, System.currentTimeMillis()));
assertEquals(1, remoteRegion.getCache().size());
// Wait for async propagation
sleep(250);
assertEquals((isUsingInvalidation() ? null : VALUE1), localAccessStrategy.get(mockedSession(), KEY, System.currentTimeMillis()));
assertEquals(VALUE1, remoteAccessStrategy.get(mockedSession(), KEY, System.currentTimeMillis()));
}
protected class PutFromLoadNode2 extends Thread {
private final Object KEY;
private final CountDownLatch writeLatch1;
private final CountDownLatch writeLatch2;
private final boolean useMinimalAPI;
private final CountDownLatch completionLatch;
public PutFromLoadNode2(Object KEY, CountDownLatch writeLatch1, CountDownLatch writeLatch2, boolean useMinimalAPI, CountDownLatch completionLatch) {
this.KEY = KEY;
this.writeLatch1 = writeLatch1;
this.writeLatch2 = writeLatch2;
this.useMinimalAPI = useMinimalAPI;
this.completionLatch = completionLatch;
}
@Override
public void run() {
try {
long txTimestamp = System.currentTimeMillis();
SessionImplementor session = mockedSession();
withTx(remoteEnvironment, session, () -> {
assertNull(remoteAccessStrategy.get(session, KEY, txTimestamp));
// Let node1 write
writeLatch1.countDown();
// Wait for node1 to finish
writeLatch2.await();
if (useMinimalAPI) {
remoteAccessStrategy.putFromLoad(session, KEY, VALUE1, txTimestamp, new Integer(1), true);
} else {
remoteAccessStrategy.putFromLoad(session, KEY, VALUE1, txTimestamp, new Integer(1));
}
return null;
});
} catch (Exception e) {
log.error("node2 caught exception", e);
node2Exception = e;
} catch (AssertionFailedError e) {
node2Failure = e;
} finally {
completionLatch.countDown();
}
}
}
}

View File

@ -21,7 +21,7 @@ import org.infinispan.AdvancedCache;
* @author Galder Zamarreño * @author Galder Zamarreño
* @since 3.5 * @since 3.5
*/ */
public abstract class AbstractRegionImplTestCase extends AbstractNonFunctionalTestCase { public abstract class AbstractRegionImplTest extends AbstractNonFunctionalTest {
protected abstract AdvancedCache getInfinispanCache(InfinispanRegionFactory regionFactory); protected abstract AdvancedCache getInfinispanCache(InfinispanRegionFactory regionFactory);

View File

@ -0,0 +1,565 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
package org.hibernate.test.cache.infinispan.access;
import javax.transaction.TransactionManager;
import java.lang.reflect.Method;
import java.util.Map;
import java.util.concurrent.Callable;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;
import org.hibernate.cache.infinispan.InfinispanRegionFactory;
import org.hibernate.cache.infinispan.access.PutFromLoadValidator;
import org.hibernate.engine.spi.SessionImplementor;
import org.hibernate.test.cache.infinispan.functional.cluster.DualNodeJtaTransactionManagerImpl;
import org.hibernate.test.cache.infinispan.util.InfinispanTestingSetup;
import org.hibernate.testing.TestForIssue;
import org.infinispan.configuration.cache.ConfigurationBuilder;
import org.infinispan.manager.EmbeddedCacheManager;
import org.infinispan.test.CacheManagerCallable;
import org.infinispan.test.fwk.TestCacheManagerFactory;
import org.infinispan.util.logging.Log;
import org.infinispan.util.logging.LogFactory;
import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import static org.infinispan.test.TestingUtil.withCacheManager;
import static org.infinispan.test.TestingUtil.withTx;
import static org.mockito.Mockito.mock;
import static org.junit.Assert.*;
/**
* Tests of {@link PutFromLoadValidator}.
*
* @author Brian Stansberry
* @author Galder Zamarreño
* @version $Revision: $
*/
public class PutFromLoadValidatorUnitTest {
private static final Log log = LogFactory.getLog(
PutFromLoadValidatorUnitTest.class);
@Rule
public InfinispanTestingSetup infinispanTestIdentifier = new InfinispanTestingSetup();
private Object KEY1 = "KEY1";
private TransactionManager tm;
@Before
public void setUp() throws Exception {
tm = DualNodeJtaTransactionManagerImpl.getInstance("test");
}
@After
public void tearDown() throws Exception {
tm = null;
try {
DualNodeJtaTransactionManagerImpl.cleanupTransactions();
}
finally {
DualNodeJtaTransactionManagerImpl.cleanupTransactionManagers();
}
}
private static EmbeddedCacheManager createCacheManager() {
EmbeddedCacheManager cacheManager = TestCacheManagerFactory.createCacheManager(false);
cacheManager.defineConfiguration(InfinispanRegionFactory.PENDING_PUTS_CACHE_NAME,
InfinispanRegionFactory.PENDING_PUTS_CACHE_CONFIGURATION);
return cacheManager;
}
@Test
public void testNakedPut() throws Exception {
nakedPutTest(false);
}
@Test
public void testNakedPutTransactional() throws Exception {
nakedPutTest(true);
}
private void nakedPutTest(final boolean transactional) throws Exception {
withCacheManager(new CacheManagerCallable(createCacheManager()) {
@Override
public void call() {
PutFromLoadValidator testee = new PutFromLoadValidator(cm.getCache().getAdvancedCache(), cm);
exec(transactional, new NakedPut(testee, true));
}
});
}
@Test
public void testRegisteredPut() throws Exception {
registeredPutTest(false);
}
@Test
public void testRegisteredPutTransactional() throws Exception {
registeredPutTest(true);
}
private void registeredPutTest(final boolean transactional) throws Exception {
withCacheManager(new CacheManagerCallable(createCacheManager()) {
@Override
public void call() {
PutFromLoadValidator testee = new PutFromLoadValidator(cm.getCache().getAdvancedCache(), cm);
exec(transactional, new RegularPut(testee));
}
});
}
@Test
public void testNakedPutAfterKeyRemoval() throws Exception {
nakedPutAfterRemovalTest(false, false);
}
@Test
public void testNakedPutAfterKeyRemovalTransactional() throws Exception {
nakedPutAfterRemovalTest(true, false);
}
@Test
public void testNakedPutAfterRegionRemoval() throws Exception {
nakedPutAfterRemovalTest(false, true);
}
@Test
public void testNakedPutAfterRegionRemovalTransactional() throws Exception {
nakedPutAfterRemovalTest(true, true);
}
private void nakedPutAfterRemovalTest(final boolean transactional,
final boolean removeRegion) throws Exception {
withCacheManager(new CacheManagerCallable(createCacheManager()) {
@Override
public void call() {
PutFromLoadValidator testee = new PutFromLoadValidator(cm.getCache().getAdvancedCache());
Invalidation invalidation = new Invalidation(testee, removeRegion);
// the naked put can succeed because it has txTimestamp after invalidation
NakedPut nakedPut = new NakedPut(testee, true);
exec(transactional, invalidation, nakedPut);
}
});
}
@Test
public void testRegisteredPutAfterKeyRemoval() throws Exception {
registeredPutAfterRemovalTest(false, false);
}
@Test
public void testRegisteredPutAfterKeyRemovalTransactional() throws Exception {
registeredPutAfterRemovalTest(true, false);
}
@Test
public void testRegisteredPutAfterRegionRemoval() throws Exception {
registeredPutAfterRemovalTest(false, true);
}
@Test
public void testRegisteredPutAfterRegionRemovalTransactional() throws Exception {
registeredPutAfterRemovalTest(true, true);
}
private void registeredPutAfterRemovalTest(final boolean transactional,
final boolean removeRegion) throws Exception {
withCacheManager(new CacheManagerCallable(createCacheManager()) {
@Override
public void call() {
PutFromLoadValidator testee = new PutFromLoadValidator(cm.getCache().getAdvancedCache(), cm);
Invalidation invalidation = new Invalidation(testee, removeRegion);
RegularPut regularPut = new RegularPut(testee);
exec(transactional, invalidation, regularPut);
}
});
}
@Test
public void testRegisteredPutWithInterveningKeyRemoval() throws Exception {
registeredPutWithInterveningRemovalTest(false, false);
}
@Test
public void testRegisteredPutWithInterveningKeyRemovalTransactional() throws Exception {
registeredPutWithInterveningRemovalTest(true, false);
}
@Test
public void testRegisteredPutWithInterveningRegionRemoval() throws Exception {
registeredPutWithInterveningRemovalTest(false, true);
}
@Test
public void testRegisteredPutWithInterveningRegionRemovalTransactional() throws Exception {
registeredPutWithInterveningRemovalTest(true, true);
}
private void registeredPutWithInterveningRemovalTest(
final boolean transactional, final boolean removeRegion)
throws Exception {
withCacheManager(new CacheManagerCallable(createCacheManager()) {
@Override
public void call() {
PutFromLoadValidator testee = new PutFromLoadValidator(cm.getCache().getAdvancedCache(), cm);
try {
long txTimestamp = System.currentTimeMillis();
if (transactional) {
tm.begin();
}
SessionImplementor session1 = mock(SessionImplementor.class);
SessionImplementor session2 = mock(SessionImplementor.class);
testee.registerPendingPut(session1, KEY1, txTimestamp);
if (removeRegion) {
testee.beginInvalidatingRegion();
} else {
testee.beginInvalidatingKey(session2, KEY1);
}
PutFromLoadValidator.Lock lock = testee.acquirePutFromLoadLock(session1, KEY1, txTimestamp);
try {
assertNull(lock);
}
finally {
if (lock != null) {
testee.releasePutFromLoadLock(KEY1, lock);
}
if (removeRegion) {
testee.endInvalidatingRegion();
} else {
testee.endInvalidatingKey(session2, KEY1);
}
}
} catch (Exception e) {
throw new RuntimeException(e);
}
}
});
}
@Test
public void testMultipleRegistrations() throws Exception {
multipleRegistrationtest(false);
}
@Test
public void testMultipleRegistrationsTransactional() throws Exception {
multipleRegistrationtest(true);
}
private void multipleRegistrationtest(final boolean transactional) throws Exception {
withCacheManager(new CacheManagerCallable(createCacheManager()) {
@Override
public void call() {
final PutFromLoadValidator testee = new PutFromLoadValidator(cm.getCache().getAdvancedCache(), cm);
final CountDownLatch registeredLatch = new CountDownLatch(3);
final CountDownLatch finishedLatch = new CountDownLatch(3);
final AtomicInteger success = new AtomicInteger();
Runnable r = new Runnable() {
public void run() {
try {
long txTimestamp = System.currentTimeMillis();
if (transactional) {
tm.begin();
}
SessionImplementor session = mock (SessionImplementor.class);
testee.registerPendingPut(session, KEY1, txTimestamp);
registeredLatch.countDown();
registeredLatch.await(5, TimeUnit.SECONDS);
PutFromLoadValidator.Lock lock = testee.acquirePutFromLoadLock(session, KEY1, txTimestamp);
if (lock != null) {
try {
log.trace("Put from load lock acquired for key = " + KEY1);
success.incrementAndGet();
} finally {
testee.releasePutFromLoadLock(KEY1, lock);
}
} else {
log.trace("Unable to acquired putFromLoad lock for key = " + KEY1);
}
finishedLatch.countDown();
} catch (Exception e) {
e.printStackTrace();
}
}
};
ExecutorService executor = Executors.newFixedThreadPool(3);
// Start with a removal so the "isPutValid" calls will fail if
// any of the concurrent activity isn't handled properly
testee.beginInvalidatingRegion();
testee.endInvalidatingRegion();
try {
Thread.sleep(10);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
// Do the registration + isPutValid calls
executor.execute(r);
executor.execute(r);
executor.execute(r);
try {
finishedLatch.await(5, TimeUnit.SECONDS);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
assertEquals("All threads succeeded", 3, success.get());
}
});
}
@Test
public void testInvalidateKeyBlocksForInProgressPut() throws Exception {
invalidationBlocksForInProgressPutTest(true);
}
@Test
public void testInvalidateRegionBlocksForInProgressPut() throws Exception {
invalidationBlocksForInProgressPutTest(false);
}
private void invalidationBlocksForInProgressPutTest(final boolean keyOnly) throws Exception {
withCacheManager(new CacheManagerCallable(createCacheManager()) {
@Override
public void call() {
final PutFromLoadValidator testee = new PutFromLoadValidator(cm.getCache().getAdvancedCache(), cm);
final CountDownLatch removeLatch = new CountDownLatch(1);
final CountDownLatch pferLatch = new CountDownLatch(1);
final AtomicReference<Object> cache = new AtomicReference<Object>("INITIAL");
Callable<Boolean> pferCallable = new Callable<Boolean>() {
public Boolean call() throws Exception {
long txTimestamp = System.currentTimeMillis();
SessionImplementor session = mock (SessionImplementor.class);
testee.registerPendingPut(session, KEY1, txTimestamp);
PutFromLoadValidator.Lock lock = testee.acquirePutFromLoadLock(session, KEY1, txTimestamp);
if (lock != null) {
try {
removeLatch.countDown();
pferLatch.await();
cache.set("PFER");
return Boolean.TRUE;
}
finally {
testee.releasePutFromLoadLock(KEY1, lock);
}
}
return Boolean.FALSE;
}
};
Callable<Void> invalidateCallable = new Callable<Void>() {
public Void call() throws Exception {
removeLatch.await();
if (keyOnly) {
SessionImplementor session = mock (SessionImplementor.class);
testee.beginInvalidatingKey(session, KEY1);
} else {
testee.beginInvalidatingRegion();
}
cache.set(null);
return null;
}
};
ExecutorService executorService = Executors.newCachedThreadPool();
Future<Boolean> pferFuture = executorService.submit(pferCallable);
Future<Void> invalidateFuture = executorService.submit(invalidateCallable);
try {
try {
invalidateFuture.get(1, TimeUnit.SECONDS);
fail("invalidateFuture did not block");
}
catch (TimeoutException good) {}
pferLatch.countDown();
assertTrue(pferFuture.get(5, TimeUnit.SECONDS));
invalidateFuture.get(5, TimeUnit.SECONDS);
assertNull(cache.get());
} catch (Exception e) {
throw new RuntimeException(e);
}
}
});
}
protected void exec(boolean transactional, Callable<?>... callables) {
try {
if (transactional) {
for (Callable<?> c : callables) {
withTx(tm, c);
}
} else {
for (Callable<?> c : callables) {
c.call();
}
}
} catch (RuntimeException e) {
throw e;
} catch (Exception e) {
throw new RuntimeException(e);
}
}
private class Invalidation implements Callable<Void> {
private PutFromLoadValidator putFromLoadValidator;
private boolean removeRegion;
public Invalidation(PutFromLoadValidator putFromLoadValidator, boolean removeRegion) {
this.putFromLoadValidator = putFromLoadValidator;
this.removeRegion = removeRegion;
}
@Override
public Void call() throws Exception {
if (removeRegion) {
boolean success = putFromLoadValidator.beginInvalidatingRegion();
assertTrue(success);
putFromLoadValidator.endInvalidatingRegion();;
} else {
SessionImplementor session = mock (SessionImplementor.class);
boolean success = putFromLoadValidator.beginInvalidatingKey(session, KEY1);
assertTrue(success);
success = putFromLoadValidator.endInvalidatingKey(session, KEY1);
assertTrue(success);
}
// if we go for the timestamp-based approach, invalidation in the same millisecond
// as the registerPendingPut/acquirePutFromLoad lock results in failure.
Thread.sleep(10);
return null;
}
}
private class RegularPut implements Callable<Void> {
private PutFromLoadValidator putFromLoadValidator;
public RegularPut(PutFromLoadValidator putFromLoadValidator) {
this.putFromLoadValidator = putFromLoadValidator;
}
@Override
public Void call() throws Exception {
try {
long txTimestamp = System.currentTimeMillis(); // this should be acquired before UserTransaction.begin()
SessionImplementor session = mock (SessionImplementor.class);
putFromLoadValidator.registerPendingPut(session, KEY1, txTimestamp);
PutFromLoadValidator.Lock lock = putFromLoadValidator.acquirePutFromLoadLock(session, KEY1, txTimestamp);
try {
assertNotNull(lock);
} finally {
if (lock != null) {
putFromLoadValidator.releasePutFromLoadLock(KEY1, lock);
}
}
} catch (Exception e) {
throw new RuntimeException(e);
}
return null;
}
}
private class NakedPut implements Callable<Void> {
private final PutFromLoadValidator testee;
private final boolean expectSuccess;
public NakedPut(PutFromLoadValidator testee, boolean expectSuccess) {
this.testee = testee;
this.expectSuccess = expectSuccess;
}
@Override
public Void call() throws Exception {
try {
long txTimestamp = System.currentTimeMillis(); // this should be acquired before UserTransaction.begin()
SessionImplementor session = mock (SessionImplementor.class);
PutFromLoadValidator.Lock lock = testee.acquirePutFromLoadLock(session, KEY1, txTimestamp);
try {
if (expectSuccess) {
assertNotNull(lock);
} else {
assertNull(lock);
}
}
finally {
if (lock != null) {
testee.releasePutFromLoadLock(KEY1, lock);
}
}
} catch (Exception e) {
throw new RuntimeException(e);
}
return null;
}
}
@Test
@TestForIssue(jiraKey = "HHH-9928")
public void testGetForNullReleasePuts() {
EmbeddedCacheManager cm = TestCacheManagerFactory.createCacheManager(false);
ConfigurationBuilder cb = new ConfigurationBuilder().read(InfinispanRegionFactory.PENDING_PUTS_CACHE_CONFIGURATION);
cb.expiration().maxIdle(500);
cm.defineConfiguration(InfinispanRegionFactory.PENDING_PUTS_CACHE_NAME, cb.build());
withCacheManager(new CacheManagerCallable(cm) {
@Override
public void call() {
PutFromLoadValidator testee = new PutFromLoadValidator(cm.getCache().getAdvancedCache(), cm);
long lastInsert = Long.MAX_VALUE;
for (int i = 0; i < 100; ++i) {
lastInsert = System.currentTimeMillis();
try {
withTx(tm, new Callable<Object>() {
@Override
public Object call() throws Exception {
SessionImplementor session = mock (SessionImplementor.class);
testee.registerPendingPut(session, KEY1, 0);
return null;
}
});
Thread.sleep(10);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
String ppName = cm.getCache().getName() + "-" + InfinispanRegionFactory.PENDING_PUTS_CACHE_NAME;
Map ppCache = cm.getCache(ppName, false);
assertNotNull(ppCache);
Object pendingPutMap = ppCache.get(KEY1);
long end = System.currentTimeMillis();
if (end - lastInsert > 500) {
log.warn("Test took too long");
return;
}
assertNotNull(pendingPutMap);
int size;
try {
Method sizeMethod = pendingPutMap.getClass().getMethod("size");
sizeMethod.setAccessible(true);
size = (Integer) sizeMethod.invoke(pendingPutMap);
} catch (Exception e) {
throw new RuntimeException(e);
}
// some of the pending puts need to be expired by now
assertTrue(size < 100);
// but some are still registered
assertTrue(size > 0);
}
});
}
}

View File

@ -1,565 +0,0 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
package org.hibernate.test.cache.infinispan.access;
import javax.transaction.TransactionManager;
import java.lang.reflect.Method;
import java.util.Map;
import java.util.concurrent.Callable;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;
import org.hibernate.cache.infinispan.InfinispanRegionFactory;
import org.hibernate.cache.infinispan.access.PutFromLoadValidator;
import org.hibernate.engine.spi.SessionImplementor;
import org.hibernate.test.cache.infinispan.functional.cluster.DualNodeJtaTransactionManagerImpl;
import org.hibernate.test.cache.infinispan.util.InfinispanTestingSetup;
import org.hibernate.testing.TestForIssue;
import org.infinispan.configuration.cache.ConfigurationBuilder;
import org.infinispan.manager.EmbeddedCacheManager;
import org.infinispan.test.CacheManagerCallable;
import org.infinispan.test.fwk.TestCacheManagerFactory;
import org.infinispan.util.logging.Log;
import org.infinispan.util.logging.LogFactory;
import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import static org.infinispan.test.TestingUtil.withCacheManager;
import static org.infinispan.test.TestingUtil.withTx;
import static org.mockito.Mockito.mock;
import static org.junit.Assert.*;
/**
* Tests of {@link PutFromLoadValidator}.
*
* @author Brian Stansberry
* @author Galder Zamarreño
* @version $Revision: $
*/
public class PutFromLoadValidatorUnitTestCase {
private static final Log log = LogFactory.getLog(
PutFromLoadValidatorUnitTestCase.class);
@Rule
public InfinispanTestingSetup infinispanTestIdentifier = new InfinispanTestingSetup();
private Object KEY1 = "KEY1";
private TransactionManager tm;
@Before
public void setUp() throws Exception {
tm = DualNodeJtaTransactionManagerImpl.getInstance("test");
}
@After
public void tearDown() throws Exception {
tm = null;
try {
DualNodeJtaTransactionManagerImpl.cleanupTransactions();
}
finally {
DualNodeJtaTransactionManagerImpl.cleanupTransactionManagers();
}
}
private static EmbeddedCacheManager createCacheManager() {
EmbeddedCacheManager cacheManager = TestCacheManagerFactory.createCacheManager(false);
cacheManager.defineConfiguration(InfinispanRegionFactory.PENDING_PUTS_CACHE_NAME,
InfinispanRegionFactory.PENDING_PUTS_CACHE_CONFIGURATION);
return cacheManager;
}
@Test
public void testNakedPut() throws Exception {
nakedPutTest(false);
}
@Test
public void testNakedPutTransactional() throws Exception {
nakedPutTest(true);
}
private void nakedPutTest(final boolean transactional) throws Exception {
withCacheManager(new CacheManagerCallable(createCacheManager()) {
@Override
public void call() {
PutFromLoadValidator testee = new PutFromLoadValidator(cm.getCache().getAdvancedCache(), cm);
exec(transactional, new NakedPut(testee, true));
}
});
}
@Test
public void testRegisteredPut() throws Exception {
registeredPutTest(false);
}
@Test
public void testRegisteredPutTransactional() throws Exception {
registeredPutTest(true);
}
private void registeredPutTest(final boolean transactional) throws Exception {
withCacheManager(new CacheManagerCallable(createCacheManager()) {
@Override
public void call() {
PutFromLoadValidator testee = new PutFromLoadValidator(cm.getCache().getAdvancedCache(), cm);
exec(transactional, new RegularPut(testee));
}
});
}
@Test
public void testNakedPutAfterKeyRemoval() throws Exception {
nakedPutAfterRemovalTest(false, false);
}
@Test
public void testNakedPutAfterKeyRemovalTransactional() throws Exception {
nakedPutAfterRemovalTest(true, false);
}
@Test
public void testNakedPutAfterRegionRemoval() throws Exception {
nakedPutAfterRemovalTest(false, true);
}
@Test
public void testNakedPutAfterRegionRemovalTransactional() throws Exception {
nakedPutAfterRemovalTest(true, true);
}
private void nakedPutAfterRemovalTest(final boolean transactional,
final boolean removeRegion) throws Exception {
withCacheManager(new CacheManagerCallable(createCacheManager()) {
@Override
public void call() {
PutFromLoadValidator testee = new PutFromLoadValidator(cm.getCache().getAdvancedCache());
Invalidation invalidation = new Invalidation(testee, removeRegion);
// the naked put can succeed because it has txTimestamp after invalidation
NakedPut nakedPut = new NakedPut(testee, true);
exec(transactional, invalidation, nakedPut);
}
});
}
@Test
public void testRegisteredPutAfterKeyRemoval() throws Exception {
registeredPutAfterRemovalTest(false, false);
}
@Test
public void testRegisteredPutAfterKeyRemovalTransactional() throws Exception {
registeredPutAfterRemovalTest(true, false);
}
@Test
public void testRegisteredPutAfterRegionRemoval() throws Exception {
registeredPutAfterRemovalTest(false, true);
}
@Test
public void testRegisteredPutAfterRegionRemovalTransactional() throws Exception {
registeredPutAfterRemovalTest(true, true);
}
private void registeredPutAfterRemovalTest(final boolean transactional,
final boolean removeRegion) throws Exception {
withCacheManager(new CacheManagerCallable(createCacheManager()) {
@Override
public void call() {
PutFromLoadValidator testee = new PutFromLoadValidator(cm.getCache().getAdvancedCache(), cm);
Invalidation invalidation = new Invalidation(testee, removeRegion);
RegularPut regularPut = new RegularPut(testee);
exec(transactional, invalidation, regularPut);
}
});
}
@Test
public void testRegisteredPutWithInterveningKeyRemoval() throws Exception {
registeredPutWithInterveningRemovalTest(false, false);
}
@Test
public void testRegisteredPutWithInterveningKeyRemovalTransactional() throws Exception {
registeredPutWithInterveningRemovalTest(true, false);
}
@Test
public void testRegisteredPutWithInterveningRegionRemoval() throws Exception {
registeredPutWithInterveningRemovalTest(false, true);
}
@Test
public void testRegisteredPutWithInterveningRegionRemovalTransactional() throws Exception {
registeredPutWithInterveningRemovalTest(true, true);
}
private void registeredPutWithInterveningRemovalTest(
final boolean transactional, final boolean removeRegion)
throws Exception {
withCacheManager(new CacheManagerCallable(createCacheManager()) {
@Override
public void call() {
PutFromLoadValidator testee = new PutFromLoadValidator(cm.getCache().getAdvancedCache(), cm);
try {
long txTimestamp = System.currentTimeMillis();
if (transactional) {
tm.begin();
}
SessionImplementor session1 = mock(SessionImplementor.class);
SessionImplementor session2 = mock(SessionImplementor.class);
testee.registerPendingPut(session1, KEY1, txTimestamp);
if (removeRegion) {
testee.beginInvalidatingRegion();
} else {
testee.beginInvalidatingKey(session2, KEY1);
}
PutFromLoadValidator.Lock lock = testee.acquirePutFromLoadLock(session1, KEY1, txTimestamp);
try {
assertNull(lock);
}
finally {
if (lock != null) {
testee.releasePutFromLoadLock(KEY1, lock);
}
if (removeRegion) {
testee.endInvalidatingRegion();
} else {
testee.endInvalidatingKey(session2, KEY1);
}
}
} catch (Exception e) {
throw new RuntimeException(e);
}
}
});
}
@Test
public void testMultipleRegistrations() throws Exception {
multipleRegistrationtest(false);
}
@Test
public void testMultipleRegistrationsTransactional() throws Exception {
multipleRegistrationtest(true);
}
private void multipleRegistrationtest(final boolean transactional) throws Exception {
withCacheManager(new CacheManagerCallable(createCacheManager()) {
@Override
public void call() {
final PutFromLoadValidator testee = new PutFromLoadValidator(cm.getCache().getAdvancedCache(), cm);
final CountDownLatch registeredLatch = new CountDownLatch(3);
final CountDownLatch finishedLatch = new CountDownLatch(3);
final AtomicInteger success = new AtomicInteger();
Runnable r = new Runnable() {
public void run() {
try {
long txTimestamp = System.currentTimeMillis();
if (transactional) {
tm.begin();
}
SessionImplementor session = mock (SessionImplementor.class);
testee.registerPendingPut(session, KEY1, txTimestamp);
registeredLatch.countDown();
registeredLatch.await(5, TimeUnit.SECONDS);
PutFromLoadValidator.Lock lock = testee.acquirePutFromLoadLock(session, KEY1, txTimestamp);
if (lock != null) {
try {
log.trace("Put from load lock acquired for key = " + KEY1);
success.incrementAndGet();
} finally {
testee.releasePutFromLoadLock(KEY1, lock);
}
} else {
log.trace("Unable to acquired putFromLoad lock for key = " + KEY1);
}
finishedLatch.countDown();
} catch (Exception e) {
e.printStackTrace();
}
}
};
ExecutorService executor = Executors.newFixedThreadPool(3);
// Start with a removal so the "isPutValid" calls will fail if
// any of the concurrent activity isn't handled properly
testee.beginInvalidatingRegion();
testee.endInvalidatingRegion();
try {
Thread.sleep(10);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
// Do the registration + isPutValid calls
executor.execute(r);
executor.execute(r);
executor.execute(r);
try {
finishedLatch.await(5, TimeUnit.SECONDS);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
assertEquals("All threads succeeded", 3, success.get());
}
});
}
@Test
public void testInvalidateKeyBlocksForInProgressPut() throws Exception {
invalidationBlocksForInProgressPutTest(true);
}
@Test
public void testInvalidateRegionBlocksForInProgressPut() throws Exception {
invalidationBlocksForInProgressPutTest(false);
}
private void invalidationBlocksForInProgressPutTest(final boolean keyOnly) throws Exception {
withCacheManager(new CacheManagerCallable(createCacheManager()) {
@Override
public void call() {
final PutFromLoadValidator testee = new PutFromLoadValidator(cm.getCache().getAdvancedCache(), cm);
final CountDownLatch removeLatch = new CountDownLatch(1);
final CountDownLatch pferLatch = new CountDownLatch(1);
final AtomicReference<Object> cache = new AtomicReference<Object>("INITIAL");
Callable<Boolean> pferCallable = new Callable<Boolean>() {
public Boolean call() throws Exception {
long txTimestamp = System.currentTimeMillis();
SessionImplementor session = mock (SessionImplementor.class);
testee.registerPendingPut(session, KEY1, txTimestamp);
PutFromLoadValidator.Lock lock = testee.acquirePutFromLoadLock(session, KEY1, txTimestamp);
if (lock != null) {
try {
removeLatch.countDown();
pferLatch.await();
cache.set("PFER");
return Boolean.TRUE;
}
finally {
testee.releasePutFromLoadLock(KEY1, lock);
}
}
return Boolean.FALSE;
}
};
Callable<Void> invalidateCallable = new Callable<Void>() {
public Void call() throws Exception {
removeLatch.await();
if (keyOnly) {
SessionImplementor session = mock (SessionImplementor.class);
testee.beginInvalidatingKey(session, KEY1);
} else {
testee.beginInvalidatingRegion();
}
cache.set(null);
return null;
}
};
ExecutorService executorService = Executors.newCachedThreadPool();
Future<Boolean> pferFuture = executorService.submit(pferCallable);
Future<Void> invalidateFuture = executorService.submit(invalidateCallable);
try {
try {
invalidateFuture.get(1, TimeUnit.SECONDS);
fail("invalidateFuture did not block");
}
catch (TimeoutException good) {}
pferLatch.countDown();
assertTrue(pferFuture.get(5, TimeUnit.SECONDS));
invalidateFuture.get(5, TimeUnit.SECONDS);
assertNull(cache.get());
} catch (Exception e) {
throw new RuntimeException(e);
}
}
});
}
protected void exec(boolean transactional, Callable<?>... callables) {
try {
if (transactional) {
for (Callable<?> c : callables) {
withTx(tm, c);
}
} else {
for (Callable<?> c : callables) {
c.call();
}
}
} catch (RuntimeException e) {
throw e;
} catch (Exception e) {
throw new RuntimeException(e);
}
}
private class Invalidation implements Callable<Void> {
private PutFromLoadValidator putFromLoadValidator;
private boolean removeRegion;
public Invalidation(PutFromLoadValidator putFromLoadValidator, boolean removeRegion) {
this.putFromLoadValidator = putFromLoadValidator;
this.removeRegion = removeRegion;
}
@Override
public Void call() throws Exception {
if (removeRegion) {
boolean success = putFromLoadValidator.beginInvalidatingRegion();
assertTrue(success);
putFromLoadValidator.endInvalidatingRegion();;
} else {
SessionImplementor session = mock (SessionImplementor.class);
boolean success = putFromLoadValidator.beginInvalidatingKey(session, KEY1);
assertTrue(success);
success = putFromLoadValidator.endInvalidatingKey(session, KEY1);
assertTrue(success);
}
// if we go for the timestamp-based approach, invalidation in the same millisecond
// as the registerPendingPut/acquirePutFromLoad lock results in failure.
Thread.sleep(10);
return null;
}
}
private class RegularPut implements Callable<Void> {
private PutFromLoadValidator putFromLoadValidator;
public RegularPut(PutFromLoadValidator putFromLoadValidator) {
this.putFromLoadValidator = putFromLoadValidator;
}
@Override
public Void call() throws Exception {
try {
long txTimestamp = System.currentTimeMillis(); // this should be acquired before UserTransaction.begin()
SessionImplementor session = mock (SessionImplementor.class);
putFromLoadValidator.registerPendingPut(session, KEY1, txTimestamp);
PutFromLoadValidator.Lock lock = putFromLoadValidator.acquirePutFromLoadLock(session, KEY1, txTimestamp);
try {
assertNotNull(lock);
} finally {
if (lock != null) {
putFromLoadValidator.releasePutFromLoadLock(KEY1, lock);
}
}
} catch (Exception e) {
throw new RuntimeException(e);
}
return null;
}
}
private class NakedPut implements Callable<Void> {
private final PutFromLoadValidator testee;
private final boolean expectSuccess;
public NakedPut(PutFromLoadValidator testee, boolean expectSuccess) {
this.testee = testee;
this.expectSuccess = expectSuccess;
}
@Override
public Void call() throws Exception {
try {
long txTimestamp = System.currentTimeMillis(); // this should be acquired before UserTransaction.begin()
SessionImplementor session = mock (SessionImplementor.class);
PutFromLoadValidator.Lock lock = testee.acquirePutFromLoadLock(session, KEY1, txTimestamp);
try {
if (expectSuccess) {
assertNotNull(lock);
} else {
assertNull(lock);
}
}
finally {
if (lock != null) {
testee.releasePutFromLoadLock(KEY1, lock);
}
}
} catch (Exception e) {
throw new RuntimeException(e);
}
return null;
}
}
@Test
@TestForIssue(jiraKey = "HHH-9928")
public void testGetForNullReleasePuts() {
EmbeddedCacheManager cm = TestCacheManagerFactory.createCacheManager(false);
ConfigurationBuilder cb = new ConfigurationBuilder().read(InfinispanRegionFactory.PENDING_PUTS_CACHE_CONFIGURATION);
cb.expiration().maxIdle(500);
cm.defineConfiguration(InfinispanRegionFactory.PENDING_PUTS_CACHE_NAME, cb.build());
withCacheManager(new CacheManagerCallable(cm) {
@Override
public void call() {
PutFromLoadValidator testee = new PutFromLoadValidator(cm.getCache().getAdvancedCache(), cm);
long lastInsert = Long.MAX_VALUE;
for (int i = 0; i < 100; ++i) {
lastInsert = System.currentTimeMillis();
try {
withTx(tm, new Callable<Object>() {
@Override
public Object call() throws Exception {
SessionImplementor session = mock (SessionImplementor.class);
testee.registerPendingPut(session, KEY1, 0);
return null;
}
});
Thread.sleep(10);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
String ppName = cm.getCache().getName() + "-" + InfinispanRegionFactory.PENDING_PUTS_CACHE_NAME;
Map ppCache = cm.getCache(ppName, false);
assertNotNull(ppCache);
Object pendingPutMap = ppCache.get(KEY1);
long end = System.currentTimeMillis();
if (end - lastInsert > 500) {
log.warn("Test took too long");
return;
}
assertNotNull(pendingPutMap);
int size;
try {
Method sizeMethod = pendingPutMap.getClass().getMethod("size");
sizeMethod.setAccessible(true);
size = (Integer) sizeMethod.invoke(pendingPutMap);
} catch (Exception e) {
throw new RuntimeException(e);
}
// some of the pending puts need to be expired by now
assertTrue(size < 100);
// but some are still registered
assertTrue(size > 0);
}
});
}
}

View File

@ -0,0 +1,226 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
package org.hibernate.test.cache.infinispan.collection;
import javax.transaction.TransactionManager;
import java.util.concurrent.Callable;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import junit.framework.AssertionFailedError;
import org.hibernate.cache.infinispan.InfinispanRegionFactory;
import org.hibernate.cache.infinispan.access.PutFromLoadValidator;
import org.hibernate.cache.infinispan.access.InvalidationCacheAccessDelegate;
import org.hibernate.cache.infinispan.collection.CollectionRegionImpl;
import org.hibernate.cache.infinispan.util.Caches;
import org.hibernate.cache.spi.access.CollectionRegionAccessStrategy;
import org.hibernate.engine.spi.SessionImplementor;
import org.hibernate.test.cache.infinispan.AbstractRegionAccessStrategyTest;
import org.hibernate.test.cache.infinispan.NodeEnvironment;
import org.hibernate.test.cache.infinispan.util.TestingKeyFactory;
import org.infinispan.AdvancedCache;
import org.infinispan.manager.EmbeddedCacheManager;
import org.infinispan.test.CacheManagerCallable;
import org.infinispan.test.fwk.TestCacheManagerFactory;
import org.junit.Test;
import static org.infinispan.test.TestingUtil.withCacheManager;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.mock;
/**
* Base class for tests of CollectionRegionAccessStrategy impls.
*
* @author Galder Zamarreño
* @since 3.5
*/
public abstract class AbstractCollectionRegionAccessStrategyTest extends
AbstractRegionAccessStrategyTest<CollectionRegionImpl, CollectionRegionAccessStrategy> {
protected static int testCount;
@Override
protected Object generateNextKey() {
return TestingKeyFactory.generateCollectionCacheKey( KEY_BASE + testCount++ );
}
@Override
protected CollectionRegionImpl getRegion(NodeEnvironment environment) {
return environment.getCollectionRegion( REGION_NAME, CACHE_DATA_DESCRIPTION );
}
@Override
protected CollectionRegionAccessStrategy getAccessStrategy(CollectionRegionImpl region) {
return region.buildAccessStrategy( getAccessType() );
}
@Test
public abstract void testCacheConfiguration();
@Test
public void testGetRegion() {
assertEquals( "Correct region", localRegion, localAccessStrategy.getRegion() );
}
@Test
public void testPutFromLoadRemoveDoesNotProduceStaleData() throws Exception {
final CountDownLatch pferLatch = new CountDownLatch( 1 );
final CountDownLatch removeLatch = new CountDownLatch( 1 );
final TransactionManager remoteTm = remoteRegion.getTransactionManager();
withCacheManager(new CacheManagerCallable(createCacheManager()) {
@Override
public void call() {
PutFromLoadValidator validator = getPutFromLoadValidator(remoteRegion.getCache(), cm, removeLatch, pferLatch);
final InvalidationCacheAccessDelegate delegate =
InvalidationCacheAccessDelegate.create(localRegion, validator);
Callable<Void> pferCallable = new Callable<Void>() {
public Void call() throws Exception {
SessionImplementor session = mockedSession();
delegate.putFromLoad(session, "k1", "v1", 0, null );
return null;
}
};
Callable<Void> removeCallable = new Callable<Void>() {
public Void call() throws Exception {
removeLatch.await();
SessionImplementor session = mockedSession();
withTx(localEnvironment, session, new Callable<Void>() {
@Override
public Void call() throws Exception {
delegate.remove(session, "k1");
return null;
}
});
pferLatch.countDown();
return null;
}
};
ExecutorService executorService = Executors.newCachedThreadPool();
Future<Void> pferFuture = executorService.submit( pferCallable );
Future<Void> removeFuture = executorService.submit( removeCallable );
try {
pferFuture.get();
removeFuture.get();
} catch (Exception e) {
throw new RuntimeException(e);
}
assertFalse(localRegion.getCache().containsKey("k1"));
}
});
}
private static EmbeddedCacheManager createCacheManager() {
EmbeddedCacheManager cacheManager = TestCacheManagerFactory.createCacheManager(false);
cacheManager.defineConfiguration(InfinispanRegionFactory.PENDING_PUTS_CACHE_NAME,
InfinispanRegionFactory.PENDING_PUTS_CACHE_CONFIGURATION);
return cacheManager;
}
protected PutFromLoadValidator getPutFromLoadValidator(AdvancedCache cache, EmbeddedCacheManager cm,
CountDownLatch removeLatch, CountDownLatch pferLatch) {
// remove the interceptor inserted by default PutFromLoadValidator, we're using different one
PutFromLoadValidator.removeFromCache(cache);
return new PutFromLoadValidator(cache, cm) {
@Override
public Lock acquirePutFromLoadLock(SessionImplementor session, Object key, long txTimestamp) {
Lock lock = super.acquirePutFromLoadLock(session, key, txTimestamp);
try {
removeLatch.countDown();
pferLatch.await( 2, TimeUnit.SECONDS );
}
catch (InterruptedException e) {
log.debug( "Interrupted" );
Thread.currentThread().interrupt();
}
catch (Exception e) {
log.error( "Error", e );
throw new RuntimeException( "Error", e );
}
return lock;
}
};
}
@Override
protected void putFromLoadTest(final boolean useMinimalAPI) throws Exception {
final Object KEY = generateNextKey();
final CountDownLatch writeLatch1 = new CountDownLatch( 1 );
final CountDownLatch writeLatch2 = new CountDownLatch( 1 );
final CountDownLatch completionLatch = new CountDownLatch( 2 );
Thread node1 = new Thread() {
@Override
public void run() {
try {
long txTimestamp = System.currentTimeMillis();
SessionImplementor session = mockedSession();
withTx(localEnvironment, session, () -> {
assertNull(localAccessStrategy.get(session, KEY, txTimestamp));
writeLatch1.await();
if (useMinimalAPI) {
localAccessStrategy.putFromLoad(session, KEY, VALUE2, txTimestamp, new Integer(2), true);
} else {
localAccessStrategy.putFromLoad(session, KEY, VALUE2, txTimestamp, new Integer(2));
}
return null;
});
}
catch (Exception e) {
log.error( "node1 caught exception", e );
node1Exception = e;
}
catch (AssertionFailedError e) {
node1Failure = e;
}
finally {
// Let node2 write
writeLatch2.countDown();
completionLatch.countDown();
}
}
};
Thread node2 = new PutFromLoadNode2(KEY, writeLatch1, writeLatch2, useMinimalAPI, completionLatch);
node1.setDaemon( true );
node2.setDaemon( true );
node1.start();
node2.start();
assertTrue( "Threads completed", completionLatch.await( 2, TimeUnit.SECONDS ) );
assertThreadsRanCleanly();
long txTimestamp = System.currentTimeMillis();
assertEquals( VALUE2, localAccessStrategy.get(mockedSession(), KEY, txTimestamp ) );
Object remoteValue = remoteAccessStrategy.get(mockedSession(), KEY, txTimestamp);
if (isUsingInvalidation()) {
assertEquals( VALUE1, remoteValue);
}
else {
assertEquals( VALUE2, remoteValue);
}
}
}

View File

@ -1,513 +0,0 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
package org.hibernate.test.cache.infinispan.collection;
import javax.transaction.TransactionManager;
import java.util.concurrent.Callable;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import junit.framework.AssertionFailedError;
import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
import org.hibernate.cache.infinispan.InfinispanRegionFactory;
import org.hibernate.cache.infinispan.access.PutFromLoadValidator;
import org.hibernate.cache.infinispan.access.TransactionalAccessDelegate;
import org.hibernate.cache.infinispan.collection.CollectionRegionImpl;
import org.hibernate.cache.infinispan.util.Caches;
import org.hibernate.cache.internal.CacheDataDescriptionImpl;
import org.hibernate.cache.spi.CacheDataDescription;
import org.hibernate.cache.spi.access.AccessType;
import org.hibernate.cache.spi.access.CollectionRegionAccessStrategy;
import org.hibernate.engine.spi.SessionImplementor;
import org.hibernate.internal.util.compare.ComparableComparator;
import org.hibernate.test.cache.infinispan.AbstractNonFunctionalTestCase;
import org.hibernate.test.cache.infinispan.NodeEnvironment;
import org.hibernate.test.cache.infinispan.util.CacheTestUtil;
import org.hibernate.test.cache.infinispan.util.InfinispanTestingSetup;
import org.hibernate.test.cache.infinispan.util.TestingKeyFactory;
import org.infinispan.AdvancedCache;
import org.infinispan.manager.EmbeddedCacheManager;
import org.infinispan.test.CacheManagerCallable;
import org.infinispan.test.fwk.TestCacheManagerFactory;
import org.infinispan.transaction.tm.BatchModeTransactionManager;
import org.jboss.logging.Logger;
import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import static org.infinispan.test.TestingUtil.withCacheManager;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.mock;
/**
* Base class for tests of CollectionRegionAccessStrategy impls.
*
* @author Galder Zamarreño
* @since 3.5
*/
public abstract class AbstractCollectionRegionAccessStrategyTestCase extends AbstractNonFunctionalTestCase {
private static final Logger log = Logger.getLogger( AbstractCollectionRegionAccessStrategyTestCase.class );
@Rule
public InfinispanTestingSetup infinispanTestIdentifier = new InfinispanTestingSetup();
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 NodeEnvironment localEnvironment;
protected CollectionRegionImpl localCollectionRegion;
protected CollectionRegionAccessStrategy localAccessStrategy;
protected SessionImplementor localSession;
protected NodeEnvironment remoteEnvironment;
protected CollectionRegionImpl remoteCollectionRegion;
protected CollectionRegionAccessStrategy remoteAccessStrategy;
protected SessionImplementor remoteSession;
protected boolean invalidation;
protected boolean synchronous;
protected Exception node1Exception;
protected Exception node2Exception;
protected AssertionFailedError node1Failure;
protected AssertionFailedError node2Failure;
protected abstract AccessType getAccessType();
@Before
public void prepareResources() throws Exception {
// to mimic exactly the old code results, both environments here are exactly the same...
StandardServiceRegistryBuilder ssrb = createStandardServiceRegistryBuilder( getConfigurationName() );
localEnvironment = new NodeEnvironment( ssrb );
localEnvironment.prepare();
localCollectionRegion = localEnvironment.getCollectionRegion( REGION_NAME, getCacheDataDescription() );
localAccessStrategy = localCollectionRegion.buildAccessStrategy( getAccessType() );
localSession = mock(SessionImplementor.class);
invalidation = Caches.isInvalidationCache(localCollectionRegion.getCache());
synchronous = Caches.isSynchronousCache(localCollectionRegion.getCache());
// Sleep a bit to avoid concurrent FLUSH problem
avoidConcurrentFlush();
remoteEnvironment = new NodeEnvironment( ssrb );
remoteEnvironment.prepare();
remoteCollectionRegion = remoteEnvironment.getCollectionRegion( REGION_NAME, getCacheDataDescription() );
remoteAccessStrategy = remoteCollectionRegion.buildAccessStrategy( getAccessType() );
remoteSession = mock(SessionImplementor.class);
}
protected abstract String getConfigurationName();
protected static StandardServiceRegistryBuilder createStandardServiceRegistryBuilder(String configName) {
final StandardServiceRegistryBuilder ssrb = CacheTestUtil.buildBaselineStandardServiceRegistryBuilder(
REGION_PREFIX,
InfinispanRegionFactory.class,
true,
false
);
ssrb.applySetting( InfinispanRegionFactory.ENTITY_CACHE_RESOURCE_PROP, configName );
return ssrb;
}
protected CacheDataDescription getCacheDataDescription() {
return new CacheDataDescriptionImpl( true, true, ComparableComparator.INSTANCE, null);
}
@After
public void releaseResources() throws Exception {
if ( localEnvironment != null ) {
localEnvironment.release();
}
if ( remoteEnvironment != null ) {
remoteEnvironment.release();
}
}
protected boolean isUsingInvalidation() {
return invalidation;
}
protected boolean isSynchronous() {
return synchronous;
}
@Test
public abstract void testCacheConfiguration();
@Test
public void testGetRegion() {
assertEquals( "Correct region", localCollectionRegion, localAccessStrategy.getRegion() );
}
@Test
public void testPutFromLoadRemoveDoesNotProduceStaleData() throws Exception {
final CountDownLatch pferLatch = new CountDownLatch( 1 );
final CountDownLatch removeLatch = new CountDownLatch( 1 );
final TransactionManager remoteTm = remoteCollectionRegion.getTransactionManager();
withCacheManager(new CacheManagerCallable(createCacheManager()) {
@Override
public void call() {
PutFromLoadValidator validator = getPutFromLoadValidator(remoteCollectionRegion.getCache(), cm, removeLatch, pferLatch);
final TransactionalAccessDelegate delegate =
TransactionalAccessDelegate.create(localCollectionRegion, validator);
final TransactionManager localTm = localCollectionRegion.getTransactionManager();
Callable<Void> pferCallable = new Callable<Void>() {
public Void call() throws Exception {
SessionImplementor session = mock(SessionImplementor.class);
delegate.putFromLoad(session, "k1", "v1", 0, null );
return null;
}
};
Callable<Void> removeCallable = new Callable<Void>() {
public Void call() throws Exception {
removeLatch.await();
Caches.withinTx(localTm, new Callable<Void>() {
@Override
public Void call() throws Exception {
SessionImplementor session = mock(SessionImplementor.class);
delegate.remove(session, "k1");
return null;
}
});
pferLatch.countDown();
return null;
}
};
ExecutorService executorService = Executors.newCachedThreadPool();
Future<Void> pferFuture = executorService.submit( pferCallable );
Future<Void> removeFuture = executorService.submit( removeCallable );
try {
pferFuture.get();
removeFuture.get();
} catch (Exception e) {
throw new RuntimeException(e);
}
assertFalse(localCollectionRegion.getCache().containsKey("k1"));
}
});
}
private static EmbeddedCacheManager createCacheManager() {
EmbeddedCacheManager cacheManager = TestCacheManagerFactory.createCacheManager(false);
cacheManager.defineConfiguration(InfinispanRegionFactory.PENDING_PUTS_CACHE_NAME,
InfinispanRegionFactory.PENDING_PUTS_CACHE_CONFIGURATION);
return cacheManager;
}
protected PutFromLoadValidator getPutFromLoadValidator(AdvancedCache cache, EmbeddedCacheManager cm,
CountDownLatch removeLatch, CountDownLatch pferLatch) {
// remove the interceptor inserted by default PutFromLoadValidator, we're using different one
PutFromLoadValidator.removeFromCache(cache);
return new PutFromLoadValidator(cache, cm) {
@Override
public Lock acquirePutFromLoadLock(SessionImplementor session, Object key, long txTimestamp) {
Lock lock = super.acquirePutFromLoadLock(session, key, txTimestamp);
try {
removeLatch.countDown();
pferLatch.await( 2, TimeUnit.SECONDS );
}
catch (InterruptedException e) {
log.debug( "Interrupted" );
Thread.currentThread().interrupt();
}
catch (Exception e) {
log.error( "Error", e );
throw new RuntimeException( "Error", e );
}
return lock;
}
};
}
@Test
public void testPutFromLoad() throws Exception {
putFromLoadTest( false );
}
@Test
public void testPutFromLoadMinimal() throws Exception {
putFromLoadTest( true );
}
private void putFromLoadTest(final boolean useMinimalAPI) throws Exception {
final Object KEY = TestingKeyFactory.generateCollectionCacheKey( 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(localSession, KEY, txTimestamp ) );
writeLatch1.await();
if ( useMinimalAPI ) {
localAccessStrategy.putFromLoad(localSession, KEY, VALUE2, txTimestamp, new Integer( 2 ), true );
}
else {
localAccessStrategy.putFromLoad(localSession, 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(remoteSession, 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(remoteSession, KEY, VALUE1, txTimestamp, new Integer( 1 ), true );
}
else {
remoteAccessStrategy.putFromLoad(remoteSession, 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(localSession, KEY, txTimestamp ) );
assertEquals( msg2, expected2, remoteAccessStrategy.get(remoteSession, KEY, txTimestamp ) );
}
@Test
public void testRemove() throws Exception {
evictOrRemoveTest( false );
}
@Test
public void testRemoveAll() throws Exception {
evictOrRemoveAllTest( false );
}
@Test
public void testEvict() throws Exception {
evictOrRemoveTest( true );
}
@Test
public void testEvictAll() throws Exception {
evictOrRemoveAllTest( true );
}
private void evictOrRemoveTest(final boolean evict) throws Exception {
final Object KEY = TestingKeyFactory.generateCollectionCacheKey( KEY_BASE + testCount++ );
assertNull( "local is clean", localAccessStrategy.get(localSession, KEY, System.currentTimeMillis() ) );
assertNull( "remote is clean", remoteAccessStrategy.get(remoteSession, KEY, System.currentTimeMillis() ) );
localAccessStrategy.putFromLoad(localSession, KEY, VALUE1, System.currentTimeMillis(), new Integer( 1 ) );
assertEquals( VALUE1, localAccessStrategy.get(localSession, KEY, System.currentTimeMillis() ) );
remoteAccessStrategy.putFromLoad(remoteSession, KEY, VALUE1, System.currentTimeMillis(), new Integer( 1 ) );
assertEquals( VALUE1, remoteAccessStrategy.get(remoteSession, KEY, System.currentTimeMillis() ) );
// Wait for async propagation
sleep( 250 );
Caches.withinTx(localCollectionRegion.getTransactionManager(), new Callable<Void>() {
@Override
public Void call() throws Exception {
if (evict)
localAccessStrategy.evict(KEY);
else
localAccessStrategy.remove(localSession, KEY);
return null;
}
});
assertEquals( null, localAccessStrategy.get(localSession, KEY, System.currentTimeMillis() ) );
assertEquals( null, remoteAccessStrategy.get(remoteSession, KEY, System.currentTimeMillis() ) );
}
private void evictOrRemoveAllTest(final boolean evict) throws Exception {
final Object KEY = TestingKeyFactory.generateCollectionCacheKey( KEY_BASE + testCount++ );
assertEquals( 0, localCollectionRegion.getCache().size() );
assertEquals( 0, remoteCollectionRegion.getCache().size() );
assertNull( "local is clean", localAccessStrategy.get(localSession, KEY, System.currentTimeMillis() ) );
assertNull( "remote is clean", remoteAccessStrategy.get(remoteSession, KEY, System.currentTimeMillis() ) );
localAccessStrategy.putFromLoad(localSession, KEY, VALUE1, System.currentTimeMillis(), new Integer( 1 ) );
assertEquals( VALUE1, localAccessStrategy.get(localSession, KEY, System.currentTimeMillis() ) );
remoteAccessStrategy.putFromLoad(remoteSession, KEY, VALUE1, System.currentTimeMillis(), new Integer( 1 ) );
assertEquals( VALUE1, remoteAccessStrategy.get(remoteSession, KEY, System.currentTimeMillis() ) );
// Wait for async propagation
sleep( 250 );
Caches.withinTx(localCollectionRegion.getTransactionManager(), new Callable<Void>() {
@Override
public Void call() throws Exception {
if (evict)
localAccessStrategy.evictAll();
else
localAccessStrategy.removeAll();
return null;
}
});
// This should re-establish the region root node
assertNull( localAccessStrategy.get(localSession, KEY, System.currentTimeMillis() ) );
assertEquals( 0, localCollectionRegion.getCache().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(remoteSession, KEY, System.currentTimeMillis() ) );
assertEquals( 0, remoteCollectionRegion.getCache().size() );
// Wait for async propagation of EndInvalidationCommand
sleep( 250 );
// Test whether the get above messes up the optimistic version
remoteAccessStrategy.putFromLoad(remoteSession, KEY, VALUE1, System.currentTimeMillis(), new Integer( 1 ) );
assertEquals( VALUE1, remoteAccessStrategy.get(remoteSession, KEY, System.currentTimeMillis() ) );
assertEquals( 1, remoteCollectionRegion.getCache().size() );
// Wait for async propagation of the putFromLoad
sleep( 250 );
assertEquals(
"local is correct", (isUsingInvalidation() ? null : VALUE1), localAccessStrategy.get(
localSession, KEY, System
.currentTimeMillis()
)
);
assertEquals( "remote is correct", VALUE1, remoteAccessStrategy.get(remoteSession, KEY, System.currentTimeMillis() ) );
}
private void rollback() {
try {
BatchModeTransactionManager.getInstance().rollback();
}
catch (Exception e) {
log.error( e.getMessage(), e );
}
}
}

View File

@ -1,21 +0,0 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
package org.hibernate.test.cache.infinispan.collection;
import org.hibernate.cache.spi.access.AccessType;
/**
* Base class for tests of TRANSACTIONAL access.
*
* @author <a href="brian.stansberry@jboss.com">Brian Stansberry</a>
*/
public abstract class AbstractReadOnlyAccessTestCase extends AbstractCollectionRegionAccessStrategyTestCase {
@Override
protected AccessType getAccessType() {
return AccessType.READ_ONLY;
}
}

View File

@ -1,20 +0,0 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
package org.hibernate.test.cache.infinispan.collection;
import org.hibernate.cache.spi.access.AccessType;
/**
* Base class for tests of TRANSACTIONAL access.
*
* @author <a href="brian.stansberry@jboss.com">Brian Stansberry</a>
*/
public abstract class AbstractTransactionalAccessTestCase extends AbstractCollectionRegionAccessStrategyTestCase {
@Override
protected AccessType getAccessType() {
return AccessType.TRANSACTIONAL;
}
}

View File

@ -0,0 +1,53 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
package org.hibernate.test.cache.infinispan.collection;
import org.hibernate.cache.spi.RegionFactory;
import org.hibernate.cache.spi.access.AccessType;
import org.hibernate.cache.spi.access.CollectionRegionAccessStrategy;
import org.hibernate.test.cache.infinispan.AbstractExtraAPITest;
import org.hibernate.test.cache.infinispan.util.TestInfinispanRegionFactory;
import static org.junit.Assert.assertNull;
import static org.mockito.Mockito.mock;
/**
* @author Galder Zamarreño
* @since 3.5
*/
public abstract class CollectionRegionAccessExtraAPITest extends AbstractExtraAPITest<CollectionRegionAccessStrategy> {
@Override
protected CollectionRegionAccessStrategy getAccessStrategy() {
return environment.getCollectionRegion( REGION_NAME, CACHE_DATA_DESCRIPTION).buildAccessStrategy( getAccessType() );
}
public static class Transactional extends CollectionRegionAccessExtraAPITest {
@Override
protected AccessType getAccessType() {
return AccessType.TRANSACTIONAL;
}
@Override
protected Class<? extends RegionFactory> getRegionFactoryClass() {
return TestInfinispanRegionFactory.Transactional.class;
}
}
public static class ReadWrite extends CollectionRegionAccessExtraAPITest {
@Override
protected AccessType getAccessType() {
return AccessType.READ_WRITE;
}
}
public static class ReadOnly extends CollectionRegionAccessExtraAPITest {
@Override
protected AccessType getAccessType() {
return AccessType.READ_ONLY;
}
}
}

View File

@ -0,0 +1,64 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
package org.hibernate.test.cache.infinispan.collection;
import java.util.Properties;
import org.hibernate.cache.CacheException;
import org.hibernate.cache.infinispan.InfinispanRegionFactory;
import org.hibernate.cache.spi.CacheDataDescription;
import org.hibernate.cache.spi.CollectionRegion;
import org.hibernate.cache.spi.Region;
import org.hibernate.cache.spi.RegionFactory;
import org.hibernate.cache.spi.access.AccessType;
import org.hibernate.cache.spi.access.CollectionRegionAccessStrategy;
import org.hibernate.test.cache.infinispan.AbstractEntityCollectionRegionTest;
import org.infinispan.AdvancedCache;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.fail;
/**
* @author Galder Zamarreño
*/
public class CollectionRegionImplTest extends AbstractEntityCollectionRegionTest {
@Override
protected void supportedAccessTypeTest(RegionFactory regionFactory, Properties properties) {
CollectionRegion region = regionFactory.buildCollectionRegion("test", properties, MUTABLE_NON_VERSIONED);
assertNotNull(region.buildAccessStrategy(AccessType.READ_ONLY));
assertNotNull(region.buildAccessStrategy(AccessType.READ_WRITE));
assertNotNull(region.buildAccessStrategy(AccessType.TRANSACTIONAL));
try {
region.buildAccessStrategy(AccessType.NONSTRICT_READ_WRITE);
fail("Incorrectly got NONSTRICT_READ_WRITE");
} catch (CacheException good) {
}
}
@Override
protected Region createRegion(InfinispanRegionFactory regionFactory, String regionName, Properties properties, CacheDataDescription cdd) {
return regionFactory.buildCollectionRegion(regionName, properties, cdd);
}
@Override
protected AdvancedCache getInfinispanCache(InfinispanRegionFactory regionFactory) {
return regionFactory.getCacheManager().getCache(InfinispanRegionFactory.DEF_ENTITY_RESOURCE).getAdvancedCache();
}
@Override
protected void putInRegion(Region region, Object key, Object value) {
CollectionRegionAccessStrategy strategy = ((CollectionRegion) region).buildAccessStrategy(AccessType.TRANSACTIONAL);
strategy.putFromLoad(null, key, value, System.currentTimeMillis(), new Integer(1));
}
@Override
protected void removeFromRegion(Region region, Object key) {
((CollectionRegion) region).buildAccessStrategy(AccessType.TRANSACTIONAL).remove(null, key);
}
}

View File

@ -1,70 +0,0 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
package org.hibernate.test.cache.infinispan.collection;
import java.util.Properties;
import org.hibernate.cache.CacheException;
import org.hibernate.cache.infinispan.InfinispanRegionFactory;
import org.hibernate.cache.spi.CacheDataDescription;
import org.hibernate.cache.spi.CollectionRegion;
import org.hibernate.cache.spi.Region;
import org.hibernate.cache.spi.RegionFactory;
import org.hibernate.cache.spi.access.AccessType;
import org.hibernate.cache.spi.access.CollectionRegionAccessStrategy;
import org.hibernate.test.cache.infinispan.AbstractEntityCollectionRegionTestCase;
import org.infinispan.AdvancedCache;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.fail;
/**
* Tests of CollectionRegionImpl.
*
* @author Galder Zamarreño
*/
public class CollectionRegionImplTestCase extends AbstractEntityCollectionRegionTestCase {
@Override
protected void supportedAccessTypeTest(RegionFactory regionFactory, Properties properties) {
CollectionRegion region = regionFactory.buildCollectionRegion("test", properties, MUTABLE_NON_VERSIONED);
assertNull("Got TRANSACTIONAL", region.buildAccessStrategy(AccessType.TRANSACTIONAL)
.lockRegion());
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 Region createRegion(InfinispanRegionFactory regionFactory, String regionName, Properties properties, CacheDataDescription cdd) {
return regionFactory.buildCollectionRegion(regionName, properties, cdd);
}
@Override
protected AdvancedCache getInfinispanCache(InfinispanRegionFactory regionFactory) {
return regionFactory.getCacheManager().getCache(InfinispanRegionFactory.DEF_ENTITY_RESOURCE).getAdvancedCache();
}
@Override
protected void putInRegion(Region region, Object key, Object value) {
CollectionRegionAccessStrategy strategy = ((CollectionRegion) region).buildAccessStrategy(AccessType.TRANSACTIONAL);
strategy.putFromLoad(null, key, value, System.currentTimeMillis(), new Integer(1));
}
@Override
protected void removeFromRegion(Region region, Object key) {
((CollectionRegion) region).buildAccessStrategy(AccessType.TRANSACTIONAL).remove(null, key);
}
}

View File

@ -0,0 +1,39 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
package org.hibernate.test.cache.infinispan.collection;
import org.hibernate.cache.spi.access.AccessType;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
/**
* Base class for tests of READ_ONLY access.
*
* @author <a href="brian.stansberry@jboss.com">Brian Stansberry</a>
*/
public abstract class CollectionRegionReadOnlyAccessTest extends AbstractCollectionRegionAccessStrategyTest {
@Override
protected AccessType getAccessType() {
return AccessType.READ_ONLY;
}
/**
* Tests READ_ONLY access when invalidation is used.
*
* @author Galder Zamarreño
* @since 3.5
*/
public static class Invalidation extends CollectionRegionReadOnlyAccessTest {
@Override
public void testCacheConfiguration() {
assertFalse(isTransactional());
assertTrue( "Using Invalidation", isUsingInvalidation() );
assertTrue( isSynchronous() );
}
}
}

View File

@ -0,0 +1,27 @@
package org.hibernate.test.cache.infinispan.collection;
import org.hibernate.cache.spi.access.AccessType;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
/**
* Tests read-write access
*
* @author Radim Vansa &lt;rvansa@redhat.com&gt;
*/
public abstract class CollectionRegionReadWriteAccessTest extends AbstractCollectionRegionAccessStrategyTest {
@Override
protected AccessType getAccessType() {
return AccessType.READ_WRITE;
}
public static class Invalidation extends CollectionRegionReadWriteAccessTest {
@Override
public void testCacheConfiguration() {
assertFalse(isTransactional());
assertTrue(isUsingInvalidation());
assertTrue(isSynchronous());
}
}
}

View File

@ -0,0 +1,45 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
package org.hibernate.test.cache.infinispan.collection;
import org.hibernate.cache.spi.RegionFactory;
import org.hibernate.cache.spi.access.AccessType;
import org.hibernate.test.cache.infinispan.util.TestInfinispanRegionFactory;
import static org.junit.Assert.assertTrue;
/**
* Base class for tests of TRANSACTIONAL access.
*
* @author <a href="brian.stansberry@jboss.com">Brian Stansberry</a>
*/
public abstract class CollectionRegionTransactionalAccessTest extends AbstractCollectionRegionAccessStrategyTest {
@Override
protected AccessType getAccessType() {
return AccessType.TRANSACTIONAL;
}
@Override
protected Class<? extends RegionFactory> getRegionFactoryClass() {
return TestInfinispanRegionFactory.Transactional.class;
}
/**
* InvalidatedTransactionalTestCase.
*
* @author Galder Zamarreño
* @since 3.5
*/
public static class Invalidation extends CollectionRegionTransactionalAccessTest {
@Override
public void testCacheConfiguration() {
assertTrue("Transactions", isTransactional());
assertTrue("Using Invalidation", isUsingInvalidation());
assertTrue("Synchronous mode", isSynchronous());
}
}
}

View File

@ -1,31 +0,0 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
package org.hibernate.test.cache.infinispan.collection;
import org.junit.Test;
import static org.junit.Assert.assertTrue;
/**
* InvalidatedTransactionalTestCase.
*
* @author Galder Zamarreño
* @since 3.5
*/
public class InvalidatedTransactionalTestCase extends AbstractTransactionalAccessTestCase {
@Test
@Override
public void testCacheConfiguration() {
assertTrue("Using Invalidation", isUsingInvalidation());
assertTrue("Synchronous mode", isSynchronous());
}
@Override
protected String getConfigurationName() {
return "entity"; // todo : should this be "collection"? the original code used "entity"...
}
}

View File

@ -1,27 +0,0 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
package org.hibernate.test.cache.infinispan.collection;
import static org.junit.Assert.assertTrue;
/**
* Tests READ_ONLY access when invalidation is used.
*
* @author Galder Zamarreño
* @since 3.5
*/
public class ReadOnlyTestCase extends AbstractReadOnlyAccessTestCase {
@Override
public void testCacheConfiguration() {
assertTrue( "Using Invalidation", isUsingInvalidation() );
}
@Override
protected String getConfigurationName() {
return "entity"; // todo : should this be "collection"? the original code used "entity"...
}
}

View File

@ -1,111 +0,0 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
package org.hibernate.test.cache.infinispan.collection;
import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
import org.hibernate.cache.infinispan.InfinispanRegionFactory;
import org.hibernate.cache.internal.CacheDataDescriptionImpl;
import org.hibernate.cache.spi.CacheDataDescription;
import org.hibernate.cache.spi.access.AccessType;
import org.hibernate.cache.spi.access.CollectionRegionAccessStrategy;
import org.hibernate.cache.spi.access.SoftLock;
import org.hibernate.engine.spi.SessionImplementor;
import org.hibernate.internal.util.compare.ComparableComparator;
import org.hibernate.test.cache.infinispan.AbstractNonFunctionalTestCase;
import org.hibernate.test.cache.infinispan.NodeEnvironment;
import org.hibernate.test.cache.infinispan.util.CacheTestUtil;
import org.hibernate.test.cache.infinispan.util.InfinispanTestingSetup;
import org.hibernate.test.cache.infinispan.util.TestingKeyFactory;
import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import static org.junit.Assert.assertNull;
import static org.mockito.Mockito.mock;
/**
* TransactionalExtraAPITestCase.
*
* @author Galder Zamarreño
* @since 3.5
*/
public class TransactionalExtraAPITestCase extends AbstractNonFunctionalTestCase {
@Rule
public InfinispanTestingSetup infinispanTestIdentifier = new InfinispanTestingSetup();
public static final String REGION_NAME = "test/com.foo.test";
public static final Object KEY = TestingKeyFactory.generateCollectionCacheKey( "KEY" );
public static final CacheDataDescription CACHE_DATA_DESCRIPTION
= new CacheDataDescriptionImpl(false, false, ComparableComparator.INSTANCE, null);
private static final SessionImplementor SESSION = mock(SessionImplementor.class);
private NodeEnvironment environment;
private static CollectionRegionAccessStrategy accessStrategy;
@Before
public final void prepareLocalAccessStrategy() throws Exception {
environment = new NodeEnvironment( createStandardServiceRegistryBuilder() );
environment.prepare();
// Sleep a bit to avoid concurrent FLUSH problem
avoidConcurrentFlush();
accessStrategy = environment.getCollectionRegion( REGION_NAME, CACHE_DATA_DESCRIPTION).buildAccessStrategy( getAccessType() );
}
protected StandardServiceRegistryBuilder createStandardServiceRegistryBuilder() {
StandardServiceRegistryBuilder ssrb = CacheTestUtil.buildBaselineStandardServiceRegistryBuilder(
REGION_PREFIX, InfinispanRegionFactory.class, true, false
);
ssrb.applySetting( InfinispanRegionFactory.ENTITY_CACHE_RESOURCE_PROP, getCacheConfigName() );
return ssrb;
}
protected String getCacheConfigName() {
return "entity";
}
protected AccessType getAccessType() {
return AccessType.TRANSACTIONAL;
}
@After
public final void releaseLocalAccessStrategy() throws Exception {
if ( environment != null ) {
environment.release();
}
}
protected CollectionRegionAccessStrategy getCollectionAccessStrategy() {
return accessStrategy;
}
@Test
public void testLockItem() {
assertNull( getCollectionAccessStrategy().lockItem(SESSION, KEY, new Integer( 1 ) ) );
}
@Test
public void testLockRegion() {
assertNull( getCollectionAccessStrategy().lockRegion() );
}
@Test
public void testUnlockItem() {
getCollectionAccessStrategy().unlockItem(SESSION, KEY, new MockSoftLock() );
}
@Test
public void testUnlockRegion() {
getCollectionAccessStrategy().unlockItem(SESSION, KEY, new MockSoftLock() );
}
public static class MockSoftLock implements SoftLock {
}
}

View File

@ -0,0 +1,400 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
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.infinispan.entity.EntityRegionImpl;
import org.hibernate.cache.spi.access.EntityRegionAccessStrategy;
import org.hibernate.cache.spi.access.SoftLock;
import org.hibernate.engine.spi.SessionImplementor;
import org.hibernate.test.cache.infinispan.AbstractRegionAccessStrategyTest;
import org.hibernate.test.cache.infinispan.NodeEnvironment;
import org.hibernate.test.cache.infinispan.util.TestSynchronization;
import org.hibernate.test.cache.infinispan.util.TestingKeyFactory;
import org.infinispan.transaction.tm.BatchModeTransactionManager;
import org.junit.Test;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.mock;
/**
* Base class for tests of EntityRegionAccessStrategy impls.
*
* @author Galder Zamarreño
* @since 3.5
*/
public abstract class AbstractEntityRegionAccessStrategyTest extends
AbstractRegionAccessStrategyTest<EntityRegionImpl, EntityRegionAccessStrategy> {
protected static int testCount;
@Override
protected Object generateNextKey() {
return TestingKeyFactory.generateEntityCacheKey( KEY_BASE + testCount++ );
}
@Override
protected EntityRegionImpl getRegion(NodeEnvironment environment) {
return environment.getEntityRegion(REGION_NAME, CACHE_DATA_DESCRIPTION);
}
@Override
protected EntityRegionAccessStrategy getAccessStrategy(EntityRegionImpl region) {
return region.buildAccessStrategy(getAccessType());
}
@Test
public abstract void testCacheConfiguration();
@Test
public void testGetRegion() {
assertEquals("Correct region", localRegion, localAccessStrategy.getRegion());
}
/**
* 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
*/
protected void putFromLoadTest(final boolean useMinimalAPI) throws Exception {
final Object KEY = generateNextKey();
final CountDownLatch writeLatch1 = new CountDownLatch(1);
final CountDownLatch writeLatch2 = new CountDownLatch(1);
final CountDownLatch completionLatch = new CountDownLatch(2);
Thread node1 = new Thread() {
@Override
public void run() {
try {
long txTimestamp = System.currentTimeMillis();
SessionImplementor session = mockedSession();
withTx(localEnvironment, session, () -> {
assertNull(localAccessStrategy.get(session, KEY, txTimestamp));
writeLatch1.await();
if (useMinimalAPI) {
localAccessStrategy.putFromLoad(session, KEY, VALUE1, txTimestamp, null, true);
} else {
localAccessStrategy.putFromLoad(session, KEY, VALUE1, txTimestamp, null);
}
doUpdate(localAccessStrategy, session, KEY, VALUE2);
return null;
});
} catch (Exception e) {
log.error("node1 caught exception", e);
node1Exception = e;
} catch (AssertionFailedError e) {
node1Failure = e;
} finally {
// Let node2 write
writeLatch2.countDown();
completionLatch.countDown();
}
}
};
Thread node2 = new PutFromLoadNode2(KEY, writeLatch1, writeLatch2, useMinimalAPI, completionLatch);
node1.setDaemon(true);
node2.setDaemon(true);
node1.start();
node2.start();
assertTrue("Threads completed", completionLatch.await(2, TimeUnit.SECONDS));
assertThreadsRanCleanly();
long txTimestamp = System.currentTimeMillis();
assertEquals( VALUE2, localAccessStrategy.get(mockedSession(), KEY, txTimestamp));
Object remoteValue = remoteAccessStrategy.get(mockedSession(), KEY, txTimestamp);
if (isUsingInvalidation()) {
// invalidation command invalidates pending put
assertNull(remoteValue);
}
else {
// The node1 update is replicated, preventing the node2 PFER
assertEquals( VALUE2, remoteValue);
}
}
@Test
public void testInsert() throws Exception {
final Object KEY = generateNextKey();
final CountDownLatch readLatch = new CountDownLatch(1);
final CountDownLatch commitLatch = new CountDownLatch(1);
final CountDownLatch completionLatch = new CountDownLatch(2);
Thread inserter = new Thread() {
@Override
public void run() {
try {
long txTimestamp = System.currentTimeMillis();
SessionImplementor session = mockedSession();
withTx(localEnvironment, session, () -> {
assertNull("Correct initial value", localAccessStrategy.get(session, KEY, txTimestamp));
doInsert(localAccessStrategy, session, KEY, VALUE1);
readLatch.countDown();
commitLatch.await();
return null;
});
} catch (Exception e) {
log.error("node1 caught exception", e);
node1Exception = e;
} catch (AssertionFailedError e) {
node1Failure = e;
} finally {
completionLatch.countDown();
}
}
};
Thread reader = new Thread() {
@Override
public void run() {
try {
long txTimestamp = System.currentTimeMillis();
SessionImplementor session = mockedSession();
withTx(localEnvironment, session, () -> {
readLatch.await();
assertNull("Correct initial value", localAccessStrategy.get(session, KEY, txTimestamp));
return null;
});
} catch (Exception e) {
log.error("node1 caught exception", e);
node1Exception = e;
} catch (AssertionFailedError e) {
node1Failure = e;
} finally {
commitLatch.countDown();
completionLatch.countDown();
}
}
};
inserter.setDaemon(true);
reader.setDaemon(true);
inserter.start();
reader.start();
assertTrue("Threads completed", completionLatch.await(1000, TimeUnit.SECONDS));
assertThreadsRanCleanly();
long txTimestamp = System.currentTimeMillis();
assertEquals("Correct node1 value", VALUE1, localAccessStrategy.get(mockedSession(), KEY, txTimestamp));
Object expected = isUsingInvalidation() ? null : VALUE1;
assertEquals("Correct node2 value", expected, remoteAccessStrategy.get(mockedSession(), KEY, txTimestamp));
}
protected void doInsert(EntityRegionAccessStrategy strategy, SessionImplementor session, Object key, String value) {
strategy.insert(session, key, value, null);
session.getTransactionCoordinator().getLocalSynchronizations().registerSynchronization(
new TestSynchronization.AfterInsert(strategy, session, key, value));
}
@Test
public void testUpdate() throws Exception {
final Object KEY = generateNextKey();
// Set up initial state
localAccessStrategy.putFromLoad(mockedSession(), KEY, VALUE1, System.currentTimeMillis(), new Integer(1));
remoteAccessStrategy.putFromLoad(mockedSession(), 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") {
@Override
public void run() {
try {
long txTimestamp = System.currentTimeMillis();
withTx(localEnvironment, mockedSession(), () -> {
log.debug("Transaction began, get initial value");
assertEquals("Correct initial value", VALUE1, localAccessStrategy.get(mockedSession(), KEY, txTimestamp));
log.debug("Now update value");
doUpdate(AbstractEntityRegionAccessStrategyTest.this.localAccessStrategy, mockedSession(), KEY, VALUE2);
log.debug("Notify the read latch");
readLatch.countDown();
log.debug("Await commit");
commitLatch.await();
return null;
});
} catch (Exception e) {
log.error("node1 caught exception", e);
node1Exception = e;
} catch (AssertionFailedError e) {
node1Failure = e;
} finally {
if (readLatch.getCount() > 0) {
readLatch.countDown();
}
log.debug("Completion latch countdown");
completionLatch.countDown();
}
}
};
Thread reader = new Thread("testUpdate-reader") {
@Override
public void run() {
try {
long txTimestamp = System.currentTimeMillis();
SessionImplementor session = mockedSession();
withTx(localEnvironment, session, () -> {
log.debug("Transaction began, await read latch");
readLatch.await();
log.debug("Read latch acquired, verify local access strategy");
// This won't block w/ mvc and will read the old value
Object expected = VALUE1;
assertEquals("Correct value", expected, localAccessStrategy.get(session, KEY, txTimestamp));
return null;
});
} catch (Exception e) {
log.error("node1 caught exception", e);
node1Exception = e;
} catch (AssertionFailedError e) {
node1Failure = e;
} finally {
commitLatch.countDown();
log.debug("Completion latch countdown");
completionLatch.countDown();
}
}
};
updater.setDaemon(true);
reader.setDaemon(true);
updater.start();
reader.start();
// Should complete promptly
assertTrue(completionLatch.await(2, TimeUnit.SECONDS));
assertThreadsRanCleanly();
long txTimestamp = System.currentTimeMillis();
assertEquals("Correct node1 value", VALUE2, localAccessStrategy.get(mockedSession(), KEY, txTimestamp));
Object expected = isUsingInvalidation() ? null : VALUE2;
assertEquals("Correct node2 value", expected, remoteAccessStrategy.get(mockedSession(), KEY, txTimestamp));
}
protected void doUpdate(EntityRegionAccessStrategy strategy, SessionImplementor session, Object key, Object value) throws javax.transaction.RollbackException, javax.transaction.SystemException {
SoftLock softLock = strategy.lockItem(session, key, null);
strategy.update(session, key, value, null, null);
session.getTransactionCoordinator().getLocalSynchronizations().registerSynchronization(
new TestSynchronization.AfterUpdate(strategy, session, key, value, softLock));
}
@Test
public void testContestedPutFromLoad() throws Exception {
final Object KEY = TestingKeyFactory.generateEntityCacheKey(KEY_BASE + testCount++);
localAccessStrategy.putFromLoad(mockedSession(), 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") {
@Override
public void run() {
try {
SessionImplementor session = mockedSession();
long txTimestamp = System.currentTimeMillis();
withTx(localEnvironment, session, () -> {
assertEquals("Correct initial value", VALUE1, localAccessStrategy.get(session, KEY, txTimestamp));
doUpdate(localAccessStrategy, session, KEY, VALUE2);
pferLatch.countDown();
commitLatch.await();
return null;
});
} catch (Exception e) {
log.error("node1 caught exception", e);
node1Exception = e;
} catch (AssertionFailedError e) {
node1Failure = e;
} finally {
completionLatch.countDown();
}
}
};
Thread putter = new Thread("Putter") {
@Override
public void run() {
try {
long txTimestamp = System.currentTimeMillis();
SessionImplementor session = mockedSession();
withTx(localEnvironment, session, () -> {
localAccessStrategy.putFromLoad(session, KEY, VALUE1, txTimestamp, new Integer(1));
return null;
});
} catch (Exception e) {
log.error("node1 caught exception", e);
node1Exception = e;
} catch (AssertionFailedError e) {
node1Failure = e;
} 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(null, KEY, txTimestamp));
}
}

View File

@ -1,629 +0,0 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
package org.hibernate.test.cache.infinispan.entity;
import java.util.Arrays;
import java.util.concurrent.Callable;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import junit.framework.AssertionFailedError;
import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
import org.hibernate.cache.infinispan.InfinispanRegionFactory;
import org.hibernate.cache.infinispan.entity.EntityRegionImpl;
import org.hibernate.cache.infinispan.util.Caches;
import org.hibernate.cache.internal.CacheDataDescriptionImpl;
import org.hibernate.cache.spi.CacheDataDescription;
import org.hibernate.cache.spi.access.AccessType;
import org.hibernate.cache.spi.access.EntityRegionAccessStrategy;
import org.hibernate.cache.spi.access.SoftLock;
import org.hibernate.engine.spi.SessionImplementor;
import org.hibernate.internal.util.compare.ComparableComparator;
import org.hibernate.test.cache.infinispan.AbstractNonFunctionalTestCase;
import org.hibernate.test.cache.infinispan.NodeEnvironment;
import org.hibernate.test.cache.infinispan.util.BatchModeTransactionCoordinator;
import org.hibernate.test.cache.infinispan.util.CacheTestUtil;
import org.hibernate.test.cache.infinispan.util.InfinispanTestingSetup;
import org.hibernate.test.cache.infinispan.util.TestingKeyFactory;
import org.infinispan.Cache;
import org.infinispan.test.TestingUtil;
import org.infinispan.transaction.tm.BatchModeTransactionManager;
import org.jboss.logging.Logger;
import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
/**
* Base class for tests of EntityRegionAccessStrategy impls.
*
* @author Galder Zamarreño
* @since 3.5
*/
public abstract class AbstractEntityRegionAccessStrategyTestCase extends AbstractNonFunctionalTestCase {
private static final Logger log = Logger.getLogger(AbstractEntityRegionAccessStrategyTestCase.class);
@Rule
public InfinispanTestingSetup infinispanTestIdentifier = new InfinispanTestingSetup();
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 NodeEnvironment localEnvironment;
protected EntityRegionImpl localEntityRegion;
protected EntityRegionAccessStrategy localAccessStrategy;
protected SessionImplementor localSession;
protected NodeEnvironment remoteEnvironment;
protected EntityRegionImpl remoteEntityRegion;
protected EntityRegionAccessStrategy remoteAccessStrategy;
protected SessionImplementor remoteSession;
protected boolean invalidation;
protected boolean synchronous;
protected Exception node1Exception;
protected Exception node2Exception;
protected AssertionFailedError node1Failure;
protected AssertionFailedError node2Failure;
@Before
public void prepareResources() throws Exception {
// to mimic exactly the old code results, both environments here are exactly the same...
StandardServiceRegistryBuilder ssrb = createStandardServiceRegistryBuilder( getConfigurationName() );
localEnvironment = new NodeEnvironment( ssrb );
localEnvironment.prepare();
localEntityRegion = localEnvironment.getEntityRegion(REGION_NAME, getCacheDataDescription());
localAccessStrategy = localEntityRegion.buildAccessStrategy(getAccessType());
localSession = mock(SessionImplementor.class);
when(localSession.getTransactionCoordinator()).thenReturn(new BatchModeTransactionCoordinator());
remoteSession = mock(SessionImplementor.class);
when(localSession.getTransactionCoordinator()).thenReturn(new BatchModeTransactionCoordinator());
invalidation = Caches.isInvalidationCache(localEntityRegion.getCache());
synchronous = Caches.isSynchronousCache(localEntityRegion.getCache());
// Sleep a bit to avoid concurrent FLUSH problem
avoidConcurrentFlush();
remoteEnvironment = new NodeEnvironment( ssrb );
remoteEnvironment.prepare();
remoteEntityRegion = remoteEnvironment.getEntityRegion(REGION_NAME, getCacheDataDescription());
remoteAccessStrategy = remoteEntityRegion.buildAccessStrategy(getAccessType());
waitForClusterToForm(localEntityRegion.getCache(),
remoteEntityRegion.getCache());
}
protected void waitForClusterToForm(Cache... caches) {
TestingUtil.blockUntilViewsReceived(10000, Arrays.asList(caches));
}
protected abstract String getConfigurationName();
protected static StandardServiceRegistryBuilder createStandardServiceRegistryBuilder(String configName) {
StandardServiceRegistryBuilder ssrb = CacheTestUtil.buildBaselineStandardServiceRegistryBuilder(
REGION_PREFIX,
InfinispanRegionFactory.class,
true,
false
);
ssrb.applySetting( InfinispanRegionFactory.ENTITY_CACHE_RESOURCE_PROP, configName );
return ssrb;
}
protected CacheDataDescription getCacheDataDescription() {
return new CacheDataDescriptionImpl(true, true, ComparableComparator.INSTANCE, null);
}
@After
public void releaseResources() throws Exception {
try {
if (localEnvironment != null) {
localEnvironment.release();
}
} finally {
if (remoteEnvironment != null) {
remoteEnvironment.release();
}
}
}
protected abstract AccessType getAccessType();
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);
}
}
@Test
public abstract void testCacheConfiguration();
@Test
public void testGetRegion() {
assertEquals("Correct region", localEntityRegion, localAccessStrategy.getRegion());
}
@Test
public void testPutFromLoad() throws Exception {
putFromLoadTest(false);
}
@Test
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 Object KEY = TestingKeyFactory.generateEntityCacheKey( 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() {
@Override
public void run() {
try {
long txTimestamp = System.currentTimeMillis();
BatchModeTransactionManager.getInstance().begin();
assertNull("node1 starts clean", localAccessStrategy.get(localSession, KEY, txTimestamp));
writeLatch1.await();
if (useMinimalAPI) {
localAccessStrategy.putFromLoad(localSession, KEY, VALUE1, txTimestamp, new Integer(1), true);
} else {
localAccessStrategy.putFromLoad(localSession, KEY, VALUE1, txTimestamp, new Integer(1));
}
SoftLock softLock = localAccessStrategy.lockItem(localSession, KEY, null);
localAccessStrategy.update(localSession, KEY, VALUE2, new Integer(2), new Integer(1));
localAccessStrategy.unlockItem(localSession, KEY, softLock);
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() {
@Override
public void run() {
try {
long txTimestamp = System.currentTimeMillis();
BatchModeTransactionManager.getInstance().begin();
assertNull("node1 starts clean", remoteAccessStrategy.get(remoteSession, KEY, txTimestamp));
// Let node1 write
writeLatch1.countDown();
// Wait for node1 to finish
writeLatch2.await();
if (useMinimalAPI) {
remoteAccessStrategy.putFromLoad(remoteSession, KEY, VALUE1, txTimestamp, new Integer(1), true);
} else {
remoteAccessStrategy.putFromLoad(remoteSession, 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(2000, TimeUnit.SECONDS));
assertThreadsRanCleanly();
long txTimestamp = System.currentTimeMillis();
assertEquals("Correct node1 value", VALUE2, localAccessStrategy.get(localSession, KEY, txTimestamp));
if (isUsingInvalidation()) {
// invalidation command invalidates pending put
assertEquals("Expected node2 value", null, remoteAccessStrategy.get(remoteSession, KEY, txTimestamp));
} else {
// The node1 update is replicated, preventing the node2 PFER
assertEquals("Correct node2 value", VALUE2, remoteAccessStrategy.get(remoteSession, KEY, txTimestamp));
}
}
@Test
public void testInsert() throws Exception {
final Object KEY = TestingKeyFactory.generateEntityCacheKey( 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() {
@Override
public void run() {
try {
long txTimestamp = System.currentTimeMillis();
BatchModeTransactionManager.getInstance().begin();
assertNull("Correct initial value", localAccessStrategy.get(localSession, KEY, txTimestamp));
localAccessStrategy.insert(localSession, KEY, VALUE1, new Integer(1));
localAccessStrategy.afterInsert(localSession, KEY, VALUE1, null);
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() {
@Override
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(
localSession, 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();
assertTrue("Threads completed", completionLatch.await(1, TimeUnit.SECONDS));
assertThreadsRanCleanly();
long txTimestamp = System.currentTimeMillis();
assertEquals("Correct node1 value", VALUE1, localAccessStrategy.get(localSession, KEY, txTimestamp));
Object expected = isUsingInvalidation() ? null : VALUE1;
assertEquals("Correct node2 value", expected, remoteAccessStrategy.get(remoteSession, KEY, txTimestamp));
}
@Test
public void testUpdate() throws Exception {
final Object KEY = TestingKeyFactory.generateEntityCacheKey( KEY_BASE + testCount++ );
// Set up initial state
localAccessStrategy.putFromLoad(localSession, KEY, VALUE1, System.currentTimeMillis(), new Integer(1));
remoteAccessStrategy.putFromLoad(remoteSession, 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") {
@Override
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(localSession, KEY, txTimestamp));
log.debug("Now update value");
SoftLock softLock = localAccessStrategy.lockItem(localSession, KEY, null);
localAccessStrategy.update(localSession, KEY, VALUE2, new Integer(2), new Integer(1));
localAccessStrategy.unlockItem(localSession, KEY, softLock);
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") {
@Override
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 won't block w/ mvc and will read the old value
Object expected = VALUE1;
assertEquals("Correct value", expected, localAccessStrategy.get(localSession, 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();
// Should complete promptly
assertTrue(completionLatch.await(2, TimeUnit.SECONDS));
assertThreadsRanCleanly();
long txTimestamp = System.currentTimeMillis();
assertEquals("Correct node1 value", VALUE2, localAccessStrategy.get(localSession, KEY, txTimestamp));
Object expected = isUsingInvalidation() ? null : VALUE2;
assertEquals("Correct node2 value", expected, remoteAccessStrategy.get(remoteSession, KEY, txTimestamp));
}
@Test
public void testRemove() throws Exception {
evictOrRemoveTest(false);
}
@Test
public void testRemoveAll() throws Exception {
evictOrRemoveAllTest(false);
}
@Test
public void testEvict() throws Exception {
evictOrRemoveTest(true);
}
@Test
public void testEvictAll() throws Exception {
evictOrRemoveAllTest(true);
}
private void evictOrRemoveTest(final boolean evict) throws Exception {
final Object KEY = TestingKeyFactory.generateEntityCacheKey( KEY_BASE + testCount++ );
assertEquals(0, localEntityRegion.getCache().size());
assertEquals(0, remoteEntityRegion.getCache().size());
assertNull("local is clean", localAccessStrategy.get(localSession, KEY, System.currentTimeMillis()));
assertNull("remote is clean", remoteAccessStrategy.get(remoteSession, KEY, System.currentTimeMillis()));
localAccessStrategy.putFromLoad(localSession, KEY, VALUE1, System.currentTimeMillis(), new Integer(1));
assertEquals(VALUE1, localAccessStrategy.get(localSession, KEY, System.currentTimeMillis()));
remoteAccessStrategy.putFromLoad(remoteSession, KEY, VALUE1, System.currentTimeMillis(), new Integer(1));
assertEquals(VALUE1, remoteAccessStrategy.get(remoteSession, KEY, System.currentTimeMillis()));
Caches.withinTx(localEntityRegion.getTransactionManager(), new Callable<Void>() {
@Override
public Void call() throws Exception {
if (evict) {
localAccessStrategy.evict(KEY);
}
else {
SoftLock softLock = localAccessStrategy.lockItem(localSession, KEY, null);
localAccessStrategy.remove(localSession, KEY);
localAccessStrategy.unlockItem(localSession, KEY, softLock);
}
return null;
}
});
assertEquals(null, localAccessStrategy.get(localSession, KEY, System.currentTimeMillis()));
assertEquals(0, localEntityRegion.getCache().size());
assertEquals(null, remoteAccessStrategy.get(remoteSession, KEY, System.currentTimeMillis()));
assertEquals(0, remoteEntityRegion.getCache().size());
}
private void evictOrRemoveAllTest(final boolean evict) throws Exception {
final Object KEY = TestingKeyFactory.generateEntityCacheKey( KEY_BASE + testCount++ );
assertEquals(0, localEntityRegion.getCache().size());
assertEquals(0, remoteEntityRegion.getCache().size());
assertNull("local is clean", localAccessStrategy.get(localSession, KEY, System.currentTimeMillis()));
assertNull("remote is clean", remoteAccessStrategy.get(remoteSession, KEY, System.currentTimeMillis()));
localAccessStrategy.putFromLoad(localSession, KEY, VALUE1, System.currentTimeMillis(), new Integer(1));
assertEquals(VALUE1, localAccessStrategy.get(localSession, KEY, System.currentTimeMillis()));
// Wait for async propagation
sleep(250);
remoteAccessStrategy.putFromLoad(remoteSession, KEY, VALUE1, System.currentTimeMillis(), new Integer(1));
assertEquals(VALUE1, remoteAccessStrategy.get(remoteSession, KEY, System.currentTimeMillis()));
// Wait for async propagation
sleep(250);
Caches.withinTx(localEntityRegion.getTransactionManager(), new Callable<Void>() {
@Override
public Void call() throws Exception {
if (evict) {
log.debug("Call evict all locally");
localAccessStrategy.evictAll();
} else {
SoftLock softLock = localAccessStrategy.lockRegion();
localAccessStrategy.removeAll();
localAccessStrategy.unlockRegion(softLock);
}
return null;
}
});
// This should re-establish the region root node in the optimistic case
assertNull(localAccessStrategy.get(localSession, KEY, System.currentTimeMillis()));
assertEquals(0, localEntityRegion.getCache().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
assertNull(remoteAccessStrategy.get(remoteSession, KEY, System.currentTimeMillis()));
assertEquals(0, remoteEntityRegion.getCache().size());
// Wait for async propagation of EndInvalidationCommand before executing naked put
sleep(250);
// Test whether the get above messes up the optimistic version
remoteAccessStrategy.putFromLoad(remoteSession, KEY, VALUE1, System.currentTimeMillis(), new Integer(1));
assertEquals(VALUE1, remoteAccessStrategy.get(remoteSession, KEY, System.currentTimeMillis()));
assertEquals(1, remoteEntityRegion.getCache().size());
// Wait for async propagation
sleep(250);
assertEquals(
"local is correct", (isUsingInvalidation() ? null : VALUE1), localAccessStrategy
.get(localSession, KEY, System.currentTimeMillis())
);
assertEquals(
"remote is correct", VALUE1, remoteAccessStrategy.get(
remoteSession, KEY, System
.currentTimeMillis()
)
);
}
protected void rollback() {
try {
BatchModeTransactionManager.getInstance().rollback();
} catch (Exception e) {
log.error(e.getMessage(), e);
}
}
}

View File

@ -1,73 +0,0 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
package org.hibernate.test.cache.infinispan.entity;
import org.hibernate.cache.spi.access.AccessType;
import org.hibernate.cache.spi.access.SoftLock;
import org.hibernate.test.cache.infinispan.util.TestingKeyFactory;
import org.infinispan.transaction.tm.BatchModeTransactionManager;
import org.junit.Test;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
/**
* Base class for tests of TRANSACTIONAL access.
*
* @author Galder Zamarreño
* @since 3.5
*/
public abstract class AbstractReadOnlyAccessTestCase extends AbstractEntityRegionAccessStrategyTestCase {
@Override
protected AccessType getAccessType() {
return AccessType.READ_ONLY;
}
@Test
@Override
public void testPutFromLoad() throws Exception {
putFromLoadTest(false);
}
@Test
@Override
public void testPutFromLoadMinimal() throws Exception {
putFromLoadTest(true);
}
private void putFromLoadTest(boolean minimal) throws Exception {
final Object KEY = TestingKeyFactory.generateEntityCacheKey( KEY_BASE + testCount++ );
long txTimestamp = System.currentTimeMillis();
BatchModeTransactionManager.getInstance().begin();
assertNull(localAccessStrategy.get(localSession, KEY, System.currentTimeMillis()));
if (minimal)
localAccessStrategy.putFromLoad(localSession, KEY, VALUE1, txTimestamp, 1, true);
else
localAccessStrategy.putFromLoad(localSession, KEY, VALUE1, txTimestamp, 1);
sleep(250);
Object expected = isUsingInvalidation() ? null : VALUE1;
assertEquals(expected, remoteAccessStrategy.get(remoteSession, KEY, System.currentTimeMillis()));
BatchModeTransactionManager.getInstance().commit();
assertEquals(VALUE1, localAccessStrategy.get(localSession, KEY, System.currentTimeMillis()));
assertEquals(expected, remoteAccessStrategy.get(remoteSession, KEY, System.currentTimeMillis()));
}
@Test(expected = UnsupportedOperationException.class)
@Override
public void testUpdate() throws Exception {
final Object KEY = TestingKeyFactory.generateEntityCacheKey( KEY_BASE + testCount++ );
SoftLock softLock = localAccessStrategy.lockItem(localSession, KEY, null);
localAccessStrategy.update(localSession, KEY, VALUE2, 2, 1);
localAccessStrategy.unlockItem(localSession, KEY, softLock);
}
}

View File

@ -1,119 +0,0 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
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.spi.access.AccessType;
import org.hibernate.cache.spi.access.SoftLock;
import org.hibernate.test.cache.infinispan.util.TestingKeyFactory;
import org.infinispan.transaction.tm.BatchModeTransactionManager;
import org.jboss.logging.Logger;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
/**
* Base class for tests of TRANSACTIONAL access.
*
* @author Galder Zamarreño
* @since 3.5
*/
public abstract class AbstractTransactionalAccessTestCase extends AbstractEntityRegionAccessStrategyTestCase {
private static final Logger log = Logger.getLogger( AbstractTransactionalAccessTestCase.class );
@Override
protected AccessType getAccessType() {
return AccessType.TRANSACTIONAL;
}
public void testContestedPutFromLoad() throws Exception {
final Object KEY = TestingKeyFactory.generateEntityCacheKey( KEY_BASE + testCount++ );
localAccessStrategy.putFromLoad(localSession, 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") {
@Override
public void run() {
try {
long txTimestamp = System.currentTimeMillis();
BatchModeTransactionManager.getInstance().begin();
assertEquals("Correct initial value", VALUE1, localAccessStrategy.get(localSession, KEY, txTimestamp));
SoftLock softLock = localAccessStrategy.lockItem(localSession, KEY, null);
localAccessStrategy.update(localSession, KEY, VALUE2, new Integer(2), new Integer(1));
localAccessStrategy.unlockItem(localSession, KEY, softLock);
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") {
@Override
public void run() {
try {
long txTimestamp = System.currentTimeMillis();
BatchModeTransactionManager.getInstance().begin();
localAccessStrategy.putFromLoad(null, 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(null, KEY, txTimestamp));
}
}

View File

@ -0,0 +1,88 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
package org.hibernate.test.cache.infinispan.entity;
import org.hibernate.cache.spi.RegionFactory;
import org.hibernate.cache.spi.access.AccessType;
import org.hibernate.cache.spi.access.EntityRegionAccessStrategy;
import org.hibernate.test.cache.infinispan.AbstractExtraAPITest;
import org.hibernate.test.cache.infinispan.util.TestInfinispanRegionFactory;
import org.junit.Test;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNull;
import static org.mockito.Mockito.mock;
/**
* 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 EntityRegionExtraAPITest extends AbstractExtraAPITest<EntityRegionAccessStrategy> {
public static final String VALUE1 = "VALUE1";
public static final String VALUE2 = "VALUE2";
@Override
protected EntityRegionAccessStrategy getAccessStrategy() {
return environment.getEntityRegion( REGION_NAME, CACHE_DATA_DESCRIPTION).buildAccessStrategy( getAccessType() );
}
protected AccessType getAccessType() {
return AccessType.TRANSACTIONAL;
}
@Test
@SuppressWarnings( {"UnnecessaryBoxing"})
public void testAfterInsert() {
assertFalse("afterInsert always returns false", accessStrategy.afterInsert(SESSION, KEY, VALUE1, Integer.valueOf( 1 )));
}
@Test
@SuppressWarnings( {"UnnecessaryBoxing"})
public void testAfterUpdate() {
assertFalse("afterInsert always returns false", accessStrategy.afterUpdate(
SESSION, KEY, VALUE2, Integer.valueOf( 1 ), Integer.valueOf( 2 ), new MockSoftLock()));
}
public static class Transactional extends EntityRegionExtraAPITest {
@Override
protected AccessType getAccessType() {
return AccessType.TRANSACTIONAL;
}
@Override
protected Class<? extends RegionFactory> getRegionFactoryClass() {
return TestInfinispanRegionFactory.Transactional.class;
}
}
public static class ReadWrite extends EntityRegionExtraAPITest {
@Override
protected AccessType getAccessType() {
return AccessType.READ_WRITE;
}
}
public static class ReadOnly extends EntityRegionExtraAPITest {
@Override
protected AccessType getAccessType() {
return AccessType.READ_ONLY;
}
@Test(expected = UnsupportedOperationException.class)
@Override
public void testAfterUpdate() {
accessStrategy.afterUpdate(null, KEY, VALUE2, 1, 2, new MockSoftLock());
}
}
}

View File

@ -0,0 +1,66 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
package org.hibernate.test.cache.infinispan.entity;
import java.util.Properties;
import org.hibernate.cache.CacheException;
import org.hibernate.cache.infinispan.InfinispanRegionFactory;
import org.hibernate.cache.spi.CacheDataDescription;
import org.hibernate.cache.spi.EntityRegion;
import org.hibernate.cache.spi.Region;
import org.hibernate.cache.spi.RegionFactory;
import org.hibernate.cache.spi.access.AccessType;
import org.hibernate.test.cache.infinispan.AbstractEntityCollectionRegionTest;
import org.infinispan.AdvancedCache;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.fail;
/**
* Tests of EntityRegionImpl.
*
* @author Galder Zamarreño
* @since 3.5
*/
public class EntityRegionImplTest extends AbstractEntityCollectionRegionTest {
@Override
protected void supportedAccessTypeTest(RegionFactory regionFactory, Properties properties) {
EntityRegion region = regionFactory.buildEntityRegion("test", properties, MUTABLE_NON_VERSIONED);
assertNotNull(region.buildAccessStrategy(AccessType.READ_ONLY));
assertNotNull(region.buildAccessStrategy(AccessType.READ_WRITE));
assertNotNull(region.buildAccessStrategy(AccessType.TRANSACTIONAL));
try {
region.buildAccessStrategy(AccessType.NONSTRICT_READ_WRITE);
fail("Incorrectly got NONSTRICT_READ_WRITE");
} catch (CacheException good) {
}
}
@Override
protected void putInRegion(Region region, Object key, Object value) {
((EntityRegion) region).buildAccessStrategy(AccessType.TRANSACTIONAL).insert(null, key, value, 1);
}
@Override
protected void removeFromRegion(Region region, Object key) {
((EntityRegion) region).buildAccessStrategy(AccessType.TRANSACTIONAL).remove(null, key);
}
@Override
protected Region createRegion(InfinispanRegionFactory regionFactory, String regionName, Properties properties, CacheDataDescription cdd) {
return regionFactory.buildEntityRegion(regionName, properties, cdd);
}
@Override
protected AdvancedCache getInfinispanCache(InfinispanRegionFactory regionFactory) {
return regionFactory.getCacheManager().getCache(
InfinispanRegionFactory.DEF_ENTITY_RESOURCE).getAdvancedCache();
}
}

View File

@ -1,71 +0,0 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
package org.hibernate.test.cache.infinispan.entity;
import java.util.Properties;
import org.hibernate.cache.CacheException;
import org.hibernate.cache.infinispan.InfinispanRegionFactory;
import org.hibernate.cache.spi.CacheDataDescription;
import org.hibernate.cache.spi.EntityRegion;
import org.hibernate.cache.spi.Region;
import org.hibernate.cache.spi.RegionFactory;
import org.hibernate.cache.spi.access.AccessType;
import org.hibernate.test.cache.infinispan.AbstractEntityCollectionRegionTestCase;
import org.infinispan.AdvancedCache;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.fail;
/**
* Tests of EntityRegionImpl.
*
* @author Galder Zamarreño
* @since 3.5
*/
public class EntityRegionImplTestCase extends AbstractEntityCollectionRegionTestCase {
@Override
protected void supportedAccessTypeTest(RegionFactory regionFactory, Properties properties) {
EntityRegion region = regionFactory.buildEntityRegion("test", properties, MUTABLE_NON_VERSIONED);
assertNull("Got TRANSACTIONAL",
region.buildAccessStrategy(AccessType.TRANSACTIONAL).lockRegion());
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(null, key, value, 1);
}
@Override
protected void removeFromRegion(Region region, Object key) {
((EntityRegion) region).buildAccessStrategy(AccessType.TRANSACTIONAL).remove(null, key);
}
@Override
protected Region createRegion(InfinispanRegionFactory regionFactory, String regionName, Properties properties, CacheDataDescription cdd) {
return regionFactory.buildEntityRegion(regionName, properties, cdd);
}
@Override
protected AdvancedCache getInfinispanCache(InfinispanRegionFactory regionFactory) {
return regionFactory.getCacheManager().getCache(
InfinispanRegionFactory.DEF_ENTITY_RESOURCE).getAdvancedCache();
}
}

View File

@ -0,0 +1,77 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
package org.hibernate.test.cache.infinispan.entity;
import org.hibernate.cache.spi.access.AccessType;
import org.hibernate.cache.spi.access.SoftLock;
import org.hibernate.engine.spi.SessionImplementor;
import org.hibernate.test.cache.infinispan.util.TestingKeyFactory;
import org.infinispan.transaction.tm.BatchModeTransactionManager;
import org.junit.Ignore;
import org.junit.Test;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
/**
* Base class for tests of TRANSACTIONAL access.
*
* @author Galder Zamarreño
* @since 3.5
*/
public abstract class EntityRegionReadOnlyAccessTest extends AbstractEntityRegionAccessStrategyTest {
@Override
protected AccessType getAccessType() {
return AccessType.READ_ONLY;
}
protected void putFromLoadTest(boolean minimal) throws Exception {
final Object KEY = TestingKeyFactory.generateEntityCacheKey( KEY_BASE + testCount++ );
Object expected = isUsingInvalidation() ? null : VALUE1;
long txTimestamp = System.currentTimeMillis();
SessionImplementor session = mockedSession();
withTx(localEnvironment, session, () -> {
assertNull(localAccessStrategy.get(session, KEY, System.currentTimeMillis()));
if (minimal)
localAccessStrategy.putFromLoad(session, KEY, VALUE1, txTimestamp, 1, true);
else
localAccessStrategy.putFromLoad(session, KEY, VALUE1, txTimestamp, 1);
return null;
});
assertEquals(VALUE1, localAccessStrategy.get(session, KEY, System.currentTimeMillis()));
assertEquals(expected, remoteAccessStrategy.get(mockedSession(), KEY, System.currentTimeMillis()));
}
@Test(expected = UnsupportedOperationException.class)
@Override
public void testUpdate() throws Exception {
final Object KEY = TestingKeyFactory.generateEntityCacheKey( KEY_BASE + testCount++ );
SessionImplementor session = mockedSession();
SoftLock softLock = localAccessStrategy.lockItem(session, KEY, null);
localAccessStrategy.update(session, KEY, VALUE2, 2, 1);
localAccessStrategy.unlockItem(session, KEY, softLock);
}
@Ignore
@Override
public void testContestedPutFromLoad() throws Exception {
}
public static class Invalidation extends EntityRegionReadOnlyAccessTest {
@Test
@Override
public void testCacheConfiguration() {
assertTrue("Using Invalidation", isUsingInvalidation());
}
}
}

View File

@ -0,0 +1,57 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
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.spi.RegionFactory;
import org.hibernate.cache.spi.access.AccessType;
import org.hibernate.cache.spi.access.SoftLock;
import org.hibernate.test.cache.infinispan.util.TestInfinispanRegionFactory;
import org.hibernate.test.cache.infinispan.util.TestingKeyFactory;
import org.infinispan.transaction.tm.BatchModeTransactionManager;
import org.jboss.logging.Logger;
import org.junit.Test;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
/**
* Base class for tests of TRANSACTIONAL access.
*
* @author Galder Zamarreño
* @since 3.5
*/
public abstract class EntityRegionTransactionalAccessTest extends AbstractEntityRegionAccessStrategyTest {
private static final Logger log = Logger.getLogger( EntityRegionTransactionalAccessTest.class );
@Override
protected AccessType getAccessType() {
return AccessType.TRANSACTIONAL;
}
@Override
protected Class<? extends RegionFactory> getRegionFactoryClass() {
return TestInfinispanRegionFactory.Transactional.class;
}
/**
* @author Galder Zamarreño
*/
public static class Invalidation extends EntityRegionTransactionalAccessTest {
@Test
@Override
public void testCacheConfiguration() {
assertTrue(isTransactional());
assertTrue("Using Invalidation", isUsingInvalidation());
assertTrue("Synchronous mode", isSynchronous());
}
}
}

View File

@ -1,31 +0,0 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
package org.hibernate.test.cache.infinispan.entity;
import org.junit.Test;
import static org.junit.Assert.assertTrue;
/**
* InvalidatedTransactionalTestCase.
*
* @author Galder Zamarreño
* @since 3.5
*/
public class InvalidatedTransactionalTestCase extends AbstractTransactionalAccessTestCase {
@Test
@Override
public void testCacheConfiguration() {
assertTrue("Using Invalidation", isUsingInvalidation());
assertTrue("Synchronous mode", isSynchronous());
}
@Override
protected String getConfigurationName() {
return "entity";
}
}

View File

@ -1,33 +0,0 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
package org.hibernate.test.cache.infinispan.entity;
import org.hibernate.cache.spi.access.AccessType;
import org.junit.Test;
/**
* 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 {
@Override
protected AccessType getAccessType() {
return AccessType.READ_ONLY;
}
@Test(expected = UnsupportedOperationException.class)
@Override
public void testAfterUpdate() {
getEntityAccessStrategy().afterUpdate(null, KEY, VALUE2, 1, 2, new MockSoftLock());
}
}

View File

@ -1,30 +0,0 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
package org.hibernate.test.cache.infinispan.entity;
import org.junit.Test;
import static org.junit.Assert.assertTrue;
/**
* Tests READ_ONLY access when pessimistic locking and invalidation are used.
*
* @author Galder Zamarreño
* @since 3.5
*/
public class ReadOnlyTestCase extends AbstractReadOnlyAccessTestCase {
@Override
protected String getConfigurationName() {
return "entity";
}
@Test
@Override
public void testCacheConfiguration() {
assertTrue("Using Invalidation", isUsingInvalidation());
}
}

View File

@ -1,149 +0,0 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
package org.hibernate.test.cache.infinispan.entity;
import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
import org.hibernate.cache.infinispan.InfinispanRegionFactory;
import org.hibernate.cache.internal.CacheDataDescriptionImpl;
import org.hibernate.cache.spi.access.AccessType;
import org.hibernate.cache.spi.access.EntityRegionAccessStrategy;
import org.hibernate.cache.spi.access.SoftLock;
import org.hibernate.engine.spi.SessionImplementor;
import org.hibernate.internal.util.compare.ComparableComparator;
import org.hibernate.test.cache.infinispan.AbstractNonFunctionalTestCase;
import org.hibernate.test.cache.infinispan.NodeEnvironment;
import org.hibernate.test.cache.infinispan.util.CacheTestUtil;
import org.hibernate.test.cache.infinispan.util.InfinispanTestingSetup;
import org.hibernate.test.cache.infinispan.util.TestingKeyFactory;
import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNull;
import static org.mockito.Mockito.mock;
/**
* 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 {
@Rule
public InfinispanTestingSetup infinispanTestIdentifier = new InfinispanTestingSetup();
public static final String REGION_NAME = "test/com.foo.test";
public static final Object KEY = TestingKeyFactory.generateEntityCacheKey( "KEY" );
public static final String VALUE1 = "VALUE1";
public static final String VALUE2 = "VALUE2";
protected static final CacheDataDescriptionImpl CACHE_DATA_DESCRIPTION
= new CacheDataDescriptionImpl(true, false, ComparableComparator.INSTANCE, null);
private static final SessionImplementor SESSION = mock(SessionImplementor.class);
private NodeEnvironment environment;
private EntityRegionAccessStrategy accessStrategy;
@Before
public final void prepareLocalAccessStrategy() throws Exception {
environment = new NodeEnvironment( createStandardServiceRegistryBuilder() );
environment.prepare();
// Sleep a bit to avoid concurrent FLUSH problem
avoidConcurrentFlush();
accessStrategy = environment.getEntityRegion( REGION_NAME, CACHE_DATA_DESCRIPTION).buildAccessStrategy( getAccessType() );
}
protected StandardServiceRegistryBuilder createStandardServiceRegistryBuilder() {
StandardServiceRegistryBuilder ssrb = CacheTestUtil.buildBaselineStandardServiceRegistryBuilder(
REGION_PREFIX,
InfinispanRegionFactory.class,
true,
false
);
ssrb.applySetting( InfinispanRegionFactory.ENTITY_CACHE_RESOURCE_PROP, getCacheConfigName() );
return ssrb;
}
@After
public final void releaseLocalAccessStrategy() throws Exception {
if ( environment != null ) {
environment.release();
}
}
protected final EntityRegionAccessStrategy getEntityAccessStrategy() {
return accessStrategy;
}
protected String getCacheConfigName() {
return "entity";
}
protected AccessType getAccessType() {
return AccessType.TRANSACTIONAL;
}
@Test
@SuppressWarnings( {"UnnecessaryBoxing"})
public void testLockItem() {
assertNull( getEntityAccessStrategy().lockItem(SESSION, KEY, Integer.valueOf( 1 ) ) );
}
@Test
public void testLockRegion() {
assertNull( getEntityAccessStrategy().lockRegion() );
}
@Test
public void testUnlockItem() {
getEntityAccessStrategy().unlockItem(SESSION, KEY, new MockSoftLock() );
}
@Test
public void testUnlockRegion() {
getEntityAccessStrategy().unlockItem(SESSION, KEY, new MockSoftLock() );
}
@Test
@SuppressWarnings( {"UnnecessaryBoxing"})
public void testAfterInsert() {
assertFalse(
"afterInsert always returns false",
getEntityAccessStrategy().afterInsert(SESSION,
KEY,
VALUE1,
Integer.valueOf( 1 )
)
);
}
@Test
@SuppressWarnings( {"UnnecessaryBoxing"})
public void testAfterUpdate() {
assertFalse(
"afterInsert always returns false",
getEntityAccessStrategy().afterUpdate(SESSION,
KEY,
VALUE2,
Integer.valueOf( 1 ),
Integer.valueOf( 2 ),
new MockSoftLock()
)
);
}
public static class MockSoftLock implements SoftLock {
}
}

View File

@ -0,0 +1,122 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
package org.hibernate.test.cache.infinispan.functional;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import org.hibernate.Session;
import org.hibernate.cache.spi.RegionFactory;
import org.hibernate.cache.spi.access.AccessType;
import org.hibernate.cfg.AvailableSettings;
import org.hibernate.cfg.Environment;
import org.hibernate.engine.jdbc.connections.spi.ConnectionProvider;
import org.hibernate.engine.transaction.jta.platform.spi.JtaPlatform;
import org.hibernate.resource.transaction.TransactionCoordinatorBuilder;
import org.hibernate.resource.transaction.backend.jdbc.internal.JdbcResourceLocalTransactionCoordinatorBuilderImpl;
import org.hibernate.resource.transaction.backend.jta.internal.JtaTransactionCoordinatorBuilderImpl;
import org.hibernate.test.cache.infinispan.tm.XaConnectionProvider;
import org.hibernate.test.cache.infinispan.util.InfinispanTestingSetup;
import org.hibernate.test.cache.infinispan.util.TestInfinispanRegionFactory;
import org.hibernate.test.cache.infinispan.util.TxUtil;
import org.hibernate.testing.BeforeClassOnce;
import org.hibernate.testing.junit4.BaseNonConfigCoreFunctionalTestCase;
import org.hibernate.test.cache.infinispan.tm.JtaPlatformImpl;
import org.hibernate.testing.junit4.CustomParameterized;
import org.infinispan.util.logging.Log;
import org.infinispan.util.logging.LogFactory;
import org.junit.ClassRule;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
/**
* @author Galder Zamarreño
* @since 3.5
*/
@RunWith(CustomParameterized.class)
public abstract class AbstractFunctionalTest extends BaseNonConfigCoreFunctionalTestCase {
private static final Log log = LogFactory.getLog( AbstractFunctionalTest.class );
protected static final Object[] TRANSACTIONAL = new Object[]{"transactional", JtaPlatformImpl.class, JtaTransactionCoordinatorBuilderImpl.class, XaConnectionProvider.class, AccessType.TRANSACTIONAL, TestInfinispanRegionFactory.Transactional.class};
protected static final Object[] READ_WRITE = new Object[]{"read-write", null, JdbcResourceLocalTransactionCoordinatorBuilderImpl.class, null, AccessType.READ_WRITE, TestInfinispanRegionFactory.class};
protected static final Object[] READ_ONLY = new Object[]{"read-only", null, JdbcResourceLocalTransactionCoordinatorBuilderImpl.class, null, AccessType.READ_ONLY, TestInfinispanRegionFactory.class};
@ClassRule
public static final InfinispanTestingSetup infinispanTestIdentifier = new InfinispanTestingSetup();
@Parameterized.Parameter(value = 0)
public String mode;
@Parameterized.Parameter(value = 1)
public Class<? extends JtaPlatform> jtaPlatformClass;
@Parameterized.Parameter(value = 2)
public Class<? extends TransactionCoordinatorBuilder> transactionCoordinatorBuilderClass;
@Parameterized.Parameter(value = 3)
public Class<? extends ConnectionProvider> connectionProviderClass;
@Parameterized.Parameter(value = 4)
public AccessType accessType;
@Parameterized.Parameter(value = 5)
public Class<? extends RegionFactory> regionFactoryClass;
protected boolean useJta;
@Parameterized.Parameters(name = "{0}")
public abstract List<Object[]> getParameters();
@BeforeClassOnce
public void setUseJta() {
useJta = jtaPlatformClass != null;
}
@Override
public String[] getMappings() {
return new String[] {
"cache/infinispan/functional/entities/Item.hbm.xml",
"cache/infinispan/functional/entities/Customer.hbm.xml",
"cache/infinispan/functional/entities/Contact.hbm.xml"
};
}
@Override
public String getCacheConcurrencyStrategy() {
return accessType.getExternalName();
}
protected boolean getUseQueryCache() {
return true;
}
@Override
@SuppressWarnings("unchecked")
protected void addSettings(Map settings) {
super.addSettings( settings );
settings.put( Environment.USE_SECOND_LEVEL_CACHE, "true" );
settings.put( Environment.GENERATE_STATISTICS, "true" );
settings.put( Environment.USE_QUERY_CACHE, String.valueOf( getUseQueryCache() ) );
settings.put( Environment.CACHE_REGION_FACTORY, regionFactoryClass.getName() );
if ( jtaPlatformClass != null ) {
settings.put( AvailableSettings.JTA_PLATFORM, jtaPlatformClass.getName() );
}
settings.put( Environment.TRANSACTION_COORDINATOR_STRATEGY, transactionCoordinatorBuilderClass.getName() );
if ( connectionProviderClass != null) {
settings.put(Environment.CONNECTION_PROVIDER, connectionProviderClass.getName());
}
}
protected void markRollbackOnly(Session session) {
TxUtil.markRollbackOnly(useJta, session);
}
}

View File

@ -1,289 +0,0 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
package org.hibernate.test.cache.infinispan.functional;
import java.util.Map;
import java.util.concurrent.Callable;
import java.util.concurrent.Phaser;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import org.hibernate.PessimisticLockException;
import org.hibernate.Session;
import org.hibernate.cache.infinispan.InfinispanRegionFactory;
import org.hibernate.cache.infinispan.entity.EntityRegionImpl;
import org.hibernate.cache.spi.Region;
import org.hibernate.stat.SecondLevelCacheStatistics;
import org.hibernate.stat.Statistics;
import org.hibernate.testing.TestForIssue;
import org.infinispan.AdvancedCache;
import org.infinispan.commands.read.GetKeyValueCommand;
import org.infinispan.context.InvocationContext;
import org.infinispan.interceptors.base.BaseCustomInterceptor;
import org.infinispan.util.logging.Log;
import org.infinispan.util.logging.LogFactory;
import org.junit.Test;
import static org.infinispan.test.TestingUtil.withTx;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
/**
* Parent tests for both transactional and
* read-only tests are defined in this class.
*
* @author Galder Zamarreño
* @since 4.1
*/
public abstract class AbstractFunctionalTestCase extends SingleNodeTestCase {
static final Log log = LogFactory.getLog(AbstractFunctionalTestCase.class);
@Test
public void testEmptySecondLevelCacheEntry() throws Exception {
sessionFactory().getCache().evictCollectionRegion( Item.class.getName() + ".items" );
Statistics stats = sessionFactory().getStatistics();
stats.clear();
SecondLevelCacheStatistics statistics = stats.getSecondLevelCacheStatistics( Item.class.getName() + ".items" );
Map cacheEntries = statistics.getEntries();
assertEquals( 0, cacheEntries.size() );
}
@Test
public void testInsertDeleteEntity() throws Exception {
final Statistics stats = sessionFactory().getStatistics();
stats.clear();
final Item item = new Item( "chris", "Chris's Item" );
withTx(tm, new Callable<Void>() {
@Override
public Void call() throws Exception {
Session s = openSession();
s.getTransaction().begin();
s.persist(item);
s.getTransaction().commit();
s.close();
return null;
}
});
log.info("Entry persisted, let's load and delete it.");
withTx(tm, new Callable<Void>() {
@Override
public Void call() throws Exception {
Session s = openSession();
s.getTransaction().begin();
Item found = (Item) s.load(Item.class, item.getId());
log.info(stats.toString());
assertEquals(item.getDescription(), found.getDescription());
assertEquals(0, stats.getSecondLevelCacheMissCount());
assertEquals(1, stats.getSecondLevelCacheHitCount());
s.delete(found);
s.getTransaction().commit();
s.close();
return null;
}
});
}
@Test
public void testInsertClearCacheDeleteEntity() throws Exception {
final Statistics stats = sessionFactory().getStatistics();
stats.clear();
final Item item = new Item( "chris", "Chris's Item" );
withTx(tm, new Callable<Void>() {
@Override
public Void call() throws Exception {
Session s = openSession();
s.getTransaction().begin();
s.persist(item);
s.getTransaction().commit();
assertEquals(0, stats.getSecondLevelCacheMissCount());
assertEquals(0, stats.getSecondLevelCacheHitCount());
assertEquals(1, stats.getSecondLevelCachePutCount());
s.close();
return null;
}
});
log.info("Entry persisted, let's load and delete it.");
cleanupCache();
Thread.sleep(10);
withTx(tm, new Callable<Void>() {
@Override
public Void call() throws Exception {
Session s = openSession();
s.getTransaction().begin();
Item found = (Item) s.load(Item.class, item.getId());
log.info(stats.toString());
assertEquals(item.getDescription(), found.getDescription());
assertEquals(1, stats.getSecondLevelCacheMissCount());
assertEquals(0, stats.getSecondLevelCacheHitCount());
assertEquals(2, stats.getSecondLevelCachePutCount());
s.delete(found);
s.getTransaction().commit();
s.close();
return null;
}
});
}
@Test
@TestForIssue(jiraKey = "HHH-9868")
public void testConcurrentRemoveAndPutFromLoad() throws Exception {
final Item item = new Item( "chris", "Chris's Item" );
withTx(tm, () -> {
Session s = openSession();
s.getTransaction().begin();
s.persist(item);
s.getTransaction().commit();
s.close();
return null;
});
Region region = sessionFactory().getSecondLevelCacheRegion(Item.class.getName());
Phaser deletePhaser = new Phaser(2);
Phaser getPhaser = new Phaser(2);
HookInterceptor hook = new HookInterceptor();
AdvancedCache entityCache = ((EntityRegionImpl) region).getCache();
AdvancedCache pendingPutsCache = entityCache.getCacheManager().getCache(
entityCache.getName() + "-" + InfinispanRegionFactory.PENDING_PUTS_CACHE_NAME).getAdvancedCache();
pendingPutsCache.addInterceptor(hook, 0);
Thread deleteThread = new Thread(() -> {
try {
withTx(tm, () -> {
Session s = openSession();
log.trace("Session opened");
s.getTransaction().begin();
log.trace("TX started");
Item loadedItem = s.get(Item.class, item.getId());
assertNotNull(loadedItem);
arriveAndAwait(deletePhaser);
arriveAndAwait(deletePhaser);
log.trace("Item loaded");
s.delete(loadedItem);
log.trace("Item deleted");
s.getTransaction().commit();
log.trace("TX committed");
// start get-thread here
arriveAndAwait(deletePhaser);
arriveAndAwait(deletePhaser);
s.close();
log.trace("Session closed");
return null;
});
} catch (Exception e) {
throw new RuntimeException(e);
}
}, "delete-thread");
Thread getThread = new Thread(() -> {
try {
withTx(tm, () -> {
Session s = openSession();
log.trace("Session opened");
s.getTransaction().begin();
log.trace("TX started");
// DB load should happen before the record is deleted,
// putFromLoad should happen after deleteThread ends
Item loadedItem = s.get(Item.class, item.getId());
assertNotNull(loadedItem);
s.close();
log.trace("Session closed");
return null;
});
} catch (PessimisticLockException e) {
// If we end up here, database locks guard us against situation tested
// in this case and HHH-9868 cannot happen.
// (delete-thread has ITEMS table write-locked and we try to acquire read-lock)
try {
arriveAndAwait(getPhaser);
arriveAndAwait(getPhaser);
} catch (Exception e1) {
throw new RuntimeException(e1);
}
} catch (Exception e) {
throw new RuntimeException(e);
}
}, "get-thread");
deleteThread.start();
// deleteThread loads the entity
arriveAndAwait(deletePhaser);
withTx(tm, () -> {
sessionFactory().getCache().evictEntity(Item.class, item.getId());
assertFalse(sessionFactory().getCache().containsEntity(Item.class, item.getId()));
return null;
});
arriveAndAwait(deletePhaser);
// delete thread invalidates PFER
arriveAndAwait(deletePhaser);
// get thread gets the entity from DB
hook.block(getPhaser, getThread);
getThread.start();
arriveAndAwait(getPhaser);
arriveAndAwait(deletePhaser);
// delete thread finishes the remove from DB and cache
deleteThread.join();
hook.unblock();
arriveAndAwait(getPhaser);
// get thread puts the entry into cache
getThread.join();
withTx(tm, () -> {
Session s = openSession();
s.getTransaction().begin();
Item loadedItem = s.get(Item.class, item.getId());
assertNull(loadedItem);
s.getTransaction().commit();
s.close();
return null;
});
}
protected static void arriveAndAwait(Phaser phaser) throws TimeoutException, InterruptedException {
phaser.awaitAdvanceInterruptibly(phaser.arrive(), 10, TimeUnit.SECONDS);
}
private static class HookInterceptor extends BaseCustomInterceptor {
Phaser phaser;
Thread thread;
public synchronized void block(Phaser phaser, Thread thread) {
this.phaser = phaser;
this.thread = thread;
}
public synchronized void unblock() {
phaser = null;
thread = null;
}
@Override
public Object visitGetKeyValueCommand(InvocationContext ctx, GetKeyValueCommand command) throws Throwable {
Phaser phaser;
Thread thread;
synchronized (this) {
phaser = this.phaser;
thread = this.thread;
}
if (phaser != null && Thread.currentThread() == thread) {
arriveAndAwait(phaser);
arriveAndAwait(phaser);
}
return super.visitGetKeyValueCommand(ctx, command);
}
}
}

View File

@ -1,22 +0,0 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
package org.hibernate.test.cache.infinispan.functional;
/**
* Functional entity read-only tests.
*
* @author Galder Zamarreño
* @since 3.5
*/
public class BasicReadOnlyTestCase extends AbstractFunctionalTestCase {
@Override
public String getCacheConcurrencyStrategy() {
return "read-only";
}
}

View File

@ -0,0 +1,233 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
package org.hibernate.test.cache.infinispan.functional;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.hibernate.FlushMode;
import org.hibernate.Session;
import org.hibernate.cache.spi.access.AccessType;
import org.hibernate.resource.transaction.backend.jdbc.internal.JdbcResourceLocalTransactionCoordinatorBuilderImpl;
import org.hibernate.resource.transaction.backend.jta.internal.JtaTransactionCoordinatorBuilderImpl;
import org.hibernate.stat.SecondLevelCacheStatistics;
import org.hibernate.test.cache.infinispan.tm.JtaPlatformImpl;
import org.hibernate.test.cache.infinispan.tm.XaConnectionProvider;
import org.hibernate.test.cache.infinispan.util.InfinispanTestingSetup;
import org.hibernate.test.cache.infinispan.functional.entities.Contact;
import org.hibernate.test.cache.infinispan.functional.entities.Customer;
import org.hibernate.test.cache.infinispan.util.TestInfinispanRegionFactory;
import org.junit.ClassRule;
import org.junit.Test;
import org.junit.runners.Parameterized;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
/**
* BulkOperationsTestCase.
*
* @author Galder Zamarreño
* @since 3.5
*/
public class BulkOperationsTest extends SingleNodeTest {
@Override
public List<Object[]> getParameters() {
return Arrays.asList(TRANSACTIONAL, READ_WRITE);
}
@ClassRule
public static final InfinispanTestingSetup infinispanTestIdentifier = new InfinispanTestingSetup();
@Override
public String[] getMappings() {
return new String[] {
"cache/infinispan/functional/entities/Contact.hbm.xml",
"cache/infinispan/functional/entities/Customer.hbm.xml"
};
}
@Test
public void testBulkOperations() throws Throwable {
boolean cleanedUp = false;
try {
createContacts();
List<Integer> rhContacts = getContactsByCustomer( "Red Hat" );
assertNotNull( "Red Hat contacts exist", rhContacts );
assertEquals( "Created expected number of Red Hat contacts", 10, rhContacts.size() );
SecondLevelCacheStatistics contactSlcs = sessionFactory()
.getStatistics()
.getSecondLevelCacheStatistics( Contact.class.getName() );
assertEquals( 20, contactSlcs.getElementCountInMemory() );
assertEquals( "Deleted all Red Hat contacts", 10, deleteContacts() );
assertEquals( 0, contactSlcs.getElementCountInMemory() );
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" );
assertEquals( 0, contactSlcs.getElementCountInMemory() );
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());
assertEquals( 10, contactSlcs.getElementCountInMemory() );
updateContactsWithOneManual( "Kabir", "UpdatedAgain" );
assertEquals( 0, contactSlcs.getElementCountInMemory());
for ( Integer id : jbContacts ) {
Contact contact = getContact( id );
assertNotNull( "JBoss contact " + id + " exists", contact );
String expected = ("Kabir".equals( contact.getName() )) ? "UpdatedAgain" : "2222";
assertEquals( "JBoss contact " + id + " has correct TLF", expected, contact.getTlf() );
}
updated = getContactsByTLF( "UpdatedAgain" );
assertNotNull( "Got updated contacts", updated );
assertEquals( "Updated contacts", 5, updated.size() );
}
catch (Throwable t) {
cleanedUp = true;
cleanup( true );
throw t;
}
finally {
// cleanup the db so we can run this test multiple times w/o restarting the cluster
if ( !cleanedUp ) {
cleanup( false );
}
}
}
public void createContacts() throws Exception {
withTxSession(s -> {
for ( int i = 0; i < 10; i++ ) {
Customer c = createCustomer( i );
s.persist(c);
}
});
}
public int deleteContacts() throws Exception {
String deleteHQL = "delete Contact where customer in "
+ " (select customer FROM Customer as customer where customer.name = :cName)";
int rowsAffected = withTxSessionApply(s ->
s.createQuery( deleteHQL ).setFlushMode( FlushMode.AUTO )
.setParameter( "cName", "Red Hat" ).executeUpdate());
return rowsAffected;
}
@SuppressWarnings( {"unchecked"})
public List<Integer> getContactsByCustomer(String customerName) throws Exception {
String selectHQL = "select contact.id from Contact contact"
+ " where contact.customer.name = :cName";
return (List<Integer>) withTxSessionApply(s -> s.createQuery(selectHQL)
.setFlushMode(FlushMode.AUTO)
.setParameter("cName", customerName)
.list());
}
@SuppressWarnings( {"unchecked"})
public List<Integer> getContactsByTLF(String tlf) throws Exception {
String selectHQL = "select contact.id from Contact contact"
+ " where contact.tlf = :cTLF";
return (List<Integer>) withTxSessionApply(s -> s.createQuery(selectHQL)
.setFlushMode(FlushMode.AUTO)
.setParameter("cTLF", tlf)
.list());
}
public int updateContacts(String name, String newTLF) throws Exception {
String updateHQL = "update Contact set tlf = :cNewTLF where name = :cName";
return withTxSessionApply(s -> s.createQuery( updateHQL )
.setFlushMode( FlushMode.AUTO )
.setParameter( "cNewTLF", newTLF )
.setParameter( "cName", name )
.executeUpdate());
}
public int updateContactsWithOneManual(String name, String newTLF) throws Exception {
String queryHQL = "from Contact c where c.name = :cName";
String updateHQL = "update Contact set tlf = :cNewTLF where name = :cName";
return withTxSessionApply(s -> {
List<Contact> list = s.createQuery(queryHQL).setParameter("cName", name).list();
list.get(0).setTlf(newTLF);
return s.createQuery(updateHQL)
.setFlushMode(FlushMode.AUTO)
.setParameter("cNewTLF", newTLF)
.setParameter("cName", name)
.executeUpdate();
});
}
public Contact getContact(Integer id) throws Exception {
return withTxSessionApply(s -> s.get( Contact.class, id ));
}
public void cleanup(boolean ignore) throws Exception {
String deleteContactHQL = "delete from Contact";
String deleteCustomerHQL = "delete from Customer";
withTxSession(s -> {
s.createQuery(deleteContactHQL).setFlushMode(FlushMode.AUTO).executeUpdate();
s.createQuery(deleteCustomerHQL).setFlushMode(FlushMode.AUTO).executeUpdate();
});
}
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 );
return customer;
}
finally {
System.out.println( "CREATE CUSTOMER " + id + " - END" );
}
}
}

View File

@ -9,9 +9,9 @@ package org.hibernate.test.cache.infinispan.functional;
import java.io.PrintWriter; import java.io.PrintWriter;
import java.io.StringWriter; import java.io.StringWriter;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet; import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Map;
import java.util.Random; import java.util.Random;
import java.util.Set; import java.util.Set;
import java.util.concurrent.Callable; import java.util.concurrent.Callable;
@ -20,18 +20,12 @@ import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors; import java.util.concurrent.Executors;
import java.util.concurrent.Future; import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import javax.transaction.TransactionManager;
import org.hibernate.FlushMode; import org.hibernate.FlushMode;
import org.hibernate.Session;
import org.hibernate.engine.jdbc.connections.spi.ConnectionProvider;
import org.hibernate.engine.transaction.jta.platform.spi.JtaPlatform;
import org.hibernate.stat.SecondLevelCacheStatistics; import org.hibernate.stat.SecondLevelCacheStatistics;
import org.hibernate.test.cache.infinispan.functional.cluster.DualNodeConnectionProviderImpl; import org.hibernate.test.cache.infinispan.functional.entities.Contact;
import org.hibernate.test.cache.infinispan.functional.cluster.DualNodeJtaPlatformImpl; import org.hibernate.test.cache.infinispan.functional.entities.Customer;
import org.hibernate.test.cache.infinispan.functional.cluster.DualNodeJtaTransactionManagerImpl;
import org.hibernate.test.cache.infinispan.functional.cluster.DualNodeTestCase;
import org.junit.Test; import org.junit.Test;
import org.infinispan.util.logging.Log; import org.infinispan.util.logging.Log;
@ -45,7 +39,7 @@ import static org.junit.Assert.assertNull;
* @author nikita_tovstoles@mba.berkeley.edu * @author nikita_tovstoles@mba.berkeley.edu
* @author Galder Zamarreño * @author Galder Zamarreño
*/ */
public class ConcurrentWriteTest extends SingleNodeTestCase { public class ConcurrentWriteTest extends SingleNodeTest {
private static final Log log = LogFactory.getLog( ConcurrentWriteTest.class ); private static final Log log = LogFactory.getLog( ConcurrentWriteTest.class );
private static final boolean trace = log.isTraceEnabled(); private static final boolean trace = log.isTraceEnabled();
/** /**
@ -67,35 +61,9 @@ public class ConcurrentWriteTest extends SingleNodeTestCase {
*/ */
private Set<Integer> customerIDs = new HashSet<Integer>(); private Set<Integer> customerIDs = new HashSet<Integer>();
private TransactionManager tm;
@Override @Override
@SuppressWarnings("unchecked") public List<Object[]> getParameters() {
protected void addSettings(Map settings) { return Arrays.asList(TRANSACTIONAL, READ_WRITE);
super.addSettings( settings );
settings.put( DualNodeTestCase.NODE_ID_PROP, DualNodeTestCase.LOCAL );
settings.put( DualNodeTestCase.NODE_ID_FIELD, DualNodeTestCase.LOCAL );
}
@Override
protected boolean getUseQueryCache() {
return true;
}
@Override
protected TransactionManager getTransactionManager() {
return DualNodeJtaTransactionManagerImpl.getInstance( DualNodeTestCase.LOCAL );
}
@Override
protected Class<? extends ConnectionProvider> getConnectionProviderClass() {
return DualNodeConnectionProviderImpl.class;
}
@Override
protected Class<? extends JtaPlatform> getJtaPlatform() {
return DualNodeJtaPlatformImpl.class;
} }
@Override @Override
@ -111,34 +79,18 @@ public class ConcurrentWriteTest extends SingleNodeTestCase {
} }
finally { finally {
cleanup(); cleanup();
// DualNodeJtaTransactionManagerImpl.cleanupTransactions();
// DualNodeJtaTransactionManagerImpl.cleanupTransactionManagers();
} }
} }
@Test @Test
public void testPingDb() throws Exception { public void testPingDb() throws Exception {
try { withTxSession(s -> s.createQuery( "from " + Customer.class.getName() ).list());
beginTx();
sessionFactory()
.getCurrentSession()
.createQuery( "from " + Customer.class.getName() )
.list();
}
catch (Exception e) {
setRollbackOnlyTx( e );
// setRollbackOnly();
// fail("failed to query DB; exception=" + e);
}
finally {
commitOrRollbackTx();
}
} }
@Test @Test
public void testSingleUser() throws Exception { public void testSingleUser() throws Exception {
// setup // setup
sessionFactory().getStatistics().clear(); sessionFactory().getStatistics().clear();
Customer customer = createCustomer( 0 ); Customer customer = createCustomer( 0 );
final Integer customerId = customer.getId(); final Integer customerId = customer.getId();
getCustomerIDs().add( customerId ); getCustomerIDs().add( customerId );
@ -215,36 +167,20 @@ sessionFactory().getStatistics().clear();
getCustomerIDs().clear(); getCustomerIDs().clear();
String deleteContactHQL = "delete from Contact"; String deleteContactHQL = "delete from Contact";
String deleteCustomerHQL = "delete from Customer"; String deleteCustomerHQL = "delete from Customer";
beginTx(); withTxSession(s -> {
try { s.createQuery(deleteContactHQL).setFlushMode(FlushMode.AUTO).executeUpdate();
Session session = sessionFactory().getCurrentSession(); s.createQuery(deleteCustomerHQL).setFlushMode(FlushMode.AUTO).executeUpdate();
session.createQuery( deleteContactHQL ).setFlushMode( FlushMode.AUTO ).executeUpdate(); });
session.createQuery( deleteCustomerHQL ).setFlushMode( FlushMode.AUTO ).executeUpdate();
}
catch (Exception e) {
setRollbackOnlyTx( e );
}
finally {
commitOrRollbackTx();
}
} }
private Customer createCustomer(int nameSuffix) throws Exception { private Customer createCustomer(int nameSuffix) throws Exception {
Customer customer = null; return withTxSessionApply(s -> {
beginTx(); Customer customer = new Customer();
try {
customer = new Customer();
customer.setName( "customer_" + nameSuffix ); customer.setName( "customer_" + nameSuffix );
customer.setContacts( new HashSet<Contact>() ); customer.setContacts( new HashSet<Contact>() );
sessionFactory().getCurrentSession().persist( customer ); s.persist( customer );
} return customer;
catch (Exception e) { });
setRollbackOnlyTx( e );
}
finally {
commitOrRollbackTx();
}
return customer;
} }
/** /**
@ -255,101 +191,75 @@ sessionFactory().getStatistics().clear();
* @throws java.lang.Exception * @throws java.lang.Exception
*/ */
private void readEveryonesFirstContact() throws Exception { private void readEveryonesFirstContact() throws Exception {
beginTx(); withTxSession(s -> {
try {
for ( Integer customerId : getCustomerIDs() ) { for ( Integer customerId : getCustomerIDs() ) {
if ( TERMINATE_ALL_USERS ) { if ( TERMINATE_ALL_USERS ) {
setRollbackOnlyTx(); markRollbackOnly(s);
return; return;
} }
Customer customer = (Customer) sessionFactory() Customer customer = s.load( Customer.class, customerId );
.getCurrentSession()
.load( Customer.class, customerId );
Set<Contact> contacts = customer.getContacts(); Set<Contact> contacts = customer.getContacts();
if ( !contacts.isEmpty() ) { if ( !contacts.isEmpty() ) {
contacts.iterator().next(); contacts.iterator().next();
} }
} }
} });
catch (Exception e) {
setRollbackOnlyTx( e );
}
finally {
commitOrRollbackTx();
}
} }
/** /**
* -load existing Customer -get customer's contacts; return 1st one * -load existing Customer -get customer's contacts; return 1st one
* *
* @param customerId * @param customerId
* @return first Contact or null if customer has none * @return first Contact or null if customer has none
*/ */
private Contact getFirstContact(Integer customerId) throws Exception { private Contact getFirstContact(Integer customerId) throws Exception {
assert customerId != null; assert customerId != null;
Contact firstContact = null; return withTxSessionApply(s -> {
beginTx(); Customer customer = s.load(Customer.class, customerId);
try { Set<Contact> contacts = customer.getContacts();
final Customer customer = (Customer) sessionFactory() Contact firstContact = contacts.isEmpty() ? null : contacts.iterator().next();
.getCurrentSession() if (TERMINATE_ALL_USERS) {
.load(Customer.class, customerId); markRollbackOnly(s);
Set<Contact> contacts = customer.getContacts(); }
firstContact = contacts.isEmpty() ? null : contacts.iterator().next(); return firstContact;
if (TERMINATE_ALL_USERS) });
setRollbackOnlyTx(); }
} catch (Exception e) {
setRollbackOnlyTx(e);
} finally {
commitOrRollbackTx();
}
return firstContact;
}
/** /**
* -load existing Customer -create a new Contact and add to customer's contacts * -load existing Customer -create a new Contact and add to customer's contacts
* *
* @param customerId * @param customerId
* @return added Contact * @return added Contact
*/ */
private Contact addContact(Integer customerId) throws Exception { private Contact addContact(Integer customerId) throws Exception {
assert customerId != null; assert customerId != null;
Contact contact = null; return withTxSessionApply(s -> {
beginTx(); final Customer customer = s.load(Customer.class, customerId);
try { Contact contact = new Contact();
final Customer customer = (Customer) sessionFactory() contact.setName("contact name");
.getCurrentSession() contact.setTlf("wtf is tlf?");
.load(Customer.class, customerId); contact.setCustomer(customer);
contact = new Contact(); customer.getContacts().add(contact);
contact.setName("contact name"); // assuming contact is persisted via cascade from customer
contact.setTlf("wtf is tlf?"); if (TERMINATE_ALL_USERS) {
contact.setCustomer(customer); markRollbackOnly(s);
customer.getContacts().add(contact); }
// assuming contact is persisted via cascade from customer return contact;
if (TERMINATE_ALL_USERS) });
setRollbackOnlyTx(); }
} catch (Exception e) {
setRollbackOnlyTx(e);
} finally {
commitOrRollbackTx();
}
return contact;
}
/** /**
* remove existing 'contact' from customer's list of contacts * remove existing 'contact' from customer's list of contacts
* *
* @param customerId * @param customerId
* @throws IllegalStateException * @throws IllegalStateException
* if customer does not own a contact * if customer does not own a contact
*/ */
private void removeContact(Integer customerId) throws Exception { private void removeContact(Integer customerId) throws Exception {
assert customerId != null; assert customerId != null;
beginTx(); withTxSession(s -> {
try { Customer customer = s.load( Customer.class, customerId );
Customer customer = (Customer) sessionFactory()
.getCurrentSession()
.load( Customer.class, customerId );
Set<Contact> contacts = customer.getContacts(); Set<Contact> contacts = customer.getContacts();
if ( contacts.size() != 1 ) { if ( contacts.size() != 1 ) {
throw new IllegalStateException( throw new IllegalStateException(
@ -369,15 +279,9 @@ sessionFactory().getStatistics().clear();
// assuming contact is persisted via cascade from customer // assuming contact is persisted via cascade from customer
if ( TERMINATE_ALL_USERS ) { if ( TERMINATE_ALL_USERS ) {
setRollbackOnlyTx(); markRollbackOnly(s);
} }
} });
catch (Exception e) {
setRollbackOnlyTx( e );
}
finally {
commitOrRollbackTx();
}
} }
/** /**
@ -423,7 +327,6 @@ sessionFactory().getStatistics().clear();
Thread.currentThread().setName( "UserRunnerThread-" + getCustomerId() ); Thread.currentThread().setName( "UserRunnerThread-" + getCustomerId() );
log.info( "Wait for all executions paths to be ready to perform calls" ); log.info( "Wait for all executions paths to be ready to perform calls" );
try { try {
// barrier.await();
for ( int i = 0; i < ITERATION_COUNT && !TERMINATE_ALL_USERS; i++ ) { for ( int i = 0; i < ITERATION_COUNT && !TERMINATE_ALL_USERS; i++ ) {
contactExists(); contactExists();
if ( trace ) { if ( trace ) {
@ -464,17 +367,6 @@ sessionFactory().getStatistics().clear();
TERMINATE_ALL_USERS = true; TERMINATE_ALL_USERS = true;
log.error( "Error", t ); log.error( "Error", t );
throw new Exception( t ); throw new Exception( t );
// rollback current transaction if any
// really should not happen since above methods all follow begin-commit-rollback pattern
// try {
// if
// (DualNodeJtaTransactionManagerImpl.getInstance(DualNodeTestUtil.LOCAL).getTransaction()
// != null) {
// DualNodeJtaTransactionManagerImpl.getInstance(DualNodeTestUtil.LOCAL).rollback();
// }
// } catch (SystemException ex) {
// throw new RuntimeException("failed to rollback tx", ex);
// }
} }
finally { finally {
log.info( "Wait for all execution paths to finish" ); log.info( "Wait for all execution paths to finish" );

View File

@ -1,73 +0,0 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
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;
}
}

View File

@ -1,49 +0,0 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
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;
}
}

View File

@ -1,12 +1,13 @@
package org.hibernate.test.cache.infinispan.functional; package org.hibernate.test.cache.infinispan.functional;
import java.util.concurrent.Callable;
import org.hibernate.Session;
import org.hibernate.stat.Statistics; import org.hibernate.stat.Statistics;
import org.hibernate.test.cache.infinispan.functional.entities.Name;
import org.hibernate.test.cache.infinispan.functional.entities.Person;
import org.junit.Test; import org.junit.Test;
import static org.infinispan.test.TestingUtil.withTx; import java.util.Arrays;
import java.util.List;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull; import static org.junit.Assert.assertNull;
@ -17,61 +18,50 @@ import static org.junit.Assert.assertTrue;
* *
* @author Radim Vansa &lt;rvansa@redhat.com&gt; * @author Radim Vansa &lt;rvansa@redhat.com&gt;
*/ */
public class EqualityTest extends SingleNodeTestCase { public class EqualityTest extends SingleNodeTest {
@Override @Override
protected Class[] getAnnotatedClasses() { public List<Object[]> getParameters() {
return new Class[] { Person.class }; return Arrays.asList(TRANSACTIONAL, READ_WRITE, READ_ONLY);
} }
@Test @Override
public void testEqualityFromType() throws Exception { protected Class[] getAnnotatedClasses() {
Person john = new Person("John", "Black", 26); return new Class[] { Person.class };
Person peter = new Person("Peter", "White", 32); }
withTx(tm, new Callable<Void>() { @Test
@Override public void testEqualityFromType() throws Exception {
public Void call() throws Exception { Person john = new Person("John", "Black", 26);
Session session = openSession(); Person peter = new Person("Peter", "White", 32);
session.getTransaction().begin();
session.persist(john);
session.persist(peter);
session.getTransaction().commit();
session.close();
return null;
}
});
Statistics statistics = sessionFactory().getStatistics(); withTxSession(s -> {
statistics.clear(); s.persist(john);
s.persist(peter);
});
for (int i = 0; i < 5; ++i) { Statistics statistics = sessionFactory().getStatistics();
withTx(tm, new Callable<Void>() { statistics.clear();
@Override
public Void call() throws Exception {
Session session = openSession();
session.getTransaction().begin();
Person p1 = session.get(Person.class, john.name);
assertPersonEquals(john, p1);
Person p2 = session.get(Person.class, peter.name);
assertPersonEquals(peter, p2);
Person p3 = session.get(Person.class, new Name("Foo", "Bar"));
assertNull(p3);
session.getTransaction().commit();
session.close();
return null;
}
});
}
assertTrue(statistics.getSecondLevelCacheHitCount() > 0); for (int i = 0; i < 5; ++i) {
assertTrue(statistics.getSecondLevelCacheMissCount() > 0); withTxSession(s -> {
} Person p1 = s.get(Person.class, john.getName());
assertPersonEquals(john, p1);
Person p2 = s.get(Person.class, peter.getName());
assertPersonEquals(peter, p2);
Person p3 = s.get(Person.class, new Name("Foo", "Bar"));
assertNull(p3);
});
}
private static void assertPersonEquals(Person expected, Person person) { assertTrue(statistics.getSecondLevelCacheHitCount() > 0);
assertNotNull(person); assertTrue(statistics.getSecondLevelCacheMissCount() > 0);
assertNotNull(person.getName()); }
assertEquals(expected.getName().getFirstName(), person.getName().getFirstName());
assertEquals(expected.getName().getLastName(), person.getName().getLastName()); private static void assertPersonEquals(Person expected, Person person) {
assertEquals(expected.getAge(), person.getAge()); assertNotNull(person);
} assertNotNull(person.getName());
assertEquals(expected.getName().getFirstName(), person.getName().getFirstName());
assertEquals(expected.getName().getLastName(), person.getName().getLastName());
assertEquals(expected.getAge(), person.getAge());
}
} }

View File

@ -6,6 +6,8 @@
*/ */
package org.hibernate.test.cache.infinispan.functional; package org.hibernate.test.cache.infinispan.functional;
import java.util.Collections;
import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Properties; import java.util.Properties;
import javax.naming.Context; import javax.naming.Context;
@ -15,16 +17,17 @@ import javax.naming.NameNotFoundException;
import javax.naming.Reference; import javax.naming.Reference;
import javax.naming.StringRefAddr; import javax.naming.StringRefAddr;
import org.hibernate.Session;
import org.hibernate.boot.registry.StandardServiceRegistry; import org.hibernate.boot.registry.StandardServiceRegistry;
import org.hibernate.cache.infinispan.InfinispanRegionFactory; import org.hibernate.cache.infinispan.InfinispanRegionFactory;
import org.hibernate.cache.infinispan.JndiInfinispanRegionFactory; import org.hibernate.cache.infinispan.JndiInfinispanRegionFactory;
import org.hibernate.cache.spi.RegionFactory; import org.hibernate.cache.spi.access.AccessType;
import org.hibernate.cfg.Environment; import org.hibernate.cfg.Environment;
import org.hibernate.engine.config.spi.ConfigurationService; import org.hibernate.engine.config.spi.ConfigurationService;
import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.resource.transaction.backend.jdbc.internal.JdbcResourceLocalTransactionCoordinatorBuilderImpl;
import org.hibernate.stat.Statistics; import org.hibernate.stat.Statistics;
import org.hibernate.test.cache.infinispan.functional.entities.Item;
import org.junit.Test; import org.junit.Test;
import org.infinispan.Cache; import org.infinispan.Cache;
@ -38,17 +41,15 @@ import org.jboss.util.naming.NonSerializableFactory;
import org.jnp.server.Main; import org.jnp.server.Main;
import org.jnp.server.SingletonNamingServer; import org.jnp.server.SingletonNamingServer;
import org.junit.runners.Parameterized;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
/** /**
* // TODO: Document this
*
* @author Galder Zamarreño * @author Galder Zamarreño
* @since // TODO
*/ */
public class JndiRegionFactoryTestCase extends SingleNodeTestCase { public class JndiRegionFactoryTest extends SingleNodeTest {
private static final Log log = LogFactory.getLog( JndiRegionFactoryTestCase.class ); private static final Log log = LogFactory.getLog( JndiRegionFactoryTest.class );
private static final String JNDI_NAME = "java:CacheManager"; private static final String JNDI_NAME = "java:CacheManager";
private Main namingMain; private Main namingMain;
private SingletonNamingServer namingServer; private SingletonNamingServer namingServer;
@ -56,6 +57,11 @@ public class JndiRegionFactoryTestCase extends SingleNodeTestCase {
private boolean bindToJndi = true; private boolean bindToJndi = true;
private EmbeddedCacheManager manager; private EmbeddedCacheManager manager;
@Override
public List<Object[]> getParameters() {
return Collections.singletonList(new Object[]{"read-write", null, JdbcResourceLocalTransactionCoordinatorBuilderImpl.class, null, AccessType.READ_WRITE, JndiInfinispanRegionFactory.class});
}
@Override @Override
protected void cleanupTest() throws Exception { protected void cleanupTest() throws Exception {
Context ctx = new InitialContext( props ); Context ctx = new InitialContext( props );
@ -65,11 +71,6 @@ public class JndiRegionFactoryTestCase extends SingleNodeTestCase {
manager.stop(); // Need to stop cos JNDI region factory does not stop it. manager.stop(); // Need to stop cos JNDI region factory does not stop it.
} }
@Override
protected Class<? extends RegionFactory> getCacheRegionFactory() {
return JndiInfinispanRegionFactory.class;
}
@Override @Override
protected void afterStandardServiceRegistryBuilt(StandardServiceRegistry ssr) { protected void afterStandardServiceRegistryBuilt(StandardServiceRegistry ssr) {
if ( bindToJndi ) { if ( bindToJndi ) {
@ -118,46 +119,23 @@ public class JndiRegionFactoryTestCase extends SingleNodeTestCase {
addEntityCheckCache( sessionFactory() ); addEntityCheckCache( sessionFactory() );
JndiInfinispanRegionFactory regionFactory = (JndiInfinispanRegionFactory) sessionFactory().getSettings().getRegionFactory(); JndiInfinispanRegionFactory regionFactory = (JndiInfinispanRegionFactory) sessionFactory().getSettings().getRegionFactory();
Cache cache = regionFactory.getCacheManager().getCache( "org.hibernate.test.cache.infinispan.functional.Item" ); Cache cache = regionFactory.getCacheManager().getCache( Item.class.getName() );
assertEquals( ComponentStatus.RUNNING, cache.getStatus() ); assertEquals( ComponentStatus.RUNNING, cache.getStatus() );
} }
private void addEntityCheckCache(SessionFactoryImplementor sessionFactory) throws Exception { private void addEntityCheckCache(SessionFactoryImplementor sessionFactory) throws Exception {
Item item = new Item( "chris", "Chris's Item" ); Item item = new Item( "chris", "Chris's Item" );
beginTx(); withTxSession(s -> s.persist( item ));
try {
Session s = sessionFactory.openSession();
s.getTransaction().begin();
s.persist( item );
s.getTransaction().commit();
s.close();
}
catch (Exception e) {
setRollbackOnlyTx( e );
}
finally {
commitOrRollbackTx();
}
beginTx(); withTxSession(s -> {
try { Item found = s.load(Item.class, item.getId());
Session s = sessionFactory.openSession();
Item found = (Item) s.load( Item.class, item.getId() );
Statistics stats = sessionFactory.getStatistics(); Statistics stats = sessionFactory.getStatistics();
log.info( stats.toString() ); log.info(stats.toString());
assertEquals( item.getDescription(), found.getDescription() ); assertEquals(item.getDescription(), found.getDescription());
assertEquals( 0, stats.getSecondLevelCacheMissCount() ); assertEquals(0, stats.getSecondLevelCacheMissCount());
assertEquals( 1, stats.getSecondLevelCacheHitCount() ); assertEquals(1, stats.getSecondLevelCacheHitCount());
s.delete( found ); s.delete(found);
s.close(); });
}
catch (Exception e) {
setRollbackOnlyTx( e );
}
finally {
commitOrRollbackTx();
}
} }
/** /**

View File

@ -0,0 +1,108 @@
package org.hibernate.test.cache.infinispan.functional;
import org.hibernate.MultiTenancyStrategy;
import org.hibernate.boot.SessionFactoryBuilder;
import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
import org.hibernate.cache.infinispan.entity.EntityRegionImpl;
import org.hibernate.cache.infinispan.util.Caches;
import org.hibernate.engine.jdbc.connections.spi.AbstractMultiTenantConnectionProvider;
import org.hibernate.engine.jdbc.connections.spi.ConnectionProvider;
import org.hibernate.engine.jdbc.connections.spi.MultiTenantConnectionProvider;
import org.hibernate.test.cache.infinispan.functional.entities.Item;
import org.hibernate.test.cache.infinispan.tm.XaConnectionProvider;
import org.hibernate.testing.env.ConnectionProviderBuilder;
import org.infinispan.AdvancedCache;
import org.infinispan.commons.util.CloseableIterable;
import org.infinispan.context.Flag;
import org.junit.Test;
import java.util.Collections;
import java.util.List;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
/**
* @author Radim Vansa &lt;rvansa@redhat.com&gt;
*/
public class MultiTenancyTest extends SingleNodeTest {
private static final String DB1 = "db1";
private static final String DB2 = "db2";
private final ConnectionProvider db1
= new XaConnectionProvider(ConnectionProviderBuilder.buildConnectionProvider(DB1));
private final ConnectionProvider db2
= new XaConnectionProvider(ConnectionProviderBuilder.buildConnectionProvider(DB2));
@Override
public List<Object[]> getParameters() {
return Collections.singletonList(READ_ONLY);
}
@Override
protected void configureStandardServiceRegistryBuilder(StandardServiceRegistryBuilder ssrb) {
super.configureStandardServiceRegistryBuilder(ssrb);
ssrb.addService(MultiTenantConnectionProvider.class, new AbstractMultiTenantConnectionProvider() {
@Override
protected ConnectionProvider getAnyConnectionProvider() {
return db1;
}
@Override
protected ConnectionProvider selectConnectionProvider(String tenantIdentifier) {
if (DB1.equals(tenantIdentifier)) return db1;
if (DB2.equals(tenantIdentifier)) return db2;
throw new IllegalArgumentException();
}
});
}
@Override
protected void configureSessionFactoryBuilder(SessionFactoryBuilder sfb) {
super.configureSessionFactoryBuilder(sfb);
sfb.applyMultiTenancyStrategy(MultiTenancyStrategy.DATABASE);
}
@Override
protected void cleanupTest() throws Exception {
db1.getConnection().close();
db2.getConnection().close();
}
@Test
public void testMultiTenancy() throws Exception {
final Item item = new Item("my item", "description" );
withTxSession(sessionFactory().withOptions().tenantIdentifier(DB1), s -> s.persist(item));
for (int i = 0; i < 5; ++i) { // make sure we get something cached
withTxSession(sessionFactory().withOptions().tenantIdentifier(DB1), s -> {
Item item2 = s.get(Item.class, item.getId());
assertNotNull(item2);
assertEquals(item.getName(), item2.getName());
});
}
// The table ITEMS is not created in DB2 - we would get just an exception
// for (int i = 0; i < 5; ++i) { // make sure we get something cached
// withTx(tm, new Callable<Void>() {
// @Override
// public Void call() throws Exception {
// Session s = sessionFactory().withOptions().tenantIdentifier(DB2).openSession();
// s.getTransaction().begin();
// Item item2 = s.get(Item.class, id);
// s.getTransaction().commit();
// s.close();
// assertNull(item2);
// return null;
// }
// });
// }
EntityRegionImpl region = (EntityRegionImpl) sessionFactory().getSecondLevelCacheRegion(Item.class.getName());
AdvancedCache localCache = region.getCache().withFlags(Flag.CACHE_MODE_LOCAL);
CloseableIterable keys = Caches.keys(localCache);
assertEquals(1, localCache.size());
assertEquals("OldCacheKeyImplementation", keys.iterator().next().getClass().getSimpleName());
}
}

View File

@ -1,122 +0,0 @@
package org.hibernate.test.cache.infinispan.functional;
import java.util.concurrent.Callable;
import org.hibernate.MultiTenancyStrategy;
import org.hibernate.Session;
import org.hibernate.boot.SessionFactoryBuilder;
import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
import org.hibernate.cache.infinispan.entity.EntityRegionImpl;
import org.hibernate.cache.infinispan.util.Caches;
import org.hibernate.engine.jdbc.connections.spi.AbstractMultiTenantConnectionProvider;
import org.hibernate.engine.jdbc.connections.spi.ConnectionProvider;
import org.hibernate.engine.jdbc.connections.spi.MultiTenantConnectionProvider;
import org.hibernate.test.cache.infinispan.tm.XaConnectionProvider;
import org.hibernate.testing.env.ConnectionProviderBuilder;
import org.infinispan.AdvancedCache;
import org.infinispan.commons.util.CloseableIterable;
import org.infinispan.commons.util.CloseableIteratorSet;
import org.infinispan.context.Flag;
import org.junit.Test;
import static org.infinispan.test.TestingUtil.withTx;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
/**
* @author Radim Vansa &lt;rvansa@redhat.com&gt;
*/
public class MultiTenancyTestCase extends SingleNodeTestCase {
private static final String DB1 = "db1";
private static final String DB2 = "db2";
private final ConnectionProvider db1
= new XaConnectionProvider(ConnectionProviderBuilder.buildConnectionProvider(DB1));
private final ConnectionProvider db2
= new XaConnectionProvider(ConnectionProviderBuilder.buildConnectionProvider(DB2));
@Override
protected void configureStandardServiceRegistryBuilder(StandardServiceRegistryBuilder ssrb) {
super.configureStandardServiceRegistryBuilder(ssrb);
ssrb.addService(MultiTenantConnectionProvider.class, new AbstractMultiTenantConnectionProvider() {
@Override
protected ConnectionProvider getAnyConnectionProvider() {
return db1;
}
@Override
protected ConnectionProvider selectConnectionProvider(String tenantIdentifier) {
if (DB1.equals(tenantIdentifier)) return db1;
if (DB2.equals(tenantIdentifier)) return db2;
throw new IllegalArgumentException();
}
});
}
@Override
protected void configureSessionFactoryBuilder(SessionFactoryBuilder sfb) {
super.configureSessionFactoryBuilder(sfb);
sfb.applyMultiTenancyStrategy(MultiTenancyStrategy.DATABASE);
}
@Override
protected void cleanupTest() throws Exception {
db1.getConnection().close();
db2.getConnection().close();
}
@Test
public void testMultiTenancy() throws Exception {
final Item item = new Item("my item", "description" );
long id = withTx(tm, new Callable<Long>() {
@Override
public Long call() throws Exception {
Session s = sessionFactory().withOptions().tenantIdentifier(DB1).openSession();
s.getTransaction().begin();
s.persist(item);
s.getTransaction().commit();
s.close();
return item.getId();
}
});
for (int i = 0; i < 5; ++i) { // make sure we get something cached
withTx(tm, new Callable<Void>() {
@Override
public Void call() throws Exception {
Session s = sessionFactory().withOptions().tenantIdentifier(DB1).openSession();
s.getTransaction().begin();
Item item2 = s.get(Item.class, id);
s.getTransaction().commit();
s.close();
assertNotNull(item2);
assertEquals(item.getName(), item2.getName());
return null;
}
});
}
// The table ITEMS is not created in DB2 - we would get just an exception
// for (int i = 0; i < 5; ++i) { // make sure we get something cached
// withTx(tm, new Callable<Void>() {
// @Override
// public Void call() throws Exception {
// Session s = sessionFactory().withOptions().tenantIdentifier(DB2).openSession();
// s.getTransaction().begin();
// Item item2 = s.get(Item.class, id);
// s.getTransaction().commit();
// s.close();
// assertNull(item2);
// return null;
// }
// });
// }
EntityRegionImpl region = (EntityRegionImpl) sessionFactory().getSecondLevelCacheRegion(Item.class.getName());
AdvancedCache localCache = region.getCache().withFlags(Flag.CACHE_MODE_LOCAL);
CloseableIterable keys = Caches.keys(localCache);
assertEquals(1, localCache.size());
assertEquals("OldCacheKeyImplementation", keys.iterator().next().getClass().getSimpleName());
}
}

View File

@ -0,0 +1,46 @@
package org.hibernate.test.cache.infinispan.functional;
import org.hibernate.cache.infinispan.entity.EntityRegionImpl;
import org.hibernate.cache.infinispan.util.Caches;
import org.hibernate.test.cache.infinispan.functional.entities.Item;
import org.infinispan.AdvancedCache;
import org.infinispan.commons.util.CloseableIterable;
import org.infinispan.context.Flag;
import org.junit.Test;
import java.util.Collections;
import java.util.List;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
/**
* @author Radim Vansa &lt;rvansa@redhat.com&gt;
*/
public class NoTenancyTest extends SingleNodeTest {
@Override
public List<Object[]> getParameters() {
return Collections.singletonList(READ_ONLY);
}
@Test
public void testNoTenancy() throws Exception {
final Item item = new Item("my item", "description" );
withTxSession(s -> s.persist(item));
for (int i = 0; i < 5; ++i) { // make sure we get something cached
withTxSession(s -> {
Item item2 = s.get(Item.class, item.getId());
assertNotNull(item2);
assertEquals(item.getName(), item2.getName());
});
}
EntityRegionImpl region = (EntityRegionImpl) sessionFactory().getSecondLevelCacheRegion(Item.class.getName());
AdvancedCache localCache = region.getCache().withFlags(Flag.CACHE_MODE_LOCAL);
CloseableIterable keys = Caches.keys(localCache);
assertEquals(1, localCache.size());
assertEquals(sessionFactory().getClassMetadata(Item.class).getIdentifierType().getReturnedClass(), keys.iterator().next().getClass());
}
}

View File

@ -1,60 +0,0 @@
package org.hibernate.test.cache.infinispan.functional;
import java.util.concurrent.Callable;
import org.hibernate.Session;
import org.hibernate.cache.infinispan.entity.EntityRegionImpl;
import org.hibernate.cache.infinispan.util.Caches;
import org.infinispan.AdvancedCache;
import org.infinispan.commons.util.CloseableIterable;
import org.infinispan.commons.util.CloseableIteratorSet;
import org.infinispan.context.Flag;
import org.junit.Test;
import static org.infinispan.test.TestingUtil.withTx;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
/**
* @author Radim Vansa &lt;rvansa@redhat.com&gt;
*/
public class NoTenancyTestCase extends SingleNodeTestCase {
@Test
public void testNoTenancy() throws Exception {
final Item item = new Item("my item", "description" );
long id = withTx(tm, new Callable<Long>() {
@Override
public Long call() throws Exception {
Session s = openSession();
s.getTransaction().begin();
s.persist(item);
s.getTransaction().commit();
s.close();
return item.getId();
}
});
for (int i = 0; i < 5; ++i) { // make sure we get something cached
withTx(tm, new Callable<Void>() {
@Override
public Void call() throws Exception {
Session s = openSession();
s.getTransaction().begin();
Item item2 = s.get(Item.class, id);
s.getTransaction().commit();
s.close();
assertNotNull(item2);
assertEquals(item.getName(), item2.getName());
return null;
}
});
}
EntityRegionImpl region = (EntityRegionImpl) sessionFactory().getSecondLevelCacheRegion(Item.class.getName());
AdvancedCache localCache = region.getCache().withFlags(Flag.CACHE_MODE_LOCAL);
CloseableIterable keys = Caches.keys(localCache);
assertEquals(1, localCache.size());
assertEquals(sessionFactory().getClassMetadata(Item.class).getIdentifierType().getReturnedClass(), keys.iterator().next().getClass());
}
}

View File

@ -0,0 +1,232 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
package org.hibernate.test.cache.infinispan.functional;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Phaser;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import org.hibernate.PessimisticLockException;
import org.hibernate.cache.infinispan.InfinispanRegionFactory;
import org.hibernate.cache.infinispan.entity.EntityRegionImpl;
import org.hibernate.cache.spi.Region;
import org.hibernate.stat.SecondLevelCacheStatistics;
import org.hibernate.stat.Statistics;
import org.hibernate.test.cache.infinispan.functional.entities.Item;
import org.hibernate.testing.TestForIssue;
import org.infinispan.AdvancedCache;
import org.infinispan.commands.read.GetKeyValueCommand;
import org.infinispan.context.InvocationContext;
import org.infinispan.interceptors.base.BaseCustomInterceptor;
import org.infinispan.util.logging.Log;
import org.infinispan.util.logging.LogFactory;
import org.junit.Test;
import org.junit.runners.Parameterized;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
/**
* Parent tests for both transactional and
* read-only tests are defined in this class.
*
* @author Galder Zamarreño
* @since 4.1
*/
public class ReadOnlyTest extends SingleNodeTest {
static final Log log = LogFactory.getLog(ReadOnlyTest.class);
@Override
public List<Object[]> getParameters() {
return Collections.singletonList(READ_ONLY);
}
@Test
public void testEmptySecondLevelCacheEntry() throws Exception {
sessionFactory().getCache().evictCollectionRegion( Item.class.getName() + ".items" );
Statistics stats = sessionFactory().getStatistics();
stats.clear();
SecondLevelCacheStatistics statistics = stats.getSecondLevelCacheStatistics( Item.class.getName() + ".items" );
Map cacheEntries = statistics.getEntries();
assertEquals( 0, cacheEntries.size() );
}
@Test
public void testInsertDeleteEntity() throws Exception {
final Statistics stats = sessionFactory().getStatistics();
stats.clear();
final Item item = new Item( "chris", "Chris's Item" );
withTxSession(s -> s.persist(item));
log.info("Entry persisted, let's load and delete it.");
withTxSession(s -> {
Item found = s.load(Item.class, item.getId());
log.info(stats.toString());
assertEquals(item.getDescription(), found.getDescription());
assertEquals(0, stats.getSecondLevelCacheMissCount());
assertEquals(1, stats.getSecondLevelCacheHitCount());
s.delete(found);
});
}
@Test
public void testInsertClearCacheDeleteEntity() throws Exception {
final Statistics stats = sessionFactory().getStatistics();
stats.clear();
final Item item = new Item( "chris", "Chris's Item" );
withTxSession(s -> s.persist(item));
assertEquals(0, stats.getSecondLevelCacheMissCount());
assertEquals(0, stats.getSecondLevelCacheHitCount());
assertEquals(1, stats.getSecondLevelCachePutCount());
log.info("Entry persisted, let's load and delete it.");
cleanupCache();
Thread.sleep(10);
withTxSession(s -> {
Item found = s.load(Item.class, item.getId());
log.info(stats.toString());
assertEquals(item.getDescription(), found.getDescription());
assertEquals(1, stats.getSecondLevelCacheMissCount());
assertEquals(0, stats.getSecondLevelCacheHitCount());
assertEquals(2, stats.getSecondLevelCachePutCount());
s.delete(found);
});
}
@Test
@TestForIssue(jiraKey = "HHH-9868")
public void testConcurrentRemoveAndPutFromLoad() throws Exception {
final Item item = new Item( "chris", "Chris's Item" );
withTxSession(s -> {
s.persist(item);
});
Region region = sessionFactory().getSecondLevelCacheRegion(Item.class.getName());
Phaser deletePhaser = new Phaser(2);
Phaser getPhaser = new Phaser(2);
HookInterceptor hook = new HookInterceptor();
AdvancedCache entityCache = ((EntityRegionImpl) region).getCache();
AdvancedCache pendingPutsCache = entityCache.getCacheManager().getCache(
entityCache.getName() + "-" + InfinispanRegionFactory.PENDING_PUTS_CACHE_NAME).getAdvancedCache();
pendingPutsCache.addInterceptor(hook, 0);
Thread deleteThread = new Thread(() -> {
try {
withTxSession(s -> {
Item loadedItem = s.get(Item.class, item.getId());
assertNotNull(loadedItem);
arriveAndAwait(deletePhaser);
arriveAndAwait(deletePhaser);
log.trace("Item loaded");
s.delete(loadedItem);
s.flush();
log.trace("Item deleted");
// start get-thread here
arriveAndAwait(deletePhaser);
arriveAndAwait(deletePhaser);
});
} catch (Exception e) {
throw new RuntimeException(e);
}
}, "delete-thread");
Thread getThread = new Thread(() -> {
try {
withTxSession(s -> {
// DB load should happen before the record is deleted,
// putFromLoad should happen after deleteThread ends
Item loadedItem = s.get(Item.class, item.getId());
assertNotNull(loadedItem);
});
} catch (PessimisticLockException e) {
// If we end up here, database locks guard us against situation tested
// in this case and HHH-9868 cannot happen.
// (delete-thread has ITEMS table write-locked and we try to acquire read-lock)
try {
arriveAndAwait(getPhaser);
arriveAndAwait(getPhaser);
} catch (Exception e1) {
throw new RuntimeException(e1);
}
} catch (Exception e) {
throw new RuntimeException(e);
}
}, "get-thread");
deleteThread.start();
// deleteThread loads the entity
arriveAndAwait(deletePhaser);
withTx(() -> {
sessionFactory().getCache().evictEntity(Item.class, item.getId());
assertFalse(sessionFactory().getCache().containsEntity(Item.class, item.getId()));
return null;
});
arriveAndAwait(deletePhaser);
// delete thread invalidates PFER
arriveAndAwait(deletePhaser);
// get thread gets the entity from DB
hook.block(getPhaser, getThread);
getThread.start();
arriveAndAwait(getPhaser);
arriveAndAwait(deletePhaser);
// delete thread finishes the remove from DB and cache
deleteThread.join();
hook.unblock();
arriveAndAwait(getPhaser);
// get thread puts the entry into cache
getThread.join();
withTxSession(s -> {
Item loadedItem = s.get(Item.class, item.getId());
assertNull(loadedItem);
});
}
protected static void arriveAndAwait(Phaser phaser) throws TimeoutException, InterruptedException {
phaser.awaitAdvanceInterruptibly(phaser.arrive(), 1000, TimeUnit.SECONDS);
}
private static class HookInterceptor extends BaseCustomInterceptor {
Phaser phaser;
Thread thread;
public synchronized void block(Phaser phaser, Thread thread) {
this.phaser = phaser;
this.thread = thread;
}
public synchronized void unblock() {
phaser = null;
thread = null;
}
@Override
public Object visitGetKeyValueCommand(InvocationContext ctx, GetKeyValueCommand command) throws Throwable {
Phaser phaser;
Thread thread;
synchronized (this) {
phaser = this.phaser;
thread = this.thread;
}
if (phaser != null && Thread.currentThread() == thread) {
arriveAndAwait(phaser);
arriveAndAwait(phaser);
}
return super.visitGetKeyValueCommand(ctx, command);
}
}
}

View File

@ -0,0 +1,687 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
package org.hibernate.test.cache.infinispan.functional;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import org.hibernate.Cache;
import org.hibernate.Criteria;
import org.hibernate.Hibernate;
import org.hibernate.NaturalIdLoadAccess;
import org.hibernate.Session;
import org.hibernate.cache.spi.entry.CacheEntry;
import org.hibernate.criterion.Restrictions;
import org.hibernate.stat.SecondLevelCacheStatistics;
import org.hibernate.stat.Statistics;
import org.hibernate.test.cache.infinispan.functional.entities.Citizen;
import org.hibernate.test.cache.infinispan.functional.entities.Item;
import org.hibernate.test.cache.infinispan.functional.entities.NaturalIdOnManyToOne;
import org.hibernate.test.cache.infinispan.functional.entities.OtherItem;
import org.hibernate.test.cache.infinispan.functional.entities.State;
import org.hibernate.test.cache.infinispan.functional.entities.VersionedItem;
import org.hibernate.testing.TestForIssue;
import org.infinispan.commons.util.ByRef;
import org.junit.After;
import org.junit.Test;
import org.junit.runners.Parameterized;
import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertNotNull;
import static org.hibernate.test.cache.infinispan.util.TxUtil.markRollbackOnly;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
/**
* Functional entity transactional tests.
*
* @author Galder Zamarreño
* @since 3.5
*/
public class ReadWriteTest extends ReadOnlyTest {
@Override
public List<Object[]> getParameters() {
return Arrays.asList(TRANSACTIONAL, READ_WRITE);
}
@Override
protected Class<?>[] getAnnotatedClasses() {
return new Class[] {
Citizen.class, State.class,
NaturalIdOnManyToOne.class
};
}
@After
public void cleanupData() throws Exception {
super.cleanupCache();
withTxSession(s -> {
s.createQuery( "delete NaturalIdOnManyToOne" ).executeUpdate();
s.createQuery( "delete Citizen" ).executeUpdate();
s.createQuery( "delete State" ).executeUpdate();
});
}
@Test
public void testCollectionCache() throws Exception {
final Statistics stats = sessionFactory().getStatistics();
stats.clear();
final Item item = new Item( "chris", "Chris's Item" );
final Item another = new Item( "another", "Owned Item" );
item.addItem( another );
withTxSession(s -> {
s.persist( item );
s.persist( another );
});
withTxSession(s -> {
Item loaded = s.load( Item.class, item.getId() );
assertEquals( 1, loaded.getItems().size() );
});
withTxSession(s -> {
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() );
Map cacheEntries = cStats.getEntries();
assertEquals( 1, cacheEntries.size() );
Item itemElement = loadedWithCachedCollection.getItems().iterator().next();
itemElement.setOwner( null );
loadedWithCachedCollection.getItems().clear();
s.delete( itemElement );
s.delete( loadedWithCachedCollection );
});
}
@Test
@TestForIssue( jiraKey = "HHH-9231" )
public void testAddNewOneToManyElementInitFlushLeaveCacheConsistent() throws Exception {
Statistics stats = sessionFactory().getStatistics();
stats.clear();
SecondLevelCacheStatistics cStats = stats.getSecondLevelCacheStatistics( Item.class.getName() + ".items" );
ByRef<Long> itemId = new ByRef<>(null);
saveItem(itemId);
// create an element for item.itsms
Item itemElement = new Item();
itemElement.setName( "element" );
itemElement.setDescription( "element item" );
withTxSession(s -> {
Item item = s.get( Item.class, itemId.get() );
assertFalse( Hibernate.isInitialized( item.getItems() ) );
// Add an element to item.items (a Set); it will initialize the Set.
item.addItem( itemElement );
assertTrue( Hibernate.isInitialized( item.getItems() ) );
s.persist( itemElement );
s.flush();
markRollbackOnly(s);
});
withTxSession(s -> {
Item item = s.get( Item.class, itemId.get() );
Hibernate.initialize( item.getItems() );
assertTrue( item.getItems().isEmpty() );
s.delete( item );
});
}
@Test
@TestForIssue( jiraKey = "HHH-9231" )
public void testAddNewOneToManyElementNoInitFlushLeaveCacheConsistent() throws Exception {
Statistics stats = sessionFactory().getStatistics();
stats.clear();
SecondLevelCacheStatistics cStats = stats.getSecondLevelCacheStatistics( Item.class.getName() + ".items" );
ByRef<Long> itemId = new ByRef<>(null);
saveItem(itemId);
// create an element for item.bagOfItems
Item itemElement = new Item();
itemElement.setName( "element" );
itemElement.setDescription( "element item" );
withTxSession(s -> {
Item item = s.get( Item.class, itemId.get() );
assertFalse( Hibernate.isInitialized( item.getItems() ) );
// Add an element to item.bagOfItems (a bag); it will not initialize the bag.
item.addItemToBag( itemElement );
assertFalse( Hibernate.isInitialized( item.getBagOfItems() ) );
s.persist( itemElement );
s.flush();
markRollbackOnly(s);
});
withTxSession(s -> {
Item item = s.get( Item.class, itemId.get() );
Hibernate.initialize( item.getItems() );
assertTrue( item.getItems().isEmpty() );
s.delete( item );
});
}
@Test
public void testAddNewOneToManyElementNoInitFlushInitLeaveCacheConsistent() throws Exception {
Statistics stats = sessionFactory().getStatistics();
stats.clear();
SecondLevelCacheStatistics cStats = stats.getSecondLevelCacheStatistics( Item.class.getName() + ".items" );
ByRef<Long> itemId = new ByRef<>(null);
saveItem(itemId);
// create an element for item.bagOfItems
Item itemElement = new Item();
itemElement.setName( "element" );
itemElement.setDescription( "element item" );
withTxSession(s -> {
Item item = s.get(Item.class, itemId.get());
assertFalse(Hibernate.isInitialized(item.getBagOfItems()));
// Add an element to item.bagOfItems (a bag); it will not initialize the bag.
item.addItemToBag(itemElement);
assertFalse(Hibernate.isInitialized(item.getBagOfItems()));
s.persist(itemElement);
s.flush();
// Now initialize the collection; it will contain the uncommitted itemElement.
Hibernate.initialize(item.getBagOfItems());
markRollbackOnly(s);
});
withTxSession(s -> {
Item item = s.get(Item.class, itemId.get());
// Because of HHH-9231, the following will fail due to ObjectNotFoundException because the
// collection will be read from the cache and it still contains the uncommitted element,
// which cannot be found.
Hibernate.initialize(item.getBagOfItems());
assertTrue(item.getBagOfItems().isEmpty());
s.delete(item);
});
}
protected void saveItem(ByRef<Long> itemId) throws Exception {
withTxSession(s -> {
Item item = new Item();
item.setName( "steve" );
item.setDescription( "steve's item" );
s.save( item );
itemId.set(item.getId());
});
}
@Test
public void testAddNewManyToManyPropertyRefNoInitFlushInitLeaveCacheConsistent() throws Exception {
Statistics stats = sessionFactory().getStatistics();
stats.clear();
SecondLevelCacheStatistics cStats = stats.getSecondLevelCacheStatistics( Item.class.getName() + ".items" );
ByRef<Long> otherItemId = new ByRef<>(null);
withTxSession(s -> {
OtherItem otherItem = new OtherItem();
otherItem.setName( "steve" );
s.save( otherItem );
otherItemId.set(otherItem.getId());
});
// create an element for otherItem.bagOfItems
Item item = new Item();
item.setName( "element" );
item.setDescription( "element Item" );
withTxSession(s -> {
OtherItem otherItem = s.get( OtherItem.class, otherItemId.get() );
assertFalse( Hibernate.isInitialized( otherItem.getBagOfItems() ) );
// Add an element to otherItem.bagOfItems (a bag); it will not initialize the bag.
otherItem.addItemToBag( item );
assertFalse( Hibernate.isInitialized( otherItem.getBagOfItems() ) );
s.persist( item );
s.flush();
// Now initialize the collection; it will contain the uncommitted itemElement.
// The many-to-many uses a property-ref
Hibernate.initialize( otherItem.getBagOfItems() );
markRollbackOnly(s);
});
withTxSession(s -> {
OtherItem otherItem = s.get( OtherItem.class, otherItemId.get() );
// Because of HHH-9231, the following will fail due to ObjectNotFoundException because the
// collection will be read from the cache and it still contains the uncommitted element,
// which cannot be found.
Hibernate.initialize( otherItem.getBagOfItems() );
assertTrue( otherItem.getBagOfItems().isEmpty() );
s.delete( otherItem );
});
}
@Test
public void testStaleWritesLeaveCacheConsistent() throws Exception {
Statistics stats = sessionFactory().getStatistics();
stats.clear();
ByRef<VersionedItem> itemRef = new ByRef<>(null);
withTxSession(s -> {
VersionedItem item = new VersionedItem();
item.setName( "steve" );
item.setDescription( "steve's item" );
s.save( item );
itemRef.set(item);
});
final VersionedItem item = itemRef.get();
Long initialVersion = item.getVersion();
// manually revert the version property
item.setVersion( new Long( item.getVersion().longValue() - 1 ) );
try {
withTxSession(s -> s.update(item));
fail("expected stale write to fail");
}
catch (Exception e) {
log.debug("Rollback was expected", e);
}
// check the version value in the cache...
SecondLevelCacheStatistics slcs = stats.getSecondLevelCacheStatistics( VersionedItem.class.getName() );
Object entry = slcs.getEntries().get( item.getId() );
Long cachedVersionValue;
cachedVersionValue = (Long) ((CacheEntry) entry).getVersion();
assertEquals(initialVersion.longValue(), cachedVersionValue.longValue());
withTxSession(s -> {
VersionedItem item2 = s.load( VersionedItem.class, item.getId() );
s.delete( item2 );
});
}
@Test
@TestForIssue( jiraKey = "HHH-5690")
public void testPersistEntityFlushRollbackNotInEntityCache() throws Exception {
Statistics stats = sessionFactory().getStatistics();
stats.clear();
SecondLevelCacheStatistics slcs = stats.getSecondLevelCacheStatistics( Item.class.getName() );
ByRef<Long> itemId = new ByRef<>(null);
withTxSession(s -> {
Item item = new Item();
item.setName("steve");
item.setDescription("steve's item");
s.persist(item);
s.flush();
itemId.set(item.getId());
// assertNotNull( slcs.getEntries().get( item.getId() ) );
markRollbackOnly(s);
});
// item should not be in entity cache.
assertEquals( Collections.EMPTY_MAP, slcs.getEntries() );
withTxSession(s -> {
Item item = s.get( Item.class, itemId.get() );
assertNull( item );
});
}
@Test
@TestForIssue( jiraKey = "HHH-5690")
public void testPersistEntityFlushEvictGetRollbackNotInEntityCache() throws Exception {
Statistics stats = sessionFactory().getStatistics();
stats.clear();
SecondLevelCacheStatistics slcs = stats.getSecondLevelCacheStatistics( Item.class.getName() );
ByRef<Long> itemId = new ByRef<>(null);
withTxSession(s -> {
Item item = new Item();
item.setName("steve");
item.setDescription("steve's item");
s.persist(item);
s.flush();
itemId.set(item.getId());
// item is cached on insert.
// assertNotNull( slcs.getEntries().get( item.getId() ) );
s.evict(item);
assertEquals(slcs.getHitCount(), 0);
item = s.get(Item.class, item.getId());
assertNotNull(item);
// assertEquals( slcs.getHitCount(), 1 );
// assertNotNull( slcs.getEntries().get( item.getId() ) );
markRollbackOnly(s);
});
// item should not be in entity cache.
//slcs = stats.getSecondLevelCacheStatistics( Item.class.getName() );
assertEquals(Collections.EMPTY_MAP, slcs.getEntries() );
withTxSession(s -> {
Item item = s.get(Item.class, itemId.get());
assertNull(item);
});
}
@Test
public void testQueryCacheInvalidation() throws Exception {
Statistics stats = sessionFactory().getStatistics();
stats.clear();
SecondLevelCacheStatistics slcs = stats.getSecondLevelCacheStatistics( Item.class.getName() );
sessionFactory().getCache().evictEntityRegion( Item.class.getName() );
assertEquals(0, slcs.getPutCount());
assertEquals( 0, slcs.getElementCountInMemory() );
assertEquals( 0, slcs.getEntries().size() );
ByRef<Item> itemRef = new ByRef<>(null);
withTxSession(s -> {
Item item = new Item();
item.setName( "widget" );
item.setDescription( "A really top-quality, full-featured widget." );
s.persist( item );
itemRef.set(item);
});
assertEquals( 1, slcs.getPutCount() );
assertEquals( 1, slcs.getElementCountInMemory() );
assertEquals( 1, slcs.getEntries().size() );
withTxSession(s -> {
Item item = s.get( Item.class, itemRef.get().getId() );
assertEquals( slcs.getHitCount(), 1 );
assertEquals( slcs.getMissCount(), 0 );
item.setDescription( "A bog standard item" );
});
assertEquals( slcs.getPutCount(), 2 );
CacheEntry entry = (CacheEntry) slcs.getEntries().get( itemRef.get().getId() );
Serializable[] ser = entry.getDisassembledState();
assertTrue( ser[0].equals( "widget" ) );
assertTrue( ser[1].equals( "A bog standard item" ) );
withTxSession(s -> s.delete(itemRef.get()));
}
@Test
public void testQueryCache() throws Exception {
Statistics stats = sessionFactory().getStatistics();
stats.clear();
Item item = new Item( "chris", "Chris's Item" );
withTxSession(s -> s.persist( item ));
// Delay added to guarantee that query cache results won't be considered
// as not up to date due to persist session and query results from first
// query happening within same 100ms gap.
Thread.sleep( 100 );
withTxSession(s -> s.createQuery( "from Item" ).setCacheable( true ).list());
withTxSession(s -> {
s.createQuery( "from Item" ).setCacheable( true ).list();
assertEquals( 1, stats.getQueryCacheHitCount() );
s.createQuery( "delete from Item" ).executeUpdate();
});
}
@Test
public void testQueryCacheHitInSameTransaction() throws Exception {
Statistics stats = sessionFactory().getStatistics();
stats.clear();
Item item = new Item( "galder", "Galder's Item" );
withTxSession(s -> s.persist( item ));
// Delay added to guarantee that query cache results won't be considered
// as not up to date due to persist session and query results from first
// query happening within same 100ms gap.
Thread.sleep( 100 );
withTxSession(s -> {
s.createQuery("from Item").setCacheable(true).list();
s.createQuery("from Item").setCacheable(true).list();
assertEquals(1, stats.getQueryCacheHitCount());
});
withTxSession(s -> s.createQuery( "delete from Item" ).executeUpdate());
}
@Test
public void testNaturalIdCached() throws Exception {
saveSomeCitizens();
// Clear the cache before the transaction begins
ReadWriteTest.this.cleanupCache();
Thread.sleep(10);
withTxSession(s -> {
State france = ReadWriteTest.this.getState(s, "Ile de France");
Criteria criteria = s.createCriteria( Citizen.class );
criteria.add( Restrictions.naturalId().set( "ssn", "1234" ).set( "state", france ) );
criteria.setCacheable( true );
Statistics stats = sessionFactory().getStatistics();
stats.setStatisticsEnabled( true );
stats.clear();
assertEquals(
"Cache hits should be empty", 0, stats
.getNaturalIdCacheHitCount()
);
// first query
List results = criteria.list();
assertEquals( 1, results.size() );
assertEquals( "NaturalId Cache Hits", 0, stats.getNaturalIdCacheHitCount() );
assertEquals( "NaturalId Cache Misses", 1, stats.getNaturalIdCacheMissCount() );
assertEquals( "NaturalId Cache Puts", 1, stats.getNaturalIdCachePutCount() );
assertEquals( "NaturalId Cache Queries", 1, stats.getNaturalIdQueryExecutionCount() );
// query a second time - result should be cached in session
criteria.list();
assertEquals( "NaturalId Cache Hits", 0, stats.getNaturalIdCacheHitCount() );
assertEquals( "NaturalId Cache Misses", 1, stats.getNaturalIdCacheMissCount() );
assertEquals( "NaturalId Cache Puts", 1, stats.getNaturalIdCachePutCount() );
assertEquals( "NaturalId Cache Queries", 1, stats.getNaturalIdQueryExecutionCount() );
// cleanup
markRollbackOnly(s);
});
}
@Test
public void testNaturalIdLoaderCached() throws Exception {
final Statistics stats = sessionFactory().getStatistics();
stats.setStatisticsEnabled( true );
stats.clear();
assertEquals( "NaturalId Cache Hits", 0, stats.getNaturalIdCacheHitCount() );
assertEquals( "NaturalId Cache Misses", 0, stats.getNaturalIdCacheMissCount() );
assertEquals( "NaturalId Cache Puts", 0, stats.getNaturalIdCachePutCount() );
assertEquals( "NaturalId Cache Queries", 0, stats.getNaturalIdQueryExecutionCount() );
saveSomeCitizens();
assertEquals( "NaturalId Cache Hits", 0, stats.getNaturalIdCacheHitCount() );
assertEquals( "NaturalId Cache Misses", 0, stats.getNaturalIdCacheMissCount() );
assertEquals( "NaturalId Cache Puts", 2, stats.getNaturalIdCachePutCount() );
assertEquals( "NaturalId Cache Queries", 0, stats.getNaturalIdQueryExecutionCount() );
//Try NaturalIdLoadAccess after insert
final Citizen citizen = withTxSessionApply(s -> {
State france = ReadWriteTest.this.getState(s, "Ile de France");
NaturalIdLoadAccess<Citizen> naturalIdLoader = s.byNaturalId(Citizen.class);
naturalIdLoader.using("ssn", "1234").using("state", france);
//Not clearing naturalId caches, should be warm from entity loading
stats.clear();
// first query
Citizen c = naturalIdLoader.load();
assertNotNull(c);
assertEquals("NaturalId Cache Hits", 1, stats.getNaturalIdCacheHitCount());
assertEquals("NaturalId Cache Misses", 0, stats.getNaturalIdCacheMissCount());
assertEquals("NaturalId Cache Puts", 0, stats.getNaturalIdCachePutCount());
assertEquals("NaturalId Cache Queries", 0, stats.getNaturalIdQueryExecutionCount());
// cleanup
markRollbackOnly(s);
return c;
});
// TODO: Clear caches manually via cache manager (it's faster!!)
this.cleanupCache();
Thread.sleep(10);
stats.setStatisticsEnabled( true );
stats.clear();
//Try NaturalIdLoadAccess
withTxSession(s -> {
// first query
Citizen loadedCitizen = (Citizen) s.get( Citizen.class, citizen.getId() );
assertNotNull( loadedCitizen );
assertEquals( "NaturalId Cache Hits", 0, stats.getNaturalIdCacheHitCount() );
assertEquals( "NaturalId Cache Misses", 0, stats.getNaturalIdCacheMissCount() );
assertEquals( "NaturalId Cache Puts", 1, stats.getNaturalIdCachePutCount() );
assertEquals( "NaturalId Cache Queries", 0, stats.getNaturalIdQueryExecutionCount() );
// cleanup
markRollbackOnly(s);
});
// Try NaturalIdLoadAccess after load
withTxSession(s -> {
State france = ReadWriteTest.this.getState(s, "Ile de France");
NaturalIdLoadAccess naturalIdLoader = s.byNaturalId(Citizen.class);
naturalIdLoader.using( "ssn", "1234" ).using( "state", france );
//Not clearing naturalId caches, should be warm from entity loading
stats.setStatisticsEnabled( true );
stats.clear();
// first query
Citizen loadedCitizen = (Citizen) naturalIdLoader.load();
assertNotNull( loadedCitizen );
assertEquals( "NaturalId Cache Hits", 1, stats.getNaturalIdCacheHitCount() );
assertEquals( "NaturalId Cache Misses", 0, stats.getNaturalIdCacheMissCount() );
assertEquals( "NaturalId Cache Puts", 0, stats.getNaturalIdCachePutCount() );
assertEquals( "NaturalId Cache Queries", 0, stats.getNaturalIdQueryExecutionCount() );
// cleanup
markRollbackOnly(s);
});
}
@Test
public void testEntityCacheContentsAfterEvictAll() throws Exception {
final List<Citizen> citizens = saveSomeCitizens();
withTxSession(s -> {
Cache cache = s.getSessionFactory().getCache();
Statistics stats = sessionFactory().getStatistics();
SecondLevelCacheStatistics slcStats = stats.getSecondLevelCacheStatistics(Citizen.class.getName());
assertTrue("2lc entity cache is expected to contain Citizen id = " + citizens.get(0).getId(),
cache.containsEntity(Citizen.class, citizens.get(0).getId()));
assertTrue("2lc entity cache is expected to contain Citizen id = " + citizens.get(1).getId(),
cache.containsEntity(Citizen.class, citizens.get(1).getId()));
assertEquals(2, slcStats.getPutCount());
cache.evictEntityRegions();
Thread.sleep(10);
assertEquals(0, slcStats.getElementCountInMemory());
assertFalse("2lc entity cache is expected to not contain Citizen id = " + citizens.get(0).getId(),
cache.containsEntity(Citizen.class, citizens.get(0).getId()));
assertFalse("2lc entity cache is expected to not contain Citizen id = " + citizens.get(1).getId(),
cache.containsEntity(Citizen.class, citizens.get(1).getId()));
Citizen citizen = s.load(Citizen.class, citizens.get(0).getId());
assertNotNull(citizen);
assertNotNull(citizen.getFirstname()); // proxy gets resolved
assertEquals(1, slcStats.getMissCount());
// cleanup
markRollbackOnly(s);
});
}
@Test
public void testMultipleEvictAll() throws Exception {
final List<Citizen> citizens = saveSomeCitizens();
withTxSession(s -> {
Cache cache = s.getSessionFactory().getCache();
cache.evictEntityRegions();
cache.evictEntityRegions();
});
withTxSession(s -> {
Cache cache = s.getSessionFactory().getCache();
cache.evictEntityRegions();
s.delete(s.load(Citizen.class, citizens.get(0).getId()));
s.delete(s.load(Citizen.class, citizens.get(1).getId()));
});
}
private List<Citizen> saveSomeCitizens() throws Exception {
final Citizen c1 = new Citizen();
c1.setFirstname( "Emmanuel" );
c1.setLastname( "Bernard" );
c1.setSsn( "1234" );
final State france = new State();
france.setName( "Ile de France" );
c1.setState( france );
final Citizen c2 = new Citizen();
c2.setFirstname( "Gavin" );
c2.setLastname( "King" );
c2.setSsn( "000" );
final State australia = new State();
australia.setName( "Australia" );
c2.setState( australia );
withTxSession(s -> {
s.persist( australia );
s.persist( france );
s.persist( c1 );
s.persist( c2 );
});
List<Citizen> citizens = new ArrayList<>(2);
citizens.add(c1);
citizens.add(c2);
return citizens;
}
private State getState(Session s, String name) {
Criteria criteria = s.createCriteria( State.class );
criteria.add( Restrictions.eq("name", name) );
criteria.setCacheable(true);
return (State) criteria.list().get( 0 );
}
}

View File

@ -0,0 +1,64 @@
package org.hibernate.test.cache.infinispan.functional;
import org.hibernate.Session;
import org.hibernate.SessionBuilder;
import org.hibernate.cache.infinispan.util.Caches;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.engine.transaction.jta.platform.spi.JtaPlatform;
import org.hibernate.test.cache.infinispan.util.TxUtil;
import javax.transaction.TransactionManager;
import java.util.concurrent.Callable;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
/**
* @author Radim Vansa &lt;rvansa@redhat.com&gt;
*/
public abstract class SingleNodeTest extends AbstractFunctionalTest {
@Override
protected void afterSessionFactoryBuilt(SessionFactoryImplementor sessionFactory) {
super.afterSessionFactoryBuilt(sessionFactory);
JtaPlatform jtaPlatform = sessionFactory().getServiceRegistry().getService(JtaPlatform.class);
if (jtaPlatformClass != null) {
assertNotNull(jtaPlatform);
assertEquals(jtaPlatformClass, jtaPlatform.getClass());
}
}
protected void withTxSession(TxUtil.ThrowingConsumer<Session, Exception> consumer) throws Exception {
withTxSession(sessionFactory().withOptions(), consumer);
}
protected void withTxSession(SessionBuilder sessionBuilder, TxUtil.ThrowingConsumer<Session, Exception> consumer) throws Exception {
JtaPlatform jtaPlatform = useJta ? sessionFactory().getServiceRegistry().getService(JtaPlatform.class) : null;
TxUtil.withTxSession(jtaPlatform, sessionBuilder, consumer);
}
protected <T> T withTxSessionApply(TxUtil.ThrowingFunction<Session, T, Exception> function) throws Exception {
JtaPlatform jtaPlatform = useJta ? sessionFactory().getServiceRegistry().getService(JtaPlatform.class) : null;
return TxUtil.withTxSessionApply(jtaPlatform, sessionFactory().withOptions(), function);
}
protected <T> T withTx(Callable<T> callable) throws Exception {
if (useJta) {
TransactionManager tm = sessionFactory().getServiceRegistry().getService(JtaPlatform.class).retrieveTransactionManager();
return Caches.withinTx(tm, () -> callable.call());
} else {
return callable.call();
}
}
public <E extends Throwable> void withSession(TxUtil.ThrowingConsumer<Session, E> consumer) throws E {
TxUtil.withSession(sessionFactory().withOptions(), consumer);
}
public <R, E extends Throwable> R withSessionApply(TxUtil.ThrowingFunction<Session, R, E> function) throws E {
return TxUtil.withSessionApply(sessionFactory().withOptions(), function);
}
}

View File

@ -1,158 +0,0 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
package org.hibernate.test.cache.infinispan.functional;
import java.util.Map;
import javax.transaction.Status;
import javax.transaction.TransactionManager;
import org.hibernate.cache.infinispan.InfinispanRegionFactory;
import org.hibernate.cache.spi.RegionFactory;
import org.hibernate.cfg.AvailableSettings;
import org.hibernate.cfg.Environment;
import org.hibernate.engine.jdbc.connections.spi.ConnectionProvider;
import org.hibernate.engine.transaction.jta.platform.spi.JtaPlatform;
import org.hibernate.resource.transaction.TransactionCoordinatorBuilder;
import org.hibernate.resource.transaction.backend.jta.internal.JtaTransactionCoordinatorBuilderImpl;
import org.hibernate.test.cache.infinispan.util.InfinispanTestingSetup;
import org.hibernate.testing.junit4.BaseNonConfigCoreFunctionalTestCase;
import org.hibernate.test.cache.infinispan.tm.JtaPlatformImpl;
import org.junit.Before;
import org.infinispan.configuration.parsing.ConfigurationBuilderHolder;
import org.infinispan.manager.EmbeddedCacheManager;
import org.infinispan.test.fwk.TestCacheManagerFactory;
import org.infinispan.util.logging.Log;
import org.infinispan.util.logging.LogFactory;
import org.junit.ClassRule;
/**
* @author Galder Zamarreño
* @since 3.5
*/
public abstract class SingleNodeTestCase extends BaseNonConfigCoreFunctionalTestCase {
private static final Log log = LogFactory.getLog( SingleNodeTestCase.class );
protected TransactionManager tm;
@ClassRule
public static final InfinispanTestingSetup infinispanTestIdentifier = new InfinispanTestingSetup();
@Before
public void prepare() {
tm = getTransactionManager();
}
protected TransactionManager getTransactionManager() {
try {
Class<? extends JtaPlatform> jtaPlatformClass = getJtaPlatform();
if ( jtaPlatformClass == null ) {
return null;
}
else {
return jtaPlatformClass.newInstance().retrieveTransactionManager();
}
}
catch (Exception e) {
log.error( "Error", e );
throw new RuntimeException( e );
}
}
@Override
public String[] getMappings() {
return new String[] {
"cache/infinispan/functional/Item.hbm.xml",
"cache/infinispan/functional/Customer.hbm.xml",
"cache/infinispan/functional/Contact.hbm.xml"
};
}
@Override
public String getCacheConcurrencyStrategy() {
return "transactional";
}
protected Class<? extends RegionFactory> getCacheRegionFactory() {
return TestInfinispanRegionFactory.class;
}
protected Class<? extends TransactionCoordinatorBuilder> getTransactionCoordinatorBuilder() {
return JtaTransactionCoordinatorBuilderImpl.class;
}
protected Class<? extends ConnectionProvider> getConnectionProviderClass() {
return org.hibernate.test.cache.infinispan.tm.XaConnectionProvider.class;
}
protected Class<? extends JtaPlatform> getJtaPlatform() {
return JtaPlatformImpl.class;
}
protected boolean getUseQueryCache() {
return true;
}
@Override
@SuppressWarnings("unchecked")
protected void addSettings(Map settings) {
super.addSettings( settings );
settings.put( Environment.USE_SECOND_LEVEL_CACHE, "true" );
settings.put( Environment.GENERATE_STATISTICS, "true" );
settings.put( Environment.USE_QUERY_CACHE, String.valueOf( getUseQueryCache() ) );
settings.put( Environment.CACHE_REGION_FACTORY, getCacheRegionFactory().getName() );
if ( getJtaPlatform() != null ) {
settings.put( AvailableSettings.JTA_PLATFORM, getJtaPlatform() );
}
settings.put( Environment.TRANSACTION_COORDINATOR_STRATEGY, getTransactionCoordinatorBuilder().getName() );
settings.put( Environment.CONNECTION_PROVIDER, getConnectionProviderClass().getName() );
}
protected void beginTx() throws Exception {
tm.begin();
}
protected void setRollbackOnlyTx() throws Exception {
tm.setRollbackOnly();
}
protected void setRollbackOnlyTx(Exception e) throws Exception {
log.error( "Error", e );
tm.setRollbackOnly();
throw e;
}
protected void setRollbackOnlyTxExpected(Exception e) throws Exception {
log.debug( "Expected behaivour", e );
tm.setRollbackOnly();
}
protected void commitOrRollbackTx() throws Exception {
if ( tm.getStatus() == Status.STATUS_ACTIVE ) {
tm.commit();
}
else {
tm.rollback();
}
}
public static class TestInfinispanRegionFactory extends InfinispanRegionFactory {
public TestInfinispanRegionFactory() {
super(); // For reflection-based instantiation
}
@Override
protected EmbeddedCacheManager createCacheManager(ConfigurationBuilderHolder holder) {
return TestCacheManagerFactory.createClusteredCacheManager(holder);
}
}
}

View File

@ -1,415 +0,0 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
package org.hibernate.test.cache.infinispan.functional.bulk;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.transaction.Status;
import javax.transaction.TransactionManager;
import org.hibernate.FlushMode;
import org.hibernate.Session;
import org.hibernate.cache.spi.RegionFactory;
import org.hibernate.cfg.AvailableSettings;
import org.hibernate.cfg.Environment;
import org.hibernate.engine.jdbc.connections.spi.ConnectionProvider;
import org.hibernate.engine.transaction.jta.platform.spi.JtaPlatform;
import org.hibernate.resource.transaction.TransactionCoordinatorBuilder;
import org.hibernate.resource.transaction.backend.jta.internal.JtaTransactionCoordinatorBuilderImpl;
import org.hibernate.stat.SecondLevelCacheStatistics;
import org.hibernate.test.cache.infinispan.util.InfinispanTestingSetup;
import org.hibernate.testing.junit4.BaseNonConfigCoreFunctionalTestCase;
import org.hibernate.test.cache.infinispan.functional.Contact;
import org.hibernate.test.cache.infinispan.functional.Customer;
import org.hibernate.test.cache.infinispan.functional.SingleNodeTestCase;
import org.hibernate.test.cache.infinispan.tm.JtaPlatformImpl;
import org.junit.ClassRule;
import org.junit.Test;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
/**
* BulkOperationsTestCase.
*
* @author Galder Zamarreño
* @since 3.5
*/
public class BulkOperationsTestCase extends BaseNonConfigCoreFunctionalTestCase {
@ClassRule
public static final InfinispanTestingSetup infinispanTestIdentifier = new InfinispanTestingSetup();
private TransactionManager tm;
@Override
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<? extends RegionFactory> getCacheRegionFactory() {
return SingleNodeTestCase.TestInfinispanRegionFactory.class;
}
protected Class<? extends TransactionCoordinatorBuilder> getTransactionCoordinatorBuilder() {
return JtaTransactionCoordinatorBuilderImpl.class;
}
protected Class<? extends ConnectionProvider> getConnectionProviderClass() {
return org.hibernate.test.cache.infinispan.tm.XaConnectionProvider.class;
}
protected JtaPlatform getJtaPlatform() {
return new JtaPlatformImpl();
}
@Override
@SuppressWarnings("unchecked")
protected void addSettings(Map settings) {
super.addSettings( settings );
settings.put( Environment.USE_SECOND_LEVEL_CACHE, "true" );
settings.put( Environment.USE_QUERY_CACHE, "false" );
settings.put( Environment.GENERATE_STATISTICS, "true" );
settings.put( Environment.CACHE_REGION_FACTORY, getCacheRegionFactory().getName() );
settings.put( Environment.TRANSACTION_COORDINATOR_STRATEGY, getTransactionCoordinatorBuilder().getName() );
settings.put( AvailableSettings.JTA_PLATFORM, getJtaPlatform() );
settings.put( Environment.CONNECTION_PROVIDER, getConnectionProviderClass().getName() );
}
@Test
public void testBulkOperations() throws Throwable {
boolean cleanedUp = false;
try {
tm = getJtaPlatform().retrieveTransactionManager();
createContacts();
List<Integer> rhContacts = getContactsByCustomer( "Red Hat" );
assertNotNull( "Red Hat contacts exist", rhContacts );
assertEquals( "Created expected number of Red Hat contacts", 10, rhContacts.size() );
SecondLevelCacheStatistics contactSlcs = sessionFactory()
.getStatistics()
.getSecondLevelCacheStatistics( Contact.class.getName() );
assertEquals( 20, contactSlcs.getElementCountInMemory() );
assertEquals( "Deleted all Red Hat contacts", 10, deleteContacts() );
assertEquals( 0, contactSlcs.getElementCountInMemory() );
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" );
assertEquals( 0, contactSlcs.getElementCountInMemory() );
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());
assertEquals( 10, contactSlcs.getElementCountInMemory() );
updateContactsWithOneManual( "Kabir", "UpdatedAgain" );
assertEquals( 0, contactSlcs.getElementCountInMemory());
for ( Integer id : jbContacts ) {
Contact contact = getContact( id );
assertNotNull( "JBoss contact " + id + " exists", contact );
String expected = ("Kabir".equals( contact.getName() )) ? "UpdatedAgain" : "2222";
assertEquals( "JBoss contact " + id + " has correct TLF", expected, contact.getTlf() );
}
updated = getContactsByTLF( "UpdatedAgain" );
assertNotNull( "Got updated contacts", updated );
assertEquals( "Updated contacts", 5, updated.size() );
}
catch (Throwable t) {
cleanedUp = true;
cleanup( true );
throw t;
}
finally {
// cleanup the db so we can run this test multiple times w/o restarting the cluster
if ( !cleanedUp ) {
cleanup( false );
}
}
}
public void createContacts() throws Exception {
tm.begin();
try {
for ( int i = 0; i < 10; i++ ) {
createCustomer( i );
}
}
catch (Exception e) {
tm.setRollbackOnly();
throw e;
}
finally {
if ( tm.getStatus() == Status.STATUS_ACTIVE ) {
tm.commit();
}
else {
tm.rollback();
}
}
}
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 = sessionFactory().getCurrentSession();
int rowsAffected = session.createQuery( deleteHQL ).setFlushMode( FlushMode.AUTO )
.setParameter( "cName", "Red Hat" ).executeUpdate();
tm.commit();
return rowsAffected;
}
catch (Exception e) {
tm.setRollbackOnly();
throw e;
}
finally {
if ( tm.getStatus() == Status.STATUS_ACTIVE ) {
tm.commit();
}
else {
try {
tm.rollback();
}
catch (Exception ee) {
// ignored
}
}
}
}
@SuppressWarnings( {"unchecked"})
public List<Integer> getContactsByCustomer(String customerName) throws Exception {
String selectHQL = "select contact.id from Contact contact";
selectHQL += " where contact.customer.name = :cName";
tm.begin();
try {
Session session = sessionFactory().getCurrentSession();
return session.createQuery( selectHQL )
.setFlushMode( FlushMode.AUTO )
.setParameter( "cName", customerName )
.list();
}
catch (Exception e) {
tm.setRollbackOnly();
throw e;
}
finally {
if ( tm.getStatus() == Status.STATUS_ACTIVE ) {
tm.commit();
}
else {
tm.rollback();
}
}
}
@SuppressWarnings( {"unchecked"})
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 = sessionFactory().getCurrentSession();
return session.createQuery( selectHQL )
.setFlushMode( FlushMode.AUTO )
.setParameter( "cTLF", tlf )
.list();
}
catch (Exception e) {
tm.setRollbackOnly();
throw e;
}
finally {
if ( tm.getStatus() == Status.STATUS_ACTIVE ) {
tm.commit();
}
else {
tm.rollback();
}
}
}
public int updateContacts(String name, String newTLF) throws Exception {
String updateHQL = "update Contact set tlf = :cNewTLF where name = :cName";
tm.begin();
try {
Session session = sessionFactory().getCurrentSession();
return session.createQuery( updateHQL )
.setFlushMode( FlushMode.AUTO )
.setParameter( "cNewTLF", newTLF )
.setParameter( "cName", name )
.executeUpdate();
}
catch (Exception e) {
tm.setRollbackOnly();
throw e;
}
finally {
if ( tm.getStatus() == Status.STATUS_ACTIVE ) {
tm.commit();
}
else {
tm.rollback();
}
}
}
public int updateContactsWithOneManual(String name, String newTLF) throws Exception {
String queryHQL = "from Contact c where c.name = :cName";
String updateHQL = "update Contact set tlf = :cNewTLF where name = :cName";
tm.begin();
try {
Session session = sessionFactory().getCurrentSession();
@SuppressWarnings("unchecked")
List<Contact> list = session.createQuery( queryHQL ).setParameter( "cName", name ).list();
list.get( 0 ).setTlf( newTLF );
return session.createQuery( updateHQL )
.setFlushMode( FlushMode.AUTO )
.setParameter( "cNewTLF", newTLF )
.setParameter( "cName", name )
.executeUpdate();
}
catch (Exception e) {
tm.setRollbackOnly();
throw e;
}
finally {
if ( tm.getStatus() == Status.STATUS_ACTIVE ) {
tm.commit();
}
else {
tm.rollback();
}
}
}
public Contact getContact(Integer id) throws Exception {
tm.begin();
try {
Session session = sessionFactory().getCurrentSession();
return (Contact) session.get( Contact.class, id );
}
catch (Exception e) {
tm.setRollbackOnly();
throw e;
}
finally {
if ( tm.getStatus() == Status.STATUS_ACTIVE ) {
tm.commit();
}
else {
tm.rollback();
}
}
}
public void cleanup(boolean ignore) throws Exception {
String deleteContactHQL = "delete from Contact";
String deleteCustomerHQL = "delete from Customer";
tm.begin();
try {
Session session = sessionFactory().getCurrentSession();
session.createQuery( deleteContactHQL ).setFlushMode( FlushMode.AUTO ).executeUpdate();
session.createQuery( deleteCustomerHQL ).setFlushMode( FlushMode.AUTO ).executeUpdate();
}
catch (Exception e) {
tm.setRollbackOnly();
throw e;
}
finally {
if ( tm.getStatus() == Status.STATUS_ACTIVE ) {
tm.commit();
}
else {
if ( !ignore ) {
try {
tm.rollback();
}
catch (Exception ee) {
// ignored
}
}
}
}
}
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" );
}
}
}

View File

@ -1,106 +0,0 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
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;
}
@Override
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;
}
@Override
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)));
}
}

View File

@ -1,89 +0,0 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
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;
}
@Override
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;
}
@Override
public int hashCode() {
int result = 17;
result = result * 31 + lastName.hashCode();
result = result * 31 + ssn.hashCode();
return result;
}
@Override
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;
}
}

View File

@ -1,93 +0,0 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
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;
import org.jboss.logging.Logger;
@Listener
public class CacheAccessListener {
private static final Logger log = Logger.getLogger( CacheAccessListener.class );
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();
log.info("Modified node " + key);
modified.add(key.toString());
}
}
@CacheEntryCreated
public void nodeCreated( CacheEntryCreatedEvent event ) {
if (!event.isPre()) {
Object key = event.getKey();
log.info("Created node " + key);
modified.add(key.toString());
}
}
@CacheEntryVisited
public void nodeVisited( CacheEntryVisitedEvent event ) {
if (!event.isPre()) {
Object key = event.getKey();
log.info("Visited node " + key);
accessed.add(key.toString());
}
}
public boolean getSawRegionModification( Object key ) {
return getSawRegion(key, modified);
}
public int getSawRegionModificationCount() {
return modified.size();
}
public void clearSawRegionModification() {
modified.clear();
}
public boolean getSawRegionAccess( Object key ) {
return getSawRegion(key, accessed);
}
public int getSawRegionAccessCount() {
return accessed.size();
}
public void clearSawRegionAccess() {
accessed.clear();
}
private boolean getSawRegion( Object key,
Set sawEvents ) {
if (sawEvents.contains(key)) {
sawEvents.remove(key);
return true;
}
return false;
}
}

View File

@ -1,275 +0,0 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
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.infinispan.util.logging.Log;
import org.infinispan.util.logging.LogFactory;
import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
/**
* Comment
*
* @author Brian Stansberry
*/
public class ClassLoaderTestDAO {
private static final Log log = LogFactory.getLog(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);
Session session = sessionFactory.openSession();
try {
return (Account) session.get(acctClass, id);
} finally {
session.close();
}
}
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);
}
}
}

View File

@ -1,58 +0,0 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
package org.hibernate.test.cache.infinispan.functional.classloader;
import junit.framework.Test;
import org.hibernate.test.cache.infinispan.functional.cluster.ClusterAwareRegionFactory;
import org.hibernate.test.cache.infinispan.functional.cluster.DualNodeJtaTransactionManagerImpl;
/**
* A TestSetup that uses SelectedClassnameClassLoader to ensure that certain classes are not visible
* to Infinispan or JGroups' classloader.
*
* @author Galder Zamarreño
*/
public class IsolatedCacheTestSetup extends SelectedClassnameClassLoaderTestSetup {
private String[] isolatedClasses;
private String cacheConfig;
/**
* Create a new IsolatedCacheTestSetup.
*/
public IsolatedCacheTestSetup(Test test, String[] isolatedClasses) {
super(test, null, null, isolatedClasses);
this.isolatedClasses = isolatedClasses;
}
@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();
// 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();
}
}
}

View File

@ -1,349 +0,0 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
package org.hibernate.test.cache.infinispan.functional.classloader;
import java.util.Map;
import javax.transaction.TransactionManager;
import org.hibernate.SessionFactory;
import org.hibernate.cache.infinispan.InfinispanRegionFactory;
import org.hibernate.cache.internal.StandardQueryCache;
import org.hibernate.test.cache.infinispan.functional.cluster.ClusterAwareRegionFactory;
import org.hibernate.test.cache.infinispan.functional.cluster.DualNodeJtaTransactionManagerImpl;
import org.hibernate.test.cache.infinispan.functional.cluster.DualNodeTestCase;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Ignore;
import org.junit.Test;
import org.infinispan.Cache;
import org.infinispan.manager.CacheContainer;
import org.infinispan.manager.EmbeddedCacheManager;
import org.jboss.logging.Logger;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.fail;
/**
* 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 DualNodeTestCase {
private static final Logger log = Logger.getLogger( IsolatedClassLoaderTest.class );
protected static final long SLEEP_TIME = 300L;
private Cache localQueryCache;
private CacheAccessListener localQueryListener;
private Cache remoteQueryCache;
private CacheAccessListener remoteQueryListener;
private static ClassLoader originalTCCL;
// private static ClassLoader visibleClassesCl;
@BeforeClass
public static void prepareClassLoader() {
final String packageName = IsolatedClassLoaderTest.class.getPackage().getName();
final String[] classes = new String[] {packageName + ".Account", packageName + ".AccountHolder"};
originalTCCL = Thread.currentThread().getContextClassLoader();
// Most likely, it will point to system classloader
ClassLoader parent = originalTCCL == null ? IsolatedClassLoaderTest.class.getClassLoader() : originalTCCL;
// First, create a classloader where classes won't be found
ClassLoader selectedTCCL = new SelectedClassnameClassLoader( null, null, classes, parent );
// Now, make the class visible to the test driver
SelectedClassnameClassLoader visible = new SelectedClassnameClassLoader( classes, null, null, selectedTCCL );
Thread.currentThread().setContextClassLoader( visible );
// visibleClassesCl = new SelectedClassnameClassLoader(classes, null, null, selectedTCCL);
// Thread.currentThread().setContextClassLoader(selectedTCCL);
}
@AfterClass
public static void resetClassLoader() {
ClusterAwareRegionFactory.clearCacheManagers();
DualNodeJtaTransactionManagerImpl.cleanupTransactions();
DualNodeJtaTransactionManagerImpl.cleanupTransactionManagers();
Thread.currentThread().setContextClassLoader( originalTCCL );
}
@Override
public String[] getMappings() {
return new String[] {"cache/infinispan/functional/classloader/Account.hbm.xml"};
}
@Override
@SuppressWarnings("unchecked")
protected void applyStandardSettings(Map settings) {
super.applyStandardSettings( settings );
settings.put( InfinispanRegionFactory.QUERY_CACHE_RESOURCE_PROP, "replicated-query" );
settings.put( "hibernate.cache.infinispan.AccountRegion.cfg", "replicated-query" );
}
@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 {
// Clear the local account cache
sessionFactory().getCache().evictEntityRegion( Account.class.getName() );
if ( localQueryCache != null && localQueryListener != null ) {
localQueryCache.removeListener( localQueryListener );
}
if ( remoteQueryCache != null && remoteQueryListener != null ) {
remoteQueryCache.removeListener( remoteQueryListener );
}
}
finally {
super.cleanupTest();
}
}
@Ignore("Infinispan caches now use whichever classloader is associated on " +
"construction, i.e. deployment JPA app, so does not rely on TCCL.")
@Test
public void testIsolatedSetup() throws Exception {
// Bind a listener to the "local" cache
// Our region factory makes its CacheManager available to us
CacheContainer localManager = ClusterAwareRegionFactory.getCacheManager( DualNodeTestCase.LOCAL );
Cache localReplicatedCache = localManager.getCache( "replicated-entity" );
// Bind a listener to the "remote" cache
CacheContainer remoteManager = ClusterAwareRegionFactory.getCacheManager( DualNodeTestCase.REMOTE );
Cache remoteReplicatedCache = remoteManager.getCache( "replicated-entity" );
ClassLoader cl = Thread.currentThread().getContextClassLoader();
Thread.currentThread().setContextClassLoader( cl.getParent() );
log.info( "TCCL is " + cl.getParent() );
Account acct = new Account();
acct.setAccountHolder( new AccountHolder() );
try {
localReplicatedCache.put( "isolated1", acct );
// With lazy deserialization, retrieval in remote forces class resolution
remoteReplicatedCache.get( "isolated1" );
fail( "Should not have succeeded in putting acct -- classloader not isolated" );
}
catch (Exception e) {
if ( e.getCause() instanceof ClassNotFoundException ) {
log.info( "Caught exception as desired", e );
}
else {
throw new IllegalStateException( "Unexpected exception", e );
}
}
Thread.currentThread().setContextClassLoader( cl );
log.info( "TCCL is " + cl );
localReplicatedCache.put( "isolated2", acct );
assertEquals( acct.getClass().getName(), remoteReplicatedCache.get( "isolated2" ).getClass().getName() );
}
@Ignore("Infinispan caches now use whichever classloader is associated on " +
"construction, i.e. deployment JPA app, so does not rely on TCCL.")
@Test
public void testClassLoaderHandlingNamedQueryRegion() throws Exception {
rebuildSessionFactory();
queryTest( true );
}
@Ignore("Infinispan caches now use whichever classloader is associated on " +
"construction, i.e. deployment JPA app, so does not rely on TCCL.")
@Test
public void testClassLoaderHandlingStandardQueryCache() throws Exception {
rebuildSessionFactory();
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
EmbeddedCacheManager localManager = ClusterAwareRegionFactory.getCacheManager( DualNodeTestCase.LOCAL );
// Bind a listener to the "remote" cache
EmbeddedCacheManager remoteManager = ClusterAwareRegionFactory.getCacheManager( DualNodeTestCase.REMOTE );
String cacheName;
if ( useNamedRegion ) {
cacheName = "AccountRegion"; // As defined by ClassLoaderTestDAO via calls to query.setCacheRegion
// Define cache configurations for region early to avoid ending up with local caches for this region
localManager.defineConfiguration(
cacheName,
localManager.getCacheConfiguration( "replicated-query" )
);
remoteManager.defineConfiguration(
cacheName,
remoteManager.getCacheConfiguration( "replicated-query" )
);
}
else {
cacheName = "replicated-query";
}
localQueryCache = localManager.getCache( cacheName );
localQueryListener = new CacheAccessListener();
localQueryCache.addListener( localQueryListener );
TransactionManager localTM = DualNodeJtaTransactionManagerImpl.getInstance( DualNodeTestCase.LOCAL );
remoteQueryCache = remoteManager.getCache( cacheName );
remoteQueryListener = new CacheAccessListener();
remoteQueryCache.addListener( remoteQueryListener );
TransactionManager remoteTM = DualNodeJtaTransactionManagerImpl.getInstance( DualNodeTestCase.REMOTE );
SessionFactory localFactory = sessionFactory();
SessionFactory remoteFactory = secondNodeEnvironment().getSessionFactory();
ClassLoaderTestDAO dao0 = new ClassLoaderTestDAO( localFactory, localTM );
ClassLoaderTestDAO dao1 = new ClassLoaderTestDAO( remoteFactory, remoteTM );
// Initial ops on node 0
setupEntities( dao0 );
String branch = "63088";
// Query on post code count
assertEquals( branch + " has correct # of accounts", 6, dao0.getCountForBranch( branch, useNamedRegion ) );
assertEquals( "Query cache used", 1, localQueryListener.getSawRegionModificationCount() );
localQueryListener.clearSawRegionModification();
// log.info("First query (get count for branch + " + branch + " ) on node0 done, contents of local query cache are: " + TestingUtil.printCache(localQueryCache));
// Sleep a bit to allow async repl to happen
sleep( SLEEP_TIME );
assertEquals( "Query cache used", 1, remoteQueryListener.getSawRegionModificationCount() );
remoteQueryListener.clearSawRegionModification();
// Do query again from node 1
log.info( "Repeat first query (get count for branch + " + branch + " ) on remote node" );
assertEquals( "63088 has correct # of accounts", 6, dao1.getCountForBranch( branch, useNamedRegion ) );
assertEquals( "Query cache used", 1, remoteQueryListener.getSawRegionModificationCount() );
remoteQueryListener.clearSawRegionModification();
sleep( SLEEP_TIME );
assertEquals( "Query cache used", 1, localQueryListener.getSawRegionModificationCount() );
localQueryListener.clearSawRegionModification();
log.info( "First query on node 1 done" );
// Sleep a bit to allow async repl to happen
sleep( SLEEP_TIME );
// Do some more queries on node 0
log.info( "Do query Smith's branch" );
assertEquals( "Correct branch for Smith", "94536", dao0.getBranch( dao0.getSmith(), useNamedRegion ) );
log.info( "Do query Jone's balance" );
assertEquals( "Correct high balances for Jones", 40, dao0.getTotalBalance( dao0.getJones(), useNamedRegion ) );
assertEquals( "Query cache used", 2, localQueryListener.getSawRegionModificationCount() );
localQueryListener.clearSawRegionModification();
// // 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
assertEquals( "Query cache remotely modified", 2, remoteQueryListener.getSawRegionModificationCount() );
remoteQueryListener.clearSawRegionModification();
log.info( "Repeat second set of queries on node1" );
// Do queries again from node 1
log.info( "Again query Smith's branch" );
assertEquals( "Correct branch for Smith", "94536", dao1.getBranch( dao1.getSmith(), useNamedRegion ) );
log.info( "Again query Jone's balance" );
assertEquals( "Correct high balances for Jones", 40, dao1.getTotalBalance( dao1.getJones(), useNamedRegion ) );
// Should be no change; query was already there
assertEquals( "Query cache modified", 0, remoteQueryListener.getSawRegionModificationCount() );
assertEquals( "Query cache accessed", 2, remoteQueryListener.getSawRegionAccessCount() );
remoteQueryListener.clearSawRegionAccess();
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" );
}
}

View File

@ -1,53 +0,0 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
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@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();
}
}

View File

@ -0,0 +1,179 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
package org.hibernate.test.cache.infinispan.functional.cluster;
import java.util.Iterator;
import java.util.List;
import org.hibernate.test.cache.infinispan.functional.entities.Account;
import org.hibernate.test.cache.infinispan.functional.entities.AccountHolder;
import org.infinispan.util.logging.Log;
import org.infinispan.util.logging.LogFactory;
import org.hibernate.Query;
import org.hibernate.SessionFactory;
import static org.hibernate.test.cache.infinispan.util.TxUtil.withTxSession;
import static org.hibernate.test.cache.infinispan.util.TxUtil.withTxSessionApply;
/**
* @author Brian Stansberry
*/
public class AccountDAO {
private static final Log log = LogFactory.getLog(AccountDAO.class);
private final boolean useJta;
private final SessionFactory sessionFactory;
private AccountHolder smith = new AccountHolder("Smith", "1000");
private AccountHolder jones = new AccountHolder("Jones", "2000");
private AccountHolder barney = new AccountHolder("Barney", "3000");
public AccountDAO(boolean useJta, SessionFactory sessionFactory) throws Exception {
this.useJta = useJta;
this.sessionFactory = sessionFactory;
}
public AccountHolder getSmith() {
return smith;
}
public AccountHolder getJones() {
return jones;
}
public AccountHolder getBarney() {
return barney;
}
public void updateAccountBranch(Integer id, String branch) throws Exception {
withTxSession(useJta, sessionFactory, session -> {
log.debug("Updating account " + id + " to branch " + branch);
Account account = session.get(Account.class, id);
log.debug("Set branch " + branch);
account.setBranch(branch);
session.update(account);
log.debug("Updated account " + id + " to branch " + branch);
});
}
public int getCountForBranch(String branch, boolean useRegion) throws Exception {
return withTxSessionApply(useJta, sessionFactory, session -> {
Query query = session.createQuery(
"select account from Account as account where account.branch = :branch");
query.setString("branch", branch);
if (useRegion) {
query.setCacheRegion("AccountRegion");
}
query.setCacheable(true);
return query.list().size();
});
}
public void createAccount(AccountHolder holder, Integer id, Integer openingBalance, String branch) throws Exception {
withTxSession(useJta, sessionFactory, session -> {
log.debug("Creating account " + id);
Account account = new Account();
account.setId(id);
account.setAccountHolder(holder);
account.setBalance(openingBalance);
log.debug("Set branch " + branch);
account.setBranch(branch);
session.persist(account);
log.debug("Created account " + id);
});
}
public Account getAccount(Integer id) throws Exception {
return withTxSessionApply(useJta, sessionFactory, session -> {
log.debug("Getting account " + id);
return session.get(Account.class, id);
});
}
public Account getAccountWithRefresh(Integer id) throws Exception {
return withTxSessionApply(useJta, sessionFactory, session -> {
log.debug("Getting account " + id + " with refresh");
Account acct = session.get(Account.class, id);
session.refresh(acct);
return session.get(Account.class, id);
});
}
public void updateAccountBalance(Integer id, Integer newBalance) throws Exception {
withTxSession(useJta, sessionFactory, session -> {
log.debug("Updating account " + id + " to balance " + newBalance);
Account account = session.get(Account.class, id);
account.setBalance(newBalance);
session.update(account);
log.debug("Updated account " + id + " to balance " + newBalance);
});
}
public String getBranch(Object holder, boolean useRegion) throws Exception {
return withTxSessionApply(useJta, sessionFactory, session -> {
Query query = session.createQuery(
"select account.branch from Account as account where account.accountHolder = ?");
query.setParameter(0, holder);
if (useRegion) {
query.setCacheRegion("AccountRegion");
}
query.setCacheable(true);
return (String) query.list().get(0);
});
}
public int getTotalBalance(AccountHolder holder, boolean useRegion) throws Exception {
List results = (List) withTxSessionApply(useJta, sessionFactory, session -> {
Query query = session.createQuery(
"select account.balance from Account as account where account.accountHolder = ?");
query.setParameter(0, holder);
if (useRegion) {
query.setCacheRegion("AccountRegion");
}
query.setCacheable(true);
return query.list();
});
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 {
withTxSession(useJta, sessionFactory, session -> {
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) {
}
}
}
});
}
public void remove() {
try {
internalCleanup();
} catch (Exception e) {
log.error("Caught exception in remove", e);
}
}
}

View File

@ -21,8 +21,7 @@ import org.hibernate.cache.spi.RegionFactory;
import org.hibernate.cache.spi.TimestampsRegion; import org.hibernate.cache.spi.TimestampsRegion;
import org.hibernate.cache.spi.access.AccessType; import org.hibernate.cache.spi.access.AccessType;
import org.hibernate.test.cache.infinispan.functional.SingleNodeTestCase; import org.hibernate.internal.util.ReflectHelper;
import org.infinispan.manager.EmbeddedCacheManager; import org.infinispan.manager.EmbeddedCacheManager;
import org.infinispan.util.logging.Log; import org.infinispan.util.logging.Log;
import org.infinispan.util.logging.LogFactory; import org.infinispan.util.logging.LogFactory;
@ -35,84 +34,88 @@ import org.infinispan.util.logging.LogFactory;
*/ */
public class ClusterAwareRegionFactory implements RegionFactory { public class ClusterAwareRegionFactory implements RegionFactory {
private static final Log log = LogFactory.getLog(ClusterAwareRegionFactory.class); private static final Log log = LogFactory.getLog(ClusterAwareRegionFactory.class);
private static final Hashtable<String, EmbeddedCacheManager> cacheManagers = new Hashtable<String, EmbeddedCacheManager>(); private static final Hashtable<String, EmbeddedCacheManager> cacheManagers = new Hashtable<String, EmbeddedCacheManager>();
private final InfinispanRegionFactory delegate = private InfinispanRegionFactory delegate;
new SingleNodeTestCase.TestInfinispanRegionFactory(); private String cacheManagerName;
private String cacheManagerName; private boolean locallyAdded;
private boolean locallyAdded;
public ClusterAwareRegionFactory(Properties props) { public ClusterAwareRegionFactory(Properties props) {
} try {
delegate = (InfinispanRegionFactory) ReflectHelper.classForName(props.getProperty(DualNodeTest.REGION_FACTORY_DELEGATE)).newInstance();
} catch (Exception e) {
throw new IllegalStateException(e);
}
}
public static EmbeddedCacheManager getCacheManager(String name) { public static EmbeddedCacheManager getCacheManager(String name) {
return cacheManagers.get(name); return cacheManagers.get(name);
} }
public static void addCacheManager(String name, EmbeddedCacheManager manager) { public static void addCacheManager(String name, EmbeddedCacheManager manager) {
cacheManagers.put(name, manager); cacheManagers.put(name, manager);
} }
public static void clearCacheManagers() { public static void clearCacheManagers() {
for (EmbeddedCacheManager manager : cacheManagers.values()) { for (EmbeddedCacheManager manager : cacheManagers.values()) {
try { try {
manager.stop(); manager.stop();
} catch (Exception e) { } catch (Exception e) {
log.error("Exception cleaning up CacheManager " + manager, e); log.error("Exception cleaning up CacheManager " + manager, e);
} }
} }
cacheManagers.clear(); cacheManagers.clear();
} }
public void start(SessionFactoryOptions settings, Properties properties) throws CacheException { public void start(SessionFactoryOptions settings, Properties properties) throws CacheException {
cacheManagerName = properties.getProperty(DualNodeTestCase.NODE_ID_PROP); cacheManagerName = properties.getProperty(DualNodeTest.NODE_ID_PROP);
EmbeddedCacheManager existing = getCacheManager(cacheManagerName); EmbeddedCacheManager existing = getCacheManager(cacheManagerName);
locallyAdded = (existing == null); locallyAdded = (existing == null);
if (locallyAdded) { if (locallyAdded) {
delegate.start(settings, properties); delegate.start(settings, properties);
cacheManagers.put(cacheManagerName, delegate.getCacheManager()); cacheManagers.put(cacheManagerName, delegate.getCacheManager());
} else { } else {
delegate.setCacheManager(existing); delegate.setCacheManager(existing);
} }
} }
public void stop() { public void stop() {
if (locallyAdded) cacheManagers.remove(cacheManagerName); if (locallyAdded) cacheManagers.remove(cacheManagerName);
delegate.stop(); delegate.stop();
} }
public CollectionRegion buildCollectionRegion(String regionName, Properties properties, public CollectionRegion buildCollectionRegion(String regionName, Properties properties,
CacheDataDescription metadata) throws CacheException { CacheDataDescription metadata) throws CacheException {
return delegate.buildCollectionRegion(regionName, properties, metadata); return delegate.buildCollectionRegion(regionName, properties, metadata);
} }
public EntityRegion buildEntityRegion(String regionName, Properties properties, public EntityRegion buildEntityRegion(String regionName, Properties properties,
CacheDataDescription metadata) throws CacheException { CacheDataDescription metadata) throws CacheException {
return delegate.buildEntityRegion(regionName, properties, metadata); return delegate.buildEntityRegion(regionName, properties, metadata);
} }
@Override @Override
public NaturalIdRegion buildNaturalIdRegion(String regionName, Properties properties, CacheDataDescription metadata) public NaturalIdRegion buildNaturalIdRegion(String regionName, Properties properties, CacheDataDescription metadata)
throws CacheException { throws CacheException {
return delegate.buildNaturalIdRegion( regionName, properties, metadata ); return delegate.buildNaturalIdRegion( regionName, properties, metadata );
} }
public QueryResultsRegion buildQueryResultsRegion(String regionName, Properties properties) public QueryResultsRegion buildQueryResultsRegion(String regionName, Properties properties)
throws CacheException { throws CacheException {
return delegate.buildQueryResultsRegion(regionName, properties); return delegate.buildQueryResultsRegion(regionName, properties);
} }
public TimestampsRegion buildTimestampsRegion(String regionName, Properties properties) public TimestampsRegion buildTimestampsRegion(String regionName, Properties properties)
throws CacheException { throws CacheException {
return delegate.buildTimestampsRegion(regionName, properties); return delegate.buildTimestampsRegion(regionName, properties);
} }
public boolean isMinimalPutsEnabledByDefault() { public boolean isMinimalPutsEnabledByDefault() {
return delegate.isMinimalPutsEnabledByDefault(); return delegate.isMinimalPutsEnabledByDefault();
} }
@Override @Override
public AccessType getDefaultAccessType() { public AccessType getDefaultAccessType() {
@ -120,6 +123,6 @@ public class ClusterAwareRegionFactory implements RegionFactory {
} }
public long nextTimestamp() { public long nextTimestamp() {
return delegate.nextTimestamp(); return delegate.nextTimestamp();
} }
} }

View File

@ -30,9 +30,9 @@ public class DualNodeJtaPlatformImpl implements JtaPlatform, Configurable {
@Override @Override
public void configure(Map configurationValues) { public void configure(Map configurationValues) {
nodeId = (String) configurationValues.get( DualNodeTestCase.NODE_ID_PROP ); nodeId = (String) configurationValues.get( DualNodeTest.NODE_ID_PROP );
if ( nodeId == null ) { if ( nodeId == null ) {
throw new HibernateException(DualNodeTestCase.NODE_ID_PROP + " not configured"); throw new HibernateException(DualNodeTest.NODE_ID_PROP + " not configured");
} }
} }

View File

@ -6,37 +6,42 @@
*/ */
package org.hibernate.test.cache.infinispan.functional.cluster; package org.hibernate.test.cache.infinispan.functional.cluster;
import java.util.Map; import java.util.Map;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.boot.Metadata; import org.hibernate.boot.Metadata;
import org.hibernate.boot.MetadataSources; import org.hibernate.boot.MetadataSources;
import org.hibernate.boot.registry.StandardServiceRegistry; import org.hibernate.boot.registry.StandardServiceRegistry;
import org.hibernate.boot.registry.StandardServiceRegistryBuilder; import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
import org.hibernate.cfg.AvailableSettings;
import org.hibernate.cfg.Environment; import org.hibernate.cfg.Environment;
import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.resource.transaction.TransactionCoordinatorBuilder; import org.hibernate.resource.transaction.TransactionCoordinatorBuilder;
import org.hibernate.resource.transaction.backend.jdbc.internal.JdbcResourceLocalTransactionCoordinatorBuilderImpl;
import org.hibernate.resource.transaction.backend.jta.internal.JtaTransactionCoordinatorBuilderImpl; import org.hibernate.resource.transaction.backend.jta.internal.JtaTransactionCoordinatorBuilderImpl;
import org.hibernate.test.cache.infinispan.functional.AbstractFunctionalTest;
import org.hibernate.test.cache.infinispan.util.InfinispanTestingSetup; import org.hibernate.test.cache.infinispan.util.InfinispanTestingSetup;
import org.hibernate.testing.junit4.BaseNonConfigCoreFunctionalTestCase;
import org.hibernate.test.cache.infinispan.util.TxUtil;
import org.infinispan.util.logging.Log; import org.infinispan.util.logging.Log;
import org.infinispan.util.logging.LogFactory; import org.infinispan.util.logging.LogFactory;
import org.junit.Before;
import org.junit.ClassRule; import org.junit.ClassRule;
import org.junit.runners.Parameterized;
/** /**
* @author Galder Zamarreño * @author Galder Zamarreño
* @since 3.5 * @since 3.5
*/ */
public abstract class DualNodeTestCase extends BaseNonConfigCoreFunctionalTestCase { public abstract class DualNodeTest extends AbstractFunctionalTest {
private static final Log log = LogFactory.getLog( DualNodeTestCase.class ); private static final Log log = LogFactory.getLog( DualNodeTest.class );
@ClassRule @ClassRule
public static final InfinispanTestingSetup infinispanTestIdentifier = new InfinispanTestingSetup(); public static final InfinispanTestingSetup infinispanTestIdentifier = new InfinispanTestingSetup();
public static final String REGION_FACTORY_DELEGATE = "hibernate.cache.region.factory_delegate";
public static final String NODE_ID_PROP = "hibernate.test.cluster.node.id"; public static final String NODE_ID_PROP = "hibernate.test.cluster.node.id";
public static final String NODE_ID_FIELD = "nodeId"; public static final String NODE_ID_FIELD = "nodeId";
public static final String LOCAL = "local"; public static final String LOCAL = "local";
@ -44,16 +49,20 @@ public abstract class DualNodeTestCase extends BaseNonConfigCoreFunctionalTestCa
private SecondNodeEnvironment secondNodeEnvironment; private SecondNodeEnvironment secondNodeEnvironment;
@Override protected void withTxSession(SessionFactory sessionFactory, TxUtil.ThrowingConsumer<Session, Exception> consumer) throws Exception {
public String[] getMappings() { TxUtil.withTxSession(useJta, sessionFactory, consumer);
return new String[] { }
"cache/infinispan/functional/Contact.hbm.xml", "cache/infinispan/functional/Customer.hbm.xml"
}; protected <T> T withTxSessionApply(SessionFactory sessionFactory, TxUtil.ThrowingFunction<Session, T, Exception> consumer) throws Exception {
return TxUtil.withTxSessionApply(useJta, sessionFactory, consumer);
} }
@Override @Override
public String getCacheConcurrencyStrategy() { public String[] getMappings() {
return "transactional"; return new String[] {
"cache/infinispan/functional/entities/Contact.hbm.xml",
"cache/infinispan/functional/entities/Customer.hbm.xml"
};
} }
@Override @Override
@ -65,6 +74,7 @@ public abstract class DualNodeTestCase extends BaseNonConfigCoreFunctionalTestCa
settings.put( NODE_ID_PROP, LOCAL ); settings.put( NODE_ID_PROP, LOCAL );
settings.put( NODE_ID_FIELD, LOCAL ); settings.put( NODE_ID_FIELD, LOCAL );
settings.put( REGION_FACTORY_DELEGATE, regionFactoryClass.getName() );
} }
@Override @Override
@ -101,10 +111,6 @@ public abstract class DualNodeTestCase extends BaseNonConfigCoreFunctionalTestCa
return ClusterAwareRegionFactory.class; return ClusterAwareRegionFactory.class;
} }
protected Class getConnectionProviderClass() {
return DualNodeConnectionProviderImpl.class;
}
protected Class getJtaPlatformClass() { protected Class getJtaPlatformClass() {
return DualNodeJtaPlatformImpl.class; return DualNodeJtaPlatformImpl.class;
} }
@ -122,20 +128,12 @@ public abstract class DualNodeTestCase extends BaseNonConfigCoreFunctionalTestCa
} }
} }
protected boolean getUseQueryCache() {
return true;
}
protected void configureSecondNode(StandardServiceRegistryBuilder ssrb) { protected void configureSecondNode(StandardServiceRegistryBuilder ssrb) {
} }
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
protected void applyStandardSettings(Map settings) { protected void applyStandardSettings(Map settings) {
settings.put( Environment.CONNECTION_PROVIDER, getConnectionProviderClass().getName() ); settings.put( Environment.CACHE_REGION_FACTORY, ClusterAwareRegionFactory.class.getName() );
settings.put( AvailableSettings.JTA_PLATFORM, getJtaPlatformClass().getName() );
settings.put( Environment.TRANSACTION_COORDINATOR_STRATEGY, getTransactionCoordinatorBuilder().getName() );
settings.put( Environment.CACHE_REGION_FACTORY, getCacheRegionFactory().getName() );
settings.put( Environment.USE_QUERY_CACHE, String.valueOf( getUseQueryCache() ) );
} }
public class SecondNodeEnvironment { public class SecondNodeEnvironment {

View File

@ -6,9 +6,10 @@
*/ */
package org.hibernate.test.cache.infinispan.functional.cluster; package org.hibernate.test.cache.infinispan.functional.cluster;
import javax.transaction.TransactionManager; import java.util.Arrays;
import java.util.HashSet; import java.util.HashSet;
import java.util.Iterator; import java.util.Iterator;
import java.util.List;
import java.util.Set; import java.util.Set;
import java.util.concurrent.Phaser; import java.util.concurrent.Phaser;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
@ -18,8 +19,8 @@ import java.util.concurrent.atomic.AtomicReference;
import org.hibernate.Session; import org.hibernate.Session;
import org.hibernate.SessionFactory; import org.hibernate.SessionFactory;
import org.hibernate.cache.infinispan.InfinispanRegionFactory; import org.hibernate.cache.infinispan.InfinispanRegionFactory;
import org.hibernate.test.cache.infinispan.functional.Contact; import org.hibernate.test.cache.infinispan.functional.entities.Contact;
import org.hibernate.test.cache.infinispan.functional.Customer; import org.hibernate.test.cache.infinispan.functional.entities.Customer;
import org.hibernate.testing.TestForIssue; import org.hibernate.testing.TestForIssue;
import org.infinispan.AdvancedCache; import org.infinispan.AdvancedCache;
import org.infinispan.Cache; import org.infinispan.Cache;
@ -36,7 +37,6 @@ import org.infinispan.util.logging.LogFactory;
import org.jboss.util.collection.ConcurrentSet; import org.jboss.util.collection.ConcurrentSet;
import org.junit.Test; import org.junit.Test;
import static org.infinispan.test.TestingUtil.withTx;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull; import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue; import static org.junit.Assert.assertTrue;
@ -47,28 +47,30 @@ import static org.junit.Assert.assertTrue;
* @author Galder Zamarreño * @author Galder Zamarreño
* @since 3.5 * @since 3.5
*/ */
public class EntityCollectionInvalidationTestCase extends DualNodeTestCase { public class EntityCollectionInvalidationTest extends DualNodeTest {
private static final Log log = LogFactory.getLog( EntityCollectionInvalidationTestCase.class ); private static final Log log = LogFactory.getLog( EntityCollectionInvalidationTest.class );
private static final long SLEEP_TIME = 50l; private static final long SLEEP_TIME = 50l;
private static final Integer CUSTOMER_ID = new Integer( 1 ); private static final Integer CUSTOMER_ID = new Integer( 1 );
static int test = 0;
private EmbeddedCacheManager localManager, remoteManager; private EmbeddedCacheManager localManager, remoteManager;
private Cache localCustomerCache, remoteCustomerCache; private Cache localCustomerCache, remoteCustomerCache;
private Cache localContactCache, remoteContactCache; private Cache localContactCache, remoteContactCache;
private Cache localCollectionCache, remoteCollectionCache; private Cache localCollectionCache, remoteCollectionCache;
private MyListener localListener, remoteListener; private MyListener localListener, remoteListener;
private TransactionManager localTM, remoteTM;
private SessionFactory localFactory, remoteFactory; private SessionFactory localFactory, remoteFactory;
@Override
public List<Object[]> getParameters() {
return Arrays.asList(TRANSACTIONAL, READ_WRITE);
}
@Override @Override
public void startUp() { public void startUp() {
super.startUp(); super.startUp();
// Bind a listener to the "local" cache // Bind a listener to the "local" cache
// Our region factory makes its CacheManager available to us // Our region factory makes its CacheManager available to us
localManager = ClusterAwareRegionFactory.getCacheManager( DualNodeTestCase.LOCAL ); localManager = ClusterAwareRegionFactory.getCacheManager( DualNodeTest.LOCAL );
// Cache localCache = localManager.getCache("entity"); // Cache localCache = localManager.getCache("entity");
localCustomerCache = localManager.getCache( Customer.class.getName() ); localCustomerCache = localManager.getCache( Customer.class.getName() );
localContactCache = localManager.getCache( Contact.class.getName() ); localContactCache = localManager.getCache( Contact.class.getName() );
@ -79,7 +81,7 @@ public class EntityCollectionInvalidationTestCase extends DualNodeTestCase {
localCollectionCache.addListener( localListener ); localCollectionCache.addListener( localListener );
// Bind a listener to the "remote" cache // Bind a listener to the "remote" cache
remoteManager = ClusterAwareRegionFactory.getCacheManager( DualNodeTestCase.REMOTE ); remoteManager = ClusterAwareRegionFactory.getCacheManager( DualNodeTest.REMOTE );
remoteCustomerCache = remoteManager.getCache( Customer.class.getName() ); remoteCustomerCache = remoteManager.getCache( Customer.class.getName() );
remoteContactCache = remoteManager.getCache( Contact.class.getName() ); remoteContactCache = remoteManager.getCache( Contact.class.getName() );
remoteCollectionCache = remoteManager.getCache( Customer.class.getName() + ".contacts" ); remoteCollectionCache = remoteManager.getCache( Customer.class.getName() + ".contacts" );
@ -90,9 +92,6 @@ public class EntityCollectionInvalidationTestCase extends DualNodeTestCase {
localFactory = sessionFactory(); localFactory = sessionFactory();
remoteFactory = secondNodeEnvironment().getSessionFactory(); remoteFactory = secondNodeEnvironment().getSessionFactory();
localTM = DualNodeJtaTransactionManagerImpl.getInstance( DualNodeTestCase.LOCAL );
remoteTM = DualNodeJtaTransactionManagerImpl.getInstance( DualNodeTestCase.REMOTE );
} }
@Override @Override
@ -102,9 +101,9 @@ public class EntityCollectionInvalidationTestCase extends DualNodeTestCase {
@Override @Override
protected void cleanupTest() throws Exception { protected void cleanupTest() throws Exception {
cleanup(localFactory, localTM); cleanup(localFactory);
localListener.clear(); localListener.clear();
remoteListener.clear(); remoteListener.clear();
// do not call super.cleanupTest becasue we would clean transaction managers // do not call super.cleanupTest becasue we would clean transaction managers
} }
@ -115,7 +114,7 @@ public class EntityCollectionInvalidationTestCase extends DualNodeTestCase {
assertTrue( localListener.isEmpty() ); assertTrue( localListener.isEmpty() );
log.debug( "Create node 0" ); log.debug( "Create node 0" );
IdContainer ids = createCustomer( localFactory, localTM ); IdContainer ids = createCustomer( localFactory );
assertTrue( remoteListener.isEmpty() ); assertTrue( remoteListener.isEmpty() );
assertTrue( localListener.isEmpty() ); assertTrue( localListener.isEmpty() );
@ -126,7 +125,7 @@ public class EntityCollectionInvalidationTestCase extends DualNodeTestCase {
log.debug( "Find node 0" ); log.debug( "Find node 0" );
// This actually brings the collection into the cache // This actually brings the collection into the cache
getCustomer( ids.customerId, localFactory, localTM ); getCustomer( ids.customerId, localFactory );
sleep( SLEEP_TIME ); sleep( SLEEP_TIME );
@ -134,7 +133,7 @@ public class EntityCollectionInvalidationTestCase extends DualNodeTestCase {
// should read everything from the cache // should read everything from the cache
log.debug( "Find(2) node 0" ); log.debug( "Find(2) node 0" );
localListener.clear(); localListener.clear();
getCustomer( ids.customerId, localFactory, localTM ); getCustomer( ids.customerId, localFactory );
// Check the read came from the cache // Check the read came from the cache
log.debug( "Check cache 0" ); log.debug( "Check cache 0" );
@ -142,13 +141,13 @@ public class EntityCollectionInvalidationTestCase extends DualNodeTestCase {
log.debug( "Find node 1" ); log.debug( "Find node 1" );
// This actually brings the collection into the cache since invalidation is in use // This actually brings the collection into the cache since invalidation is in use
getCustomer( ids.customerId, remoteFactory, remoteTM ); getCustomer( ids.customerId, remoteFactory );
// Now the collection is in the cache so, the 2nd "get" // Now the collection is in the cache so, the 2nd "get"
// should read everything from the cache // should read everything from the cache
log.debug( "Find(2) node 1" ); log.debug( "Find(2) node 1" );
remoteListener.clear(); remoteListener.clear();
getCustomer( ids.customerId, remoteFactory, remoteTM ); getCustomer( ids.customerId, remoteFactory );
// Check the read came from the cache // Check the read came from the cache
log.debug( "Check cache 1" ); log.debug( "Check cache 1" );
@ -156,7 +155,7 @@ public class EntityCollectionInvalidationTestCase extends DualNodeTestCase {
// Modify customer in remote // Modify customer in remote
remoteListener.clear(); remoteListener.clear();
ids = modifyCustomer( ids.customerId, remoteFactory, remoteTM ); ids = modifyCustomer( ids.customerId, remoteFactory );
sleep( 250 ); sleep( 250 );
assertLoadedFromCache( remoteListener, ids.customerId, ids.contactIds ); assertLoadedFromCache( remoteListener, ids.customerId, ids.contactIds );
@ -178,28 +177,18 @@ public class EntityCollectionInvalidationTestCase extends DualNodeTestCase {
remotePPCache.getAdvancedCache().addInterceptor(hookInterceptor, 0); remotePPCache.getAdvancedCache().addInterceptor(hookInterceptor, 0);
IdContainer idContainer = new IdContainer(); IdContainer idContainer = new IdContainer();
withTx(localTM, () -> { withTxSession(localFactory, s -> {
Session s = localFactory.getCurrentSession();
s.getTransaction().begin();
Customer customer = new Customer(); Customer customer = new Customer();
customer.setName( "JBoss" ); customer.setName( "JBoss" );
s.persist(customer); s.persist(customer);
s.getTransaction().commit();
s.close();
idContainer.customerId = customer.getId(); idContainer.customerId = customer.getId();
return null;
}); });
// start loading // start loading
Thread getThread = new Thread(() -> { Thread getThread = new Thread(() -> {
try { try {
withTx(remoteTM, () -> { withTxSession(remoteFactory, s -> {
Session s = remoteFactory.getCurrentSession();
s.getTransaction().begin();
s.get(Customer.class, idContainer.customerId); s.get(Customer.class, idContainer.customerId);
s.getTransaction().commit();
s.close();
return null;
}); });
} catch (Exception e) { } catch (Exception e) {
log.error("Failure to get customer", e); log.error("Failure to get customer", e);
@ -208,13 +197,9 @@ public class EntityCollectionInvalidationTestCase extends DualNodeTestCase {
}, "get-thread"); }, "get-thread");
Thread deleteThread = new Thread(() -> { Thread deleteThread = new Thread(() -> {
try { try {
withTx(localTM, () -> { withTxSession(localFactory, s -> {
Session s = localFactory.getCurrentSession();
s.getTransaction().begin();
Customer customer = s.get(Customer.class, idContainer.customerId); Customer customer = s.get(Customer.class, idContainer.customerId);
s.delete(customer); s.delete(customer);
s.getTransaction().commit();
return null;
}); });
} catch (Exception e) { } catch (Exception e) {
log.error("Failure to delete customer", e); log.error("Failure to delete customer", e);
@ -239,9 +224,9 @@ public class EntityCollectionInvalidationTestCase extends DualNodeTestCase {
throw new IllegalStateException("delete-thread failed", deleteException.get()); throw new IllegalStateException("delete-thread failed", deleteException.get());
} }
Customer localCustomer = getCustomer(idContainer.customerId, localFactory, localTM); Customer localCustomer = getCustomer(idContainer.customerId, localFactory);
assertNull(localCustomer); assertNull(localCustomer);
Customer remoteCustomer = getCustomer(idContainer.customerId, remoteFactory, remoteTM); Customer remoteCustomer = getCustomer(idContainer.customerId, remoteFactory);
assertNull(remoteCustomer); assertNull(remoteCustomer);
assertTrue(remoteCustomerCache.isEmpty()); assertTrue(remoteCustomerCache.isEmpty());
} }
@ -255,83 +240,47 @@ public class EntityCollectionInvalidationTestCase extends DualNodeTestCase {
assertTrue( remoteCollectionCache.isEmpty() ); assertTrue( remoteCollectionCache.isEmpty() );
} }
private IdContainer createCustomer(SessionFactory sessionFactory, TransactionManager tm) private IdContainer createCustomer(SessionFactory sessionFactory)
throws Exception { throws Exception {
log.debug( "CREATE CUSTOMER" ); log.debug( "CREATE CUSTOMER" );
tm.begin(); Customer customer = new Customer();
customer.setName("JBoss");
Set<Contact> contacts = new HashSet<Contact>();
try { Contact kabir = new Contact();
Session session = sessionFactory.getCurrentSession(); kabir.setCustomer(customer);
Customer customer = new Customer(); kabir.setName("Kabir");
customer.setName( "JBoss" ); kabir.setTlf("1111");
Set<Contact> contacts = new HashSet<Contact>(); contacts.add(kabir);
Contact kabir = new Contact(); Contact bill = new Contact();
kabir.setCustomer( customer ); bill.setCustomer(customer);
kabir.setName( "Kabir" ); bill.setName("Bill");
kabir.setTlf( "1111" ); bill.setTlf("2222");
contacts.add( kabir ); contacts.add(bill);
Contact bill = new Contact(); customer.setContacts(contacts);
bill.setCustomer( customer );
bill.setName( "Bill" );
bill.setTlf( "2222" );
contacts.add( bill );
customer.setContacts( contacts ); withTxSession(sessionFactory, session -> session.save(customer));
session.save( customer ); IdContainer ids = new IdContainer();
tm.commit(); ids.customerId = customer.getId();
Set contactIds = new HashSet();
contactIds.add( kabir.getId() );
contactIds.add( bill.getId() );
ids.contactIds = contactIds;
IdContainer ids = new IdContainer(); log.debug( "CREATE CUSTOMER - END" );
ids.customerId = customer.getId(); return ids;
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 { private Customer getCustomer(Integer id, SessionFactory sessionFactory) throws Exception {
log.debug( "Find customer with id=" + id ); log.debug( "Find customer with id=" + id );
tm.begin(); return withTxSessionApply(sessionFactory, session -> doGetCustomer(id, session));
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 { private Customer doGetCustomer(Integer id, Session session) throws Exception {
Customer customer = session.get( Customer.class, id ); Customer customer = session.get( Customer.class, id );
if (customer == null) { if (customer == null) {
return null; return null;
@ -346,15 +295,13 @@ public class EntityCollectionInvalidationTestCase extends DualNodeTestCase {
return customer; return customer;
} }
private IdContainer modifyCustomer(Integer id, SessionFactory sessionFactory, TransactionManager tm) private IdContainer modifyCustomer(Integer id, SessionFactory sessionFactory)
throws Exception { throws Exception {
log.debug( "Modify customer with id=" + id ); log.debug( "Modify customer with id=" + id );
tm.begin(); return withTxSessionApply(sessionFactory, session -> {
try {
Session session = sessionFactory.getCurrentSession();
IdContainer ids = new IdContainer(); IdContainer ids = new IdContainer();
Set contactIds = new HashSet(); Set contactIds = new HashSet();
Customer customer = doGetCustomer( id, session, tm ); Customer customer = doGetCustomer( id, session );
customer.setName( "NewJBoss" ); customer.setName( "NewJBoss" );
ids.customerId = customer.getId(); ids.customerId = customer.getId();
Set<Contact> contacts = customer.getContacts(); Set<Contact> contacts = customer.getContacts();
@ -368,51 +315,26 @@ public class EntityCollectionInvalidationTestCase extends DualNodeTestCase {
contact.setCustomer( null ); contact.setCustomer( null );
session.save( customer ); session.save( customer );
tm.commit();
return ids; 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 { private void cleanup(SessionFactory sessionFactory) throws Exception {
tm.begin(); withTxSession(sessionFactory, session -> {
try { Customer c = (Customer) session.get(Customer.class, CUSTOMER_ID);
Session session = sessionFactory.getCurrentSession(); if (c != null) {
Customer c = (Customer) session.get( Customer.class, CUSTOMER_ID );
if ( c != null ) {
Set contacts = c.getContacts(); Set contacts = c.getContacts();
for ( Iterator it = contacts.iterator(); it.hasNext(); ) { for (Iterator it = contacts.iterator(); it.hasNext(); ) {
session.delete( it.next() ); session.delete(it.next());
} }
c.setContacts( null ); c.setContacts(null);
session.delete( c ); session.delete(c);
} }
// since we don't use orphan removal, some contacts may persist // since we don't use orphan removal, some contacts may persist
for (Object contact : session.createCriteria(Contact.class).list()) { for (Object contact : session.createCriteria(Contact.class).list()) {
session.delete(contact); session.delete(contact);
} }
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) { private void assertLoadedFromCache(MyListener listener, Integer custId, Set contactIds) {
@ -477,7 +399,7 @@ public class EntityCollectionInvalidationTestCase extends DualNodeTestCase {
String key = event.getCache().getName() + "#" + event.getKey(); String key = event.getCache().getName() + "#" + event.getKey();
log.debug( "MyListener[" + name + "] - Visiting key " + key ); log.debug( "MyListener[" + name + "] - Visiting key " + key );
// String name = fqn.toString(); // String name = fqn.toString();
String token = ".functional."; String token = ".entities.";
int index = key.indexOf( token ); int index = key.indexOf( token );
if ( index > -1 ) { if ( index > -1 ) {
index += token.length(); index += token.length();

View File

@ -0,0 +1,244 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
package org.hibernate.test.cache.infinispan.functional.cluster;
import java.util.Arrays;
import java.util.List;
import java.util.Set;
import org.hibernate.Criteria;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.cache.infinispan.util.Caches;
import org.hibernate.criterion.Restrictions;
import org.hibernate.test.cache.infinispan.functional.entities.Citizen;
import org.hibernate.test.cache.infinispan.functional.entities.NaturalIdOnManyToOne;
import org.hibernate.test.cache.infinispan.functional.entities.State;
import org.infinispan.Cache;
import org.infinispan.manager.CacheContainer;
import org.infinispan.notifications.Listener;
import org.infinispan.notifications.cachelistener.annotation.CacheEntryVisited;
import org.infinispan.notifications.cachelistener.event.CacheEntryVisitedEvent;
import org.infinispan.util.logging.Log;
import org.infinispan.util.logging.LogFactory;
import org.jboss.util.collection.ConcurrentSet;
import org.junit.Test;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
/**
* // TODO: Document this
*
* @author Galder Zamarreño
*/
public class NaturalIdInvalidationTest extends DualNodeTest {
private static final Log log = LogFactory.getLog(NaturalIdInvalidationTest.class);
private static final long SLEEP_TIME = 50l;
@Override
public List<Object[]> getParameters() {
return Arrays.asList(TRANSACTIONAL, READ_WRITE, READ_ONLY);
}
@Override
protected Class<?>[] getAnnotatedClasses() {
return new Class[] {
Citizen.class, State.class,
NaturalIdOnManyToOne.class
};
}
@Test
public void testAll() throws Exception {
log.info( "*** testAll()" );
// Bind a listener to the "local" cache
// Our region factory makes its CacheManager available to us
CacheContainer localManager = ClusterAwareRegionFactory.getCacheManager(DualNodeTest.LOCAL);
Cache localNaturalIdCache = localManager.getCache(Citizen.class.getName() + "##NaturalId");
MyListener localListener = new MyListener( "local" );
localNaturalIdCache.addListener(localListener);
// Bind a listener to the "remote" cache
CacheContainer remoteManager = ClusterAwareRegionFactory.getCacheManager(DualNodeTest.REMOTE);
Cache remoteNaturalIdCache = remoteManager.getCache(Citizen.class.getName() + "##NaturalId");
MyListener remoteListener = new MyListener( "remote" );
remoteNaturalIdCache.addListener(remoteListener);
SessionFactory localFactory = sessionFactory();
SessionFactory remoteFactory = secondNodeEnvironment().getSessionFactory();
try {
assertTrue(remoteListener.isEmpty());
assertTrue(localListener.isEmpty());
saveSomeCitizens(localFactory);
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
getCitizenWithCriteria(localFactory);
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();
getCitizenWithCriteria(localFactory);
// Check the read came from the cache
log.debug( "Check cache 0" );
assertLoadedFromCache(localListener, "1234");
log.debug( "Find node 1" );
// This actually brings the collection into the cache since invalidation is in use
getCitizenWithCriteria(remoteFactory);
// 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();
getCitizenWithCriteria(remoteFactory);
// Check the read came from the cache
log.debug( "Check cache 1" );
assertLoadedFromCache(remoteListener, "1234");
// Modify customer in remote
remoteListener.clear();
deleteCitizenWithCriteria(remoteFactory);
sleep(250);
Set localKeys = Caches.keys(localNaturalIdCache.getAdvancedCache()).toSet();
assertEquals(1, localKeys.size());
// Only key left is the one for the citizen *not* in France
localKeys.toString().contains("000");
}
catch (Exception e) {
log.error("Error", e);
throw e;
} finally {
withTxSession(localFactory, s -> {
s.createQuery( "delete NaturalIdOnManyToOne" ).executeUpdate();
s.createQuery( "delete Citizen" ).executeUpdate();
s.createQuery( "delete State" ).executeUpdate();
});
}
}
private void assertLoadedFromCache(MyListener localListener, String id) {
for (String visited : localListener.visited){
if (visited.contains(id))
return;
}
fail("Citizen (" + id + ") should have present in the cache");
}
private void saveSomeCitizens(SessionFactory sf) throws Exception {
final Citizen c1 = new Citizen();
c1.setFirstname( "Emmanuel" );
c1.setLastname( "Bernard" );
c1.setSsn( "1234" );
final State france = new State();
france.setName( "Ile de France" );
c1.setState( france );
final Citizen c2 = new Citizen();
c2.setFirstname( "Gavin" );
c2.setLastname( "King" );
c2.setSsn( "000" );
final State australia = new State();
australia.setName( "Australia" );
c2.setState( australia );
withTxSession(sf, s -> {
s.persist( australia );
s.persist( france );
s.persist( c1 );
s.persist( c2 );
});
}
private void getCitizenWithCriteria(SessionFactory sf) throws Exception {
withTxSession(sf, s -> {
State france = getState(s, "Ile de France");
Criteria criteria = s.createCriteria( Citizen.class );
criteria.add( Restrictions.naturalId().set( "ssn", "1234" ).set( "state", france ) );
criteria.setCacheable( true );
criteria.list();
});
}
private void deleteCitizenWithCriteria(SessionFactory sf) throws Exception {
withTxSession(sf, s -> {
State france = getState(s, "Ile de France");
Criteria criteria = s.createCriteria( Citizen.class );
criteria.add( Restrictions.naturalId().set( "ssn", "1234" ).set( "state", france ) );
criteria.setCacheable( true );
Citizen c = (Citizen) criteria.uniqueResult();
s.delete(c);
});
}
private State getState(Session s, String name) {
Criteria criteria = s.createCriteria( State.class );
criteria.add( Restrictions.eq("name", name) );
criteria.setCacheable(true);
return (State) criteria.list().get( 0 );
}
@Listener
public static class MyListener {
private static final Log log = LogFactory.getLog( 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() ) {
visited.add(event.getKey().toString());
// 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 );
// }
}
}
}
}

View File

@ -1,280 +0,0 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
package org.hibernate.test.cache.infinispan.functional.cluster;
import javax.transaction.TransactionManager;
import java.util.Set;
import java.util.concurrent.Callable;
import org.hibernate.Criteria;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;
import org.hibernate.cache.infinispan.util.Caches;
import org.hibernate.criterion.Restrictions;
import org.hibernate.test.cache.infinispan.functional.Citizen;
import org.hibernate.test.cache.infinispan.functional.NaturalIdOnManyToOne;
import org.hibernate.test.cache.infinispan.functional.State;
import org.infinispan.Cache;
import org.infinispan.manager.CacheContainer;
import org.infinispan.notifications.Listener;
import org.infinispan.notifications.cachelistener.annotation.CacheEntryVisited;
import org.infinispan.notifications.cachelistener.event.CacheEntryVisitedEvent;
import org.infinispan.util.logging.Log;
import org.infinispan.util.logging.LogFactory;
import org.jboss.util.collection.ConcurrentSet;
import org.junit.Test;
import static org.infinispan.test.TestingUtil.withTx;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
/**
* // TODO: Document this
*
* @author Galder Zamarreño
* @since // TODO
*/
public class NaturalIdInvalidationTestCase extends DualNodeTestCase {
private static final Log log = LogFactory.getLog(NaturalIdInvalidationTestCase.class);
private static final long SLEEP_TIME = 50l;
private static final Integer CUSTOMER_ID = new Integer( 1 );
private static int test = 0;
@Override
protected Class<?>[] getAnnotatedClasses() {
return new Class[] {
Citizen.class, State.class,
NaturalIdOnManyToOne.class
};
}
@Test
public void testAll() throws Exception {
log.info( "*** testAll()" );
// Bind a listener to the "local" cache
// Our region factory makes its CacheManager available to us
CacheContainer localManager = ClusterAwareRegionFactory.getCacheManager(DualNodeTestCase.LOCAL);
Cache localNaturalIdCache = localManager.getCache(Citizen.class.getName() + "##NaturalId");
MyListener localListener = new MyListener( "local" );
localNaturalIdCache.addListener(localListener);
TransactionManager localTM = DualNodeJtaTransactionManagerImpl.getInstance(DualNodeTestCase.LOCAL);
// Bind a listener to the "remote" cache
CacheContainer remoteManager = ClusterAwareRegionFactory.getCacheManager(DualNodeTestCase.REMOTE);
Cache remoteNaturalIdCache = remoteManager.getCache(Citizen.class.getName() + "##NaturalId");
MyListener remoteListener = new MyListener( "remote" );
remoteNaturalIdCache.addListener(remoteListener);
TransactionManager remoteTM = DualNodeJtaTransactionManagerImpl.getInstance(DualNodeTestCase.REMOTE);
SessionFactory localFactory = sessionFactory();
SessionFactory remoteFactory = secondNodeEnvironment().getSessionFactory();
try {
assertTrue(remoteListener.isEmpty());
assertTrue(localListener.isEmpty());
saveSomeCitizens(localTM, localFactory);
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
getCitizenWithCriteria(localTM, localFactory);
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();
getCitizenWithCriteria(localTM, localFactory);
// Check the read came from the cache
log.debug( "Check cache 0" );
assertLoadedFromCache(localListener, "1234");
log.debug( "Find node 1" );
// This actually brings the collection into the cache since invalidation is in use
getCitizenWithCriteria(remoteTM, remoteFactory);
// 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();
getCitizenWithCriteria(remoteTM, remoteFactory);
// Check the read came from the cache
log.debug( "Check cache 1" );
assertLoadedFromCache(remoteListener, "1234");
// Modify customer in remote
remoteListener.clear();
deleteCitizenWithCriteria(remoteTM, remoteFactory);
sleep(250);
Set localKeys = Caches.keys(localNaturalIdCache.getAdvancedCache()).toSet();
assertEquals(1, localKeys.size());
// Only key left is the one for the citizen *not* in France
localKeys.toString().contains("000");
}
catch (Exception e) {
log.error("Error", e);
throw e;
} finally {
withTx(localTM, new Callable<Void>() {
@Override
public Void call() throws Exception {
Session s = sessionFactory().openSession();
s.beginTransaction();
s.createQuery( "delete NaturalIdOnManyToOne" ).executeUpdate();
s.createQuery( "delete Citizen" ).executeUpdate();
s.createQuery( "delete State" ).executeUpdate();
s.getTransaction().commit();
s.close();
return null;
}
});
}
}
private void assertLoadedFromCache(MyListener localListener, String id) {
for (String visited : localListener.visited){
if (visited.contains(id))
return;
}
fail("Citizen (" + id + ") should have present in the cache");
}
private void saveSomeCitizens(TransactionManager tm, final SessionFactory sf) throws Exception {
final Citizen c1 = new Citizen();
c1.setFirstname( "Emmanuel" );
c1.setLastname( "Bernard" );
c1.setSsn( "1234" );
final State france = new State();
france.setName( "Ile de France" );
c1.setState( france );
final Citizen c2 = new Citizen();
c2.setFirstname( "Gavin" );
c2.setLastname( "King" );
c2.setSsn( "000" );
final State australia = new State();
australia.setName( "Australia" );
c2.setState( australia );
withTx(tm, new Callable<Void>() {
@Override
public Void call() throws Exception {
Session s = sf.openSession();
Transaction tx = s.beginTransaction();
s.persist( australia );
s.persist( france );
s.persist( c1 );
s.persist( c2 );
tx.commit();
s.close();
return null;
}
});
}
private void getCitizenWithCriteria(TransactionManager tm, final SessionFactory sf) throws Exception {
withTx(tm, new Callable<Void >() {
@Override
public Void call() throws Exception {
Session s = sf.openSession();
Transaction tx = s.beginTransaction();
State france = getState(s, "Ile de France");
Criteria criteria = s.createCriteria( Citizen.class );
criteria.add( Restrictions.naturalId().set( "ssn", "1234" ).set( "state", france ) );
criteria.setCacheable( true );
criteria.list();
// cleanup
tx.commit();
s.close();
return null;
}
});
}
private void deleteCitizenWithCriteria(TransactionManager tm, final SessionFactory sf) throws Exception {
withTx(tm, new Callable<Void >() {
@Override
public Void call() throws Exception {
Session s = sf.openSession();
Transaction tx = s.beginTransaction();
State france = getState(s, "Ile de France");
Criteria criteria = s.createCriteria( Citizen.class );
criteria.add( Restrictions.naturalId().set( "ssn", "1234" ).set( "state", france ) );
criteria.setCacheable( true );
Citizen c = (Citizen) criteria.uniqueResult();
s.delete(c);
// cleanup
tx.commit();
s.close();
return null;
}
});
}
private State getState(Session s, String name) {
Criteria criteria = s.createCriteria( State.class );
criteria.add( Restrictions.eq("name", name) );
criteria.setCacheable(true);
return (State) criteria.list().get( 0 );
}
@Listener
public static class MyListener {
private static final Log log = LogFactory.getLog( 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() ) {
visited.add(event.getKey().toString());
// 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 );
// }
}
}
}
}

View File

@ -13,12 +13,12 @@ package org.hibernate.test.cache.infinispan.functional.cluster;
* @author Galder Zamarreño * @author Galder Zamarreño
* @since 3.5 * @since 3.5
*/ */
public class RepeatableSessionRefreshTest extends SessionRefreshTestCase { public class RepeatableSessionRefreshTest extends SessionRefreshTest {
private static final String CACHE_CONFIG = "entity-repeatable"; private static final String CACHE_CONFIG = "entity-repeatable";
@Override @Override
protected String getEntityCacheConfigName() { protected String getEntityCacheConfigName() {
return CACHE_CONFIG; return CACHE_CONFIG;
} }
} }

View File

@ -6,16 +6,16 @@
*/ */
package org.hibernate.test.cache.infinispan.functional.cluster; package org.hibernate.test.cache.infinispan.functional.cluster;
import java.util.Arrays;
import java.util.List;
import java.util.Map; import java.util.Map;
import javax.transaction.TransactionManager;
import org.hibernate.SessionFactory; import org.hibernate.SessionFactory;
import org.hibernate.boot.registry.StandardServiceRegistryBuilder; import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
import org.hibernate.cache.infinispan.InfinispanRegionFactory; import org.hibernate.cache.infinispan.InfinispanRegionFactory;
import org.hibernate.cfg.Environment; import org.hibernate.cfg.Environment;
import org.hibernate.test.cache.infinispan.functional.classloader.Account; import org.hibernate.test.cache.infinispan.functional.entities.Account;
import org.hibernate.test.cache.infinispan.functional.classloader.ClassLoaderTestDAO;
import org.junit.Test; import org.junit.Test;
import org.infinispan.Cache; import org.infinispan.Cache;
@ -33,12 +33,16 @@ import static org.junit.Assert.assertNotNull;
* @author Galder Zamarreño * @author Galder Zamarreño
* @since 3.5 * @since 3.5
*/ */
public class SessionRefreshTestCase extends DualNodeTestCase { public class SessionRefreshTest extends DualNodeTest {
private static final Logger log = Logger.getLogger( SessionRefreshTestCase.class ); private static final Logger log = Logger.getLogger( SessionRefreshTest.class );
static int test = 0;
private Cache localCache; private Cache localCache;
@Override
public List<Object[]> getParameters() {
return Arrays.asList(TRANSACTIONAL, READ_WRITE);
}
@Override @Override
protected void configureSecondNode(StandardServiceRegistryBuilder ssrb) { protected void configureSecondNode(StandardServiceRegistryBuilder ssrb) {
super.configureSecondNode( ssrb ); super.configureSecondNode( ssrb );
@ -57,7 +61,7 @@ public class SessionRefreshTestCase extends DualNodeTestCase {
@Override @Override
public String[] getMappings() { public String[] getMappings() {
return new String[] {"cache/infinispan/functional/classloader/Account.hbm.xml"}; return new String[] {"cache/infinispan/functional/entities/Account.hbm.xml"};
} }
@Override @Override
@ -70,49 +74,47 @@ public class SessionRefreshTestCase extends DualNodeTestCase {
@Test @Test
public void testRefreshAfterExternalChange() throws Exception { public void testRefreshAfterExternalChange() throws Exception {
// First session factory uses a cache // First session factory uses a cache
CacheContainer localManager = ClusterAwareRegionFactory.getCacheManager( DualNodeTestCase.LOCAL ); CacheContainer localManager = ClusterAwareRegionFactory.getCacheManager( DualNodeTest.LOCAL );
localCache = localManager.getCache( Account.class.getName() ); localCache = localManager.getCache( Account.class.getName() );
TransactionManager localTM = DualNodeJtaTransactionManagerImpl.getInstance( DualNodeTestCase.LOCAL );
SessionFactory localFactory = sessionFactory(); SessionFactory localFactory = sessionFactory();
// Second session factory doesn't; just needs a transaction manager // Second session factory doesn't; just needs a transaction manager
TransactionManager remoteTM = DualNodeJtaTransactionManagerImpl.getInstance( DualNodeTestCase.REMOTE );
SessionFactory remoteFactory = secondNodeEnvironment().getSessionFactory(); SessionFactory remoteFactory = secondNodeEnvironment().getSessionFactory();
ClassLoaderTestDAO dao0 = new ClassLoaderTestDAO( localFactory, localTM ); AccountDAO dao0 = new AccountDAO(useJta, localFactory );
ClassLoaderTestDAO dao1 = new ClassLoaderTestDAO( remoteFactory, remoteTM ); AccountDAO dao1 = new AccountDAO(useJta, remoteFactory );
Integer id = new Integer( 1 ); Integer id = new Integer( 1 );
dao0.createAccount( dao0.getSmith(), id, new Integer( 5 ), DualNodeTestCase.LOCAL ); dao0.createAccount( dao0.getSmith(), id, new Integer( 5 ), DualNodeTest.LOCAL );
// Basic sanity check // Basic sanity check
Account acct1 = dao1.getAccount( id ); Account acct1 = dao1.getAccount( id );
assertNotNull( acct1 ); assertNotNull( acct1 );
assertEquals( DualNodeTestCase.LOCAL, acct1.getBranch() ); assertEquals( DualNodeTest.LOCAL, acct1.getBranch() );
// This dao's session factory isn't caching, so cache won't see this change // This dao's session factory isn't caching, so cache won't see this change
dao1.updateAccountBranch( id, DualNodeTestCase.REMOTE ); dao1.updateAccountBranch( id, DualNodeTest.REMOTE );
// dao1's session doesn't touch the cache, // dao1's session doesn't touch the cache,
// so reading from dao0 should show a stale value from the cache // so reading from dao0 should show a stale value from the cache
// (we check to confirm the cache is used) // (we check to confirm the cache is used)
Account acct0 = dao0.getAccount( id ); Account acct0 = dao0.getAccount( id );
assertNotNull( acct0 ); assertNotNull( acct0 );
assertEquals( DualNodeTestCase.LOCAL, acct0.getBranch() ); assertEquals( DualNodeTest.LOCAL, acct0.getBranch() );
log.debug( "Contents when re-reading from local: " + TestingUtil.printCache( localCache ) ); log.debug( "Contents when re-reading from local: " + TestingUtil.printCache( localCache ) );
// Now call session.refresh and confirm we get the correct value // Now call session.refresh and confirm we get the correct value
acct0 = dao0.getAccountWithRefresh( id ); acct0 = dao0.getAccountWithRefresh( id );
assertNotNull( acct0 ); assertNotNull( acct0 );
assertEquals( DualNodeTestCase.REMOTE, acct0.getBranch() ); assertEquals( DualNodeTest.REMOTE, acct0.getBranch() );
log.debug( "Contents after refreshing in remote: " + TestingUtil.printCache( localCache ) ); log.debug( "Contents after refreshing in remote: " + TestingUtil.printCache( localCache ) );
// Double check with a brand new session, in case the other session // Double check with a brand new session, in case the other session
// for some reason bypassed the 2nd level cache // for some reason bypassed the 2nd level cache
ClassLoaderTestDAO dao0A = new ClassLoaderTestDAO( localFactory, localTM ); AccountDAO dao0A = new AccountDAO(useJta, localFactory );
Account acct0A = dao0A.getAccount( id ); Account acct0A = dao0A.getAccount( id );
assertNotNull( acct0A ); assertNotNull( acct0A );
assertEquals( DualNodeTestCase.REMOTE, acct0A.getBranch() ); assertEquals( DualNodeTest.REMOTE, acct0A.getBranch() );
log.debug( "Contents after creating a new session: " + TestingUtil.printCache( localCache ) ); log.debug( "Contents after creating a new session: " + TestingUtil.printCache( localCache ) );
} }
} }

View File

@ -0,0 +1,107 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
package org.hibernate.test.cache.infinispan.functional.entities;
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;
}
@Override
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;
}
@Override
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)));
}
}

View File

@ -0,0 +1,89 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
package org.hibernate.test.cache.infinispan.functional.entities;
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;
}
@Override
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;
}
@Override
public int hashCode() {
int result = 17;
result = result * 31 + lastName.hashCode();
result = result * 31 + ssn.hashCode();
return result;
}
@Override
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;
}
}

View File

@ -5,7 +5,7 @@
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>. * See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/ */
package org.hibernate.test.cache.infinispan.functional; package org.hibernate.test.cache.infinispan.functional.entities;
import javax.persistence.Entity; import javax.persistence.Entity;
import javax.persistence.GeneratedValue; import javax.persistence.GeneratedValue;
import javax.persistence.Id; import javax.persistence.Id;

View File

@ -6,7 +6,7 @@
*/ */
//$Id$ //$Id$
package org.hibernate.test.cache.infinispan.functional; package org.hibernate.test.cache.infinispan.functional.entities;
import javax.persistence.Entity; import javax.persistence.Entity;
import javax.persistence.GeneratedValue; import javax.persistence.GeneratedValue;

View File

@ -0,0 +1,73 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
package org.hibernate.test.cache.infinispan.functional.entities;
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;
}
}

View File

@ -0,0 +1,49 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
package org.hibernate.test.cache.infinispan.functional.entities;
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;
}
}

View File

@ -4,7 +4,7 @@
* License: GNU Lesser General Public License (LGPL), version 2.1 or later. * License: GNU Lesser General Public License (LGPL), version 2.1 or later.
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>. * See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/ */
package org.hibernate.test.cache.infinispan.functional; package org.hibernate.test.cache.infinispan.functional.entities;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashSet; import java.util.HashSet;
import java.util.List; import java.util.List;

View File

@ -1,4 +1,4 @@
package org.hibernate.test.cache.infinispan.functional; package org.hibernate.test.cache.infinispan.functional.entities;
import javax.persistence.Embeddable; import javax.persistence.Embeddable;
import java.io.Serializable; import java.io.Serializable;

View File

@ -4,7 +4,7 @@
* License: GNU Lesser General Public License (LGPL), version 2.1 or later. * License: GNU Lesser General Public License (LGPL), version 2.1 or later.
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>. * See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/ */
package org.hibernate.test.cache.infinispan.functional; package org.hibernate.test.cache.infinispan.functional.entities;
import org.hibernate.annotations.NaturalId; import org.hibernate.annotations.NaturalId;
import org.hibernate.annotations.NaturalIdCache; import org.hibernate.annotations.NaturalIdCache;

Some files were not shown because too many files have changed in this diff Show More