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)
This commit is contained in:
parent
2dc0adb893
commit
3689924d74
|
@ -104,12 +104,12 @@ public class InfinispanRegionFactory implements RegionFactory {
|
|||
*/
|
||||
public static final String INFINISPAN_CONFIG_RESOURCE_PROP = "hibernate.cache.infinispan.cfg";
|
||||
|
||||
/**
|
||||
* Property name that controls whether Infinispan statistics are enabled.
|
||||
* The property value is expected to be a boolean true or false, and it
|
||||
* overrides statistic configuration in base Infinispan configuration,
|
||||
* if provided.
|
||||
*/
|
||||
/**
|
||||
* Property name that controls whether Infinispan statistics are enabled.
|
||||
* The property value is expected to be a boolean true or false, and it
|
||||
* overrides statistic configuration in base Infinispan configuration,
|
||||
* if provided.
|
||||
*/
|
||||
public static final String INFINISPAN_GLOBAL_STATISTICS_PROP = "hibernate.cache.infinispan.statistics";
|
||||
|
||||
/**
|
||||
|
|
157
hibernate-infinispan/src/main/java/org/hibernate/cache/infinispan/access/AccessDelegate.java
vendored
Normal file
157
hibernate-infinispan/src/main/java/org/hibernate/cache/infinispan/access/AccessDelegate.java
vendored
Normal 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 <rvansa@redhat.com>
|
||||
*/
|
||||
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);
|
||||
}
|
|
@ -9,37 +9,31 @@ package org.hibernate.cache.infinispan.access;
|
|||
import org.hibernate.cache.CacheException;
|
||||
import org.hibernate.cache.infinispan.impl.BaseRegion;
|
||||
import org.hibernate.cache.infinispan.util.Caches;
|
||||
import org.hibernate.cache.spi.access.SoftLock;
|
||||
import org.hibernate.engine.spi.SessionImplementor;
|
||||
import org.infinispan.AdvancedCache;
|
||||
import org.infinispan.util.logging.Log;
|
||||
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 Galder Zamarreño
|
||||
* @since 3.5
|
||||
*/
|
||||
public abstract class TransactionalAccessDelegate {
|
||||
protected static final Log log = LogFactory.getLog( TransactionalAccessDelegate.class );
|
||||
public abstract class InvalidationCacheAccessDelegate implements AccessDelegate {
|
||||
protected static final Log log = LogFactory.getLog( InvalidationCacheAccessDelegate.class );
|
||||
protected static final boolean TRACE_ENABLED = log.isTraceEnabled();
|
||||
protected final AdvancedCache cache;
|
||||
protected final BaseRegion region;
|
||||
protected final PutFromLoadValidator putValidator;
|
||||
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()) {
|
||||
return new TxTransactionalAccessDelegate(region, validator);
|
||||
return new TxInvalidationCacheAccessDelegate(region, validator);
|
||||
}
|
||||
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
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
protected TransactionalAccessDelegate(BaseRegion region, PutFromLoadValidator validator) {
|
||||
protected InvalidationCacheAccessDelegate(BaseRegion region, PutFromLoadValidator validator) {
|
||||
this.region = region;
|
||||
this.cache = region.getCache();
|
||||
this.putValidator = validator;
|
||||
|
@ -67,6 +61,7 @@ public abstract class TransactionalAccessDelegate {
|
|||
* @return the cached object or <tt>null</tt>
|
||||
* @throws CacheException if the cache retrieval failed
|
||||
*/
|
||||
@Override
|
||||
@SuppressWarnings("UnusedParameters")
|
||||
public Object get(SessionImplementor session, Object key, long txTimestamp) throws CacheException {
|
||||
if ( !region.checkValid() ) {
|
||||
|
@ -79,16 +74,7 @@ public abstract class TransactionalAccessDelegate {
|
|||
return val;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
@Override
|
||||
public boolean putFromLoad(SessionImplementor session, Object key, Object value, long txTimestamp, Object version) {
|
||||
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
|
||||
* @throws CacheException if storing the object failed
|
||||
*/
|
||||
@Override
|
||||
@SuppressWarnings("UnusedParameters")
|
||||
public boolean putFromLoad(SessionImplementor session, Object key, Object value, long txTimestamp, Object version, boolean minimalPutOverride)
|
||||
throws CacheException {
|
||||
|
@ -143,41 +130,7 @@ public abstract class TransactionalAccessDelegate {
|
|||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
@Override
|
||||
public void remove(SessionImplementor session, Object key) throws CacheException {
|
||||
if ( !putValidator.beginInvalidatingKey(session, key)) {
|
||||
throw new CacheException(
|
||||
|
@ -196,11 +149,7 @@ public abstract class TransactionalAccessDelegate {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called to evict data from the entire region
|
||||
*
|
||||
* @throws CacheException if eviction the region fails
|
||||
*/
|
||||
@Override
|
||||
public void removeAll() throws CacheException {
|
||||
try {
|
||||
if (!putValidator.beginInvalidatingRegion()) {
|
||||
|
@ -213,23 +162,12 @@ public abstract class TransactionalAccessDelegate {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
@Override
|
||||
public void evict(Object key) throws CacheException {
|
||||
writeCache.remove( key );
|
||||
}
|
||||
|
||||
/**
|
||||
* Forcibly evict all items from the cache immediately without regard for transaction
|
||||
* isolation.
|
||||
*
|
||||
* @throws CacheException if evicting items fails
|
||||
*/
|
||||
@Override
|
||||
public void evictAll() throws CacheException {
|
||||
try {
|
||||
if (!putValidator.beginInvalidatingRegion()) {
|
||||
|
@ -245,16 +183,7 @@ public abstract class TransactionalAccessDelegate {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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}
|
||||
*/
|
||||
@Override
|
||||
public void unlockItem(SessionImplementor session, Object key) throws CacheException {
|
||||
if ( !putValidator.endInvalidatingKey(session, key) ) {
|
||||
// TODO: localization
|
||||
|
@ -262,50 +191,4 @@ public abstract class TransactionalAccessDelegate {
|
|||
+ 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;
|
||||
}
|
||||
}
|
|
@ -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 <rvansa@redhat.com>
|
||||
*/
|
||||
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;
|
||||
}
|
||||
}
|
|
@ -24,6 +24,7 @@ import org.hibernate.cache.spi.RegionFactory;
|
|||
import org.hibernate.engine.spi.SessionImplementor;
|
||||
import org.hibernate.resource.transaction.TransactionCoordinator;
|
||||
import org.infinispan.AdvancedCache;
|
||||
import org.infinispan.configuration.cache.CacheMode;
|
||||
import org.infinispan.configuration.cache.Configuration;
|
||||
import org.infinispan.configuration.cache.ConfigurationBuilder;
|
||||
import org.infinispan.interceptors.EntryWrappingInterceptor;
|
||||
|
@ -34,8 +35,8 @@ import org.infinispan.util.logging.Log;
|
|||
import org.infinispan.util.logging.LogFactory;
|
||||
|
||||
/**
|
||||
* Encapsulates logic to allow a {@link TransactionalAccessDelegate} to determine
|
||||
* whether a {@link TransactionalAccessDelegate#putFromLoad(org.hibernate.engine.spi.SessionImplementor, Object, Object, long, Object, boolean)}
|
||||
* Encapsulates logic to allow a {@link InvalidationCacheAccessDelegate} to determine
|
||||
* 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
|
||||
* 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
|
||||
|
@ -134,13 +135,10 @@ public class PutFromLoadValidator {
|
|||
|
||||
/**
|
||||
* Creates a new put from load validator instance.
|
||||
*
|
||||
* @param cache Cache instance on which to store pending put information.
|
||||
* @param cacheManager where to find a cache to store pending put information
|
||||
*/
|
||||
public PutFromLoadValidator(AdvancedCache cache,
|
||||
EmbeddedCacheManager cacheManager) {
|
||||
|
||||
* @param cache Cache instance on which to store pending put information.
|
||||
* @param cacheManager where to find a cache to store pending put information
|
||||
*/
|
||||
public PutFromLoadValidator(AdvancedCache cache, EmbeddedCacheManager cacheManager) {
|
||||
Configuration cacheConfiguration = cache.getCacheConfiguration();
|
||||
Configuration pendingPutsConfiguration = cacheManager.getCacheConfiguration(InfinispanRegionFactory.PENDING_PUTS_CACHE_NAME);
|
||||
ConfigurationBuilder configurationBuilder = new ConfigurationBuilder();
|
||||
|
@ -155,11 +153,14 @@ public class PutFromLoadValidator {
|
|||
else {
|
||||
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
|
||||
// that are not, we need to use custom interceptor, not listeners (which fire only for present entries).
|
||||
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();
|
||||
log.debug("Interceptor chain was: " + interceptorChain);
|
||||
int position = 0;
|
||||
|
|
|
@ -8,17 +8,16 @@ 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;
|
||||
|
||||
/**
|
||||
* Delegate for non-transactional caches
|
||||
* Delegate for transactional caches
|
||||
*
|
||||
* @author Radim Vansa <rvansa@redhat.com>
|
||||
*/
|
||||
public class NonTxTransactionalAccessDelegate extends TransactionalAccessDelegate {
|
||||
public NonTxTransactionalAccessDelegate(BaseRegion region, PutFromLoadValidator validator) {
|
||||
public class TxInvalidationCacheAccessDelegate extends InvalidationCacheAccessDelegate {
|
||||
public TxInvalidationCacheAccessDelegate(BaseRegion region, PutFromLoadValidator 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
|
||||
// (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)) {
|
||||
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.remove(key);
|
||||
writeCache.put(key, value);
|
||||
}
|
||||
finally {
|
||||
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
|
||||
// (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)) {
|
||||
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.remove(key);
|
||||
writeCache.put(key, value);
|
||||
}
|
||||
finally {
|
||||
putValidator.resetCurrentSession();
|
||||
|
@ -74,13 +73,22 @@ public class NonTxTransactionalAccessDelegate extends TransactionalAccessDelegat
|
|||
}
|
||||
|
||||
@Override
|
||||
public void unlockItem(SessionImplementor session, Object key) throws CacheException {
|
||||
TransactionCoordinator tc = session.getTransactionCoordinator();
|
||||
boolean doPFER = tc != null && tc.getTransactionDriverControl().getStatus() == TransactionStatus.COMMITTED;
|
||||
if ( !putValidator.endInvalidatingKey(session, key, doPFER) ) {
|
||||
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;
|
||||
}
|
||||
|
||||
@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;
|
||||
}
|
||||
}
|
|
@ -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 <rvansa@redhat.com>
|
||||
*/
|
||||
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;
|
||||
}
|
||||
}
|
|
@ -7,7 +7,7 @@
|
|||
package org.hibernate.cache.infinispan.collection;
|
||||
|
||||
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.access.CollectionRegionAccessStrategy;
|
||||
import org.hibernate.cache.spi.access.SoftLock;
|
||||
|
@ -16,21 +16,19 @@ import org.hibernate.engine.spi.SessionImplementor;
|
|||
import org.hibernate.persister.collection.CollectionPersister;
|
||||
|
||||
/**
|
||||
* Transactional collection region access for Infinispan.
|
||||
* Collection region access for Infinispan.
|
||||
*
|
||||
* @author Chris Bredesen
|
||||
* @author Galder Zamarreño
|
||||
* @since 3.5
|
||||
*/
|
||||
class TransactionalAccess implements CollectionRegionAccessStrategy {
|
||||
|
||||
class CollectionAccess implements CollectionRegionAccessStrategy {
|
||||
private final CollectionRegionImpl region;
|
||||
private final AccessDelegate delegate;
|
||||
|
||||
private final TransactionalAccessDelegate delegate;
|
||||
|
||||
TransactionalAccess(CollectionRegionImpl region) {
|
||||
CollectionAccess(CollectionRegionImpl region, AccessDelegate delegate) {
|
||||
this.region = region;
|
||||
this.delegate = TransactionalAccessDelegate.create( region, region.getPutFromLoadValidator() );
|
||||
this.delegate = delegate;
|
||||
}
|
||||
|
||||
public void evict(Object key) throws CacheException {
|
|
@ -7,7 +7,8 @@
|
|||
package org.hibernate.cache.infinispan.collection;
|
||||
|
||||
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.spi.CacheDataDescription;
|
||||
import org.hibernate.cache.spi.CacheKeysFactory;
|
||||
|
@ -27,17 +28,16 @@ import javax.transaction.TransactionManager;
|
|||
* @since 3.5
|
||||
*/
|
||||
public class CollectionRegionImpl extends BaseTransactionalDataRegion implements CollectionRegion {
|
||||
|
||||
/**
|
||||
* Construct a collection region
|
||||
*
|
||||
* @param cache instance to store collection instances
|
||||
* @param name of collection type
|
||||
/**
|
||||
* Construct a collection region
|
||||
*
|
||||
* @param cache instance to store collection instances
|
||||
* @param name of collection type
|
||||
* @param transactionManager
|
||||
* @param metadata for the collection type
|
||||
* @param factory for the region
|
||||
* @param cacheKeysFactory factory for cache keys
|
||||
*/
|
||||
* @param metadata for the collection type
|
||||
* @param factory for the region
|
||||
* @param cacheKeysFactory factory for cache keys
|
||||
*/
|
||||
public CollectionRegionImpl(
|
||||
AdvancedCache cache, String name, TransactionManager transactionManager,
|
||||
CacheDataDescription metadata, RegionFactory factory, CacheKeysFactory cacheKeysFactory) {
|
||||
|
@ -46,16 +46,16 @@ public class CollectionRegionImpl extends BaseTransactionalDataRegion implements
|
|||
|
||||
@Override
|
||||
public CollectionRegionAccessStrategy buildAccessStrategy(AccessType accessType) throws CacheException {
|
||||
if ( AccessType.READ_ONLY.equals( accessType )
|
||||
|| AccessType.TRANSACTIONAL.equals( accessType ) ) {
|
||||
return new TransactionalAccess( this );
|
||||
checkAccessType( accessType );
|
||||
getValidator();
|
||||
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 );
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
package org.hibernate.cache.infinispan.entity;
|
||||
|
||||
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.spi.CacheDataDescription;
|
||||
import org.hibernate.cache.spi.CacheKeysFactory;
|
||||
|
@ -28,17 +28,16 @@ import javax.transaction.TransactionManager;
|
|||
* @since 3.5
|
||||
*/
|
||||
public class EntityRegionImpl extends BaseTransactionalDataRegion implements EntityRegion {
|
||||
|
||||
/**
|
||||
* Construct a entity region
|
||||
*
|
||||
* @param cache instance to store entity instances
|
||||
* @param name of entity type
|
||||
/**
|
||||
* Construct a entity region
|
||||
*
|
||||
* @param cache instance to store entity instances
|
||||
* @param name of entity type
|
||||
* @param transactionManager
|
||||
* @param metadata for the entity type
|
||||
* @param factory for the region
|
||||
* @param cacheKeysFactory factory for cache keys
|
||||
*/
|
||||
* @param metadata for the entity type
|
||||
* @param factory for the region
|
||||
* @param cacheKeysFactory factory for cache keys
|
||||
*/
|
||||
public EntityRegionImpl(
|
||||
AdvancedCache cache, String name, TransactionManager transactionManager,
|
||||
CacheDataDescription metadata, RegionFactory factory, CacheKeysFactory cacheKeysFactory) {
|
||||
|
@ -47,22 +46,19 @@ public class EntityRegionImpl extends BaseTransactionalDataRegion implements Ent
|
|||
|
||||
@Override
|
||||
public EntityRegionAccessStrategy buildAccessStrategy(AccessType accessType) throws CacheException {
|
||||
checkAccessType(accessType);
|
||||
if ( !getCacheDataDescription().isMutable() ) {
|
||||
accessType = AccessType.READ_ONLY;
|
||||
}
|
||||
InvalidationCacheAccessDelegate accessDelegate = InvalidationCacheAccessDelegate.create(this, getValidator());
|
||||
switch ( accessType ) {
|
||||
case READ_ONLY:
|
||||
return new ReadOnlyAccess( this );
|
||||
return new ReadOnlyAccess( this, accessDelegate);
|
||||
case READ_WRITE:
|
||||
case TRANSACTIONAL:
|
||||
if ( getCacheDataDescription().isMutable() ) {
|
||||
return new TransactionalAccess( this );
|
||||
}
|
||||
else {
|
||||
return new ReadOnlyAccess( this );
|
||||
}
|
||||
return new ReadWriteAccess( this, accessDelegate);
|
||||
default:
|
||||
throw new CacheException( "Unsupported access type [" + accessType.getExternalName() + "]" );
|
||||
}
|
||||
}
|
||||
|
||||
public PutFromLoadValidator getPutFromLoadValidator() {
|
||||
return new PutFromLoadValidator( cache );
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,21 +7,66 @@
|
|||
package org.hibernate.cache.infinispan.entity;
|
||||
|
||||
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.engine.spi.SessionFactoryImplementor;
|
||||
import org.hibernate.engine.spi.SessionImplementor;
|
||||
import org.hibernate.persister.entity.EntityPersister;
|
||||
|
||||
/**
|
||||
* A specialization of {@link TransactionalAccess} that ensures we never update data. Infinispan
|
||||
* access is always transactional.
|
||||
* A specialization of {@link ReadWriteAccess} that ensures we never update data.
|
||||
*
|
||||
* @author Chris Bredesen
|
||||
* @author Galder Zamarreño
|
||||
* @since 3.5
|
||||
*/
|
||||
class ReadOnlyAccess extends TransactionalAccess {
|
||||
class ReadOnlyAccess implements EntityRegionAccessStrategy {
|
||||
|
||||
ReadOnlyAccess(EntityRegionImpl region) {
|
||||
super( region );
|
||||
protected final EntityRegionImpl 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
|
||||
|
@ -31,6 +76,25 @@ class ReadOnlyAccess extends TransactionalAccess {
|
|||
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
|
||||
public boolean afterUpdate(
|
||||
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" );
|
||||
}
|
||||
|
||||
@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);
|
||||
}
|
||||
}
|
||||
|
|
36
hibernate-infinispan/src/main/java/org/hibernate/cache/infinispan/entity/ReadWriteAccess.java
vendored
Normal file
36
hibernate-infinispan/src/main/java/org/hibernate/cache/infinispan/entity/ReadWriteAccess.java
vendored
Normal 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 );
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -14,10 +14,12 @@ import javax.transaction.Transaction;
|
|||
import javax.transaction.TransactionManager;
|
||||
|
||||
import org.hibernate.cache.CacheException;
|
||||
import org.hibernate.cache.infinispan.access.PutFromLoadValidator;
|
||||
import org.hibernate.cache.infinispan.util.Caches;
|
||||
import org.hibernate.cache.spi.Region;
|
||||
import org.hibernate.cache.spi.RegionFactory;
|
||||
|
||||
import org.hibernate.cache.spi.access.AccessType;
|
||||
import org.infinispan.AdvancedCache;
|
||||
import org.infinispan.context.Flag;
|
||||
import org.infinispan.util.logging.Log;
|
||||
|
@ -35,7 +37,6 @@ import org.infinispan.util.logging.LogFactory;
|
|||
public abstract class BaseRegion implements Region {
|
||||
|
||||
private static final Log log = LogFactory.getLog( BaseRegion.class );
|
||||
private Transaction currentTransaction;
|
||||
|
||||
private enum InvalidateState {
|
||||
INVALID, CLEARING, VALID
|
||||
|
@ -48,12 +49,13 @@ public abstract class BaseRegion implements Region {
|
|||
private final Object invalidationMutex = new Object();
|
||||
private final AtomicReference<InvalidateState> invalidateState =
|
||||
new AtomicReference<InvalidateState>( InvalidateState.VALID );
|
||||
private volatile Transaction invalidateTransaction;
|
||||
|
||||
private final RegionFactory factory;
|
||||
|
||||
protected final AdvancedCache cache;
|
||||
|
||||
private PutFromLoadValidator validator;
|
||||
|
||||
/**
|
||||
* Base region constructor.
|
||||
*
|
||||
|
@ -110,7 +112,7 @@ public abstract class BaseRegion implements Region {
|
|||
@Override
|
||||
public int getTimeout() {
|
||||
// 60 seconds
|
||||
return 600;
|
||||
return 60000;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -149,21 +151,7 @@ public abstract class BaseRegion implements Region {
|
|||
synchronized (invalidationMutex) {
|
||||
if ( invalidateState.compareAndSet( InvalidateState.INVALID, InvalidateState.CLEARING ) ) {
|
||||
try {
|
||||
// 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.
|
||||
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();
|
||||
}
|
||||
|
||||
runInvalidation( getCurrentTransaction() != null );
|
||||
log.tracef( "Transition state from CLEARING to VALID" );
|
||||
invalidateState.compareAndSet(
|
||||
InvalidateState.CLEARING, InvalidateState.VALID
|
||||
|
@ -228,7 +216,7 @@ public abstract class BaseRegion implements Region {
|
|||
if (log.isTraceEnabled()) {
|
||||
log.trace( "Invalidate region: " + name );
|
||||
}
|
||||
invalidateState.set( InvalidateState.INVALID );
|
||||
invalidateState.set(InvalidateState.INVALID);
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,7 +7,9 @@
|
|||
package org.hibernate.cache.infinispan.naturalid;
|
||||
|
||||
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.InvalidationCacheAccessDelegate;
|
||||
import org.hibernate.cache.infinispan.impl.BaseTransactionalDataRegion;
|
||||
import org.hibernate.cache.spi.CacheDataDescription;
|
||||
import org.hibernate.cache.spi.CacheKeysFactory;
|
||||
|
@ -46,18 +48,19 @@ public class NaturalIdRegionImpl extends BaseTransactionalDataRegion
|
|||
|
||||
@Override
|
||||
public NaturalIdRegionAccessStrategy buildAccessStrategy(AccessType accessType) throws CacheException {
|
||||
checkAccessType( accessType );
|
||||
if (!getCacheDataDescription().isMutable()) {
|
||||
accessType = AccessType.READ_ONLY;
|
||||
}
|
||||
AccessDelegate delegate = InvalidationCacheAccessDelegate.create( this, getValidator());
|
||||
switch ( accessType ) {
|
||||
case READ_ONLY:
|
||||
return new ReadOnlyAccess( this );
|
||||
return new ReadOnlyAccess( this, delegate );
|
||||
case READ_WRITE:
|
||||
case TRANSACTIONAL:
|
||||
return new TransactionalAccess( this );
|
||||
return new ReadWriteAccess( this, delegate );
|
||||
default:
|
||||
throw new CacheException( "Unsupported access type [" + accessType.getExternalName() + "]" );
|
||||
}
|
||||
}
|
||||
|
||||
public PutFromLoadValidator getPutFromLoadValidator() {
|
||||
return new PutFromLoadValidator( cache );
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -7,16 +7,29 @@
|
|||
package org.hibernate.cache.infinispan.naturalid;
|
||||
|
||||
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.engine.spi.SessionImplementor;
|
||||
import org.hibernate.persister.entity.EntityPersister;
|
||||
|
||||
/**
|
||||
* @author Strong Liu <stliu@hibernate.org>
|
||||
*/
|
||||
class ReadOnlyAccess extends TransactionalAccess {
|
||||
class ReadOnlyAccess implements NaturalIdRegionAccessStrategy {
|
||||
|
||||
ReadOnlyAccess(NaturalIdRegionImpl naturalIdRegion) {
|
||||
super( naturalIdRegion );
|
||||
protected final NaturalIdRegionImpl region;
|
||||
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
|
||||
|
@ -24,9 +37,83 @@ class ReadOnlyAccess extends TransactionalAccess {
|
|||
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
|
||||
public boolean afterUpdate(SessionImplementor session, Object key, Object value, SoftLock lock) throws CacheException {
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
|
33
hibernate-infinispan/src/main/java/org/hibernate/cache/infinispan/naturalid/ReadWriteAccess.java
vendored
Normal file
33
hibernate-infinispan/src/main/java/org/hibernate/cache/infinispan/naturalid/ReadWriteAccess.java
vendored
Normal 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 );
|
||||
}
|
||||
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -124,7 +124,7 @@ public class Caches {
|
|||
* @return a cache that ignores return values
|
||||
*/
|
||||
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(
|
||||
AdvancedCache cache, Flag extraFlag) {
|
||||
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();
|
||||
}
|
||||
|
||||
public static boolean isTransactionalCache(AdvancedCache cache) {
|
||||
return cache.getCacheConfiguration().transaction().transactionMode().isTransactional();
|
||||
}
|
||||
|
||||
|
||||
public static void removeAll(AdvancedCache cache) {
|
||||
CloseableIterator it = keys(cache).iterator();
|
||||
try {
|
||||
|
|
|
@ -20,40 +20,23 @@
|
|||
|
||||
<!-- Default configuration is appropriate for entity/collection caching. -->
|
||||
<invalidation-cache name="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"/>
|
||||
<locking concurrency-level="1000" acquire-timeout="15000"/>
|
||||
<transaction mode="NONE" />
|
||||
<eviction max-entries="10000" strategy="LRU"/>
|
||||
<expiration max-idle="100000" interval="5000"/>
|
||||
</invalidation-cache>
|
||||
|
||||
<!-- Default configuration for immutable entities -->
|
||||
<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"/>
|
||||
<eviction max-entries="10000" strategy="LRU"/>
|
||||
<expiration max-idle="100000" interval="5000"/>
|
||||
</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. -->
|
||||
<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" />
|
||||
<eviction max-entries="10000" strategy="LRU"/>
|
||||
<expiration max-idle="100000" interval="5000"/>
|
||||
|
@ -61,7 +44,7 @@
|
|||
|
||||
<!-- A query cache that replicates queries. Replication is asynchronous. -->
|
||||
<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" />
|
||||
<eviction max-entries="10000" strategy="LRU"/>
|
||||
<expiration max-idle="100000" interval="5000"/>
|
||||
|
@ -71,7 +54,7 @@
|
|||
is required if query caching is used, even if the query cache
|
||||
itself is configured with CacheMode=LOCAL. -->
|
||||
<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 -->
|
||||
<transaction mode="NONE"/>
|
||||
<!-- Don't ever evict modification timestamps -->
|
||||
|
|
|
@ -30,7 +30,7 @@ import static org.junit.Assert.assertTrue;
|
|||
* @author Galder Zamarreño
|
||||
* @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);
|
||||
|
||||
@Test
|
||||
|
@ -43,7 +43,8 @@ public abstract class AbstractEntityCollectionRegionTestCase extends AbstractReg
|
|||
"test",
|
||||
InfinispanRegionFactory.class,
|
||||
true,
|
||||
false
|
||||
false,
|
||||
jtaPlatform
|
||||
);
|
||||
ssrb.applySetting( InfinispanRegionFactory.ENTITY_CACHE_RESOURCE_PROP, "entity" );
|
||||
final StandardServiceRegistry registry = ssrb.build();
|
||||
|
@ -72,7 +73,8 @@ public abstract class AbstractEntityCollectionRegionTestCase extends AbstractReg
|
|||
"test",
|
||||
InfinispanRegionFactory.class,
|
||||
true,
|
||||
false
|
||||
false,
|
||||
jtaPlatform
|
||||
);
|
||||
final StandardServiceRegistry registry = ssrb.build();
|
||||
try {
|
||||
|
@ -101,7 +103,8 @@ public abstract class AbstractEntityCollectionRegionTestCase extends AbstractReg
|
|||
"test",
|
||||
InfinispanRegionFactory.class,
|
||||
true,
|
||||
false
|
||||
false,
|
||||
jtaPlatform
|
||||
);
|
||||
final StandardServiceRegistry registry = ssrb.build();
|
||||
try {
|
80
hibernate-infinispan/src/test/java/org/hibernate/test/cache/infinispan/AbstractExtraAPITest.java
vendored
Normal file
80
hibernate-infinispan/src/test/java/org/hibernate/test/cache/infinispan/AbstractExtraAPITest.java
vendored
Normal 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 <rvansa@redhat.com>
|
||||
*/
|
||||
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 {
|
||||
}
|
||||
}
|
|
@ -12,7 +12,6 @@ import java.util.Properties;
|
|||
import java.util.Set;
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.function.BiConsumer;
|
||||
|
||||
import org.hibernate.Session;
|
||||
import org.hibernate.SessionFactory;
|
||||
|
@ -29,18 +28,12 @@ import org.hibernate.cache.spi.Region;
|
|||
import org.hibernate.cache.spi.RegionFactory;
|
||||
import org.hibernate.cfg.AvailableSettings;
|
||||
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.TestInfinispanRegionFactory;
|
||||
import org.infinispan.AdvancedCache;
|
||||
import org.infinispan.commons.util.CloseableIterable;
|
||||
import org.infinispan.transaction.tm.BatchModeTransactionManager;
|
||||
import org.jboss.logging.Logger;
|
||||
import org.junit.Test;
|
||||
|
||||
import javax.transaction.TransactionManager;
|
||||
|
||||
import static org.hibernate.test.cache.infinispan.util.CacheTestUtil.assertEqualsEventually;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNull;
|
||||
|
@ -51,8 +44,8 @@ import static org.junit.Assert.assertNull;
|
|||
* @author Galder Zamarreño
|
||||
* @since 3.5
|
||||
*/
|
||||
public abstract class AbstractGeneralDataRegionTestCase extends AbstractRegionImplTestCase {
|
||||
private static final Logger log = Logger.getLogger( AbstractGeneralDataRegionTestCase.class );
|
||||
public abstract class AbstractGeneralDataRegionTest extends AbstractRegionImplTest {
|
||||
private static final Logger log = Logger.getLogger( AbstractGeneralDataRegionTest.class );
|
||||
|
||||
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 VALUE3 = "value3";
|
||||
|
||||
protected TransactionManager tm = BatchModeTransactionManager.getInstance();
|
||||
|
||||
protected StandardServiceRegistryBuilder createStandardServiceRegistryBuilder() {
|
||||
return CacheTestUtil.buildBaselineStandardServiceRegistryBuilder(
|
||||
"test",
|
||||
InfinispanRegionFactory.class,
|
||||
false,
|
||||
true
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void putInRegion(Region region, Object key, Object 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 {
|
||||
StandardServiceRegistryBuilder ssrb = createStandardServiceRegistryBuilder()
|
||||
.applySetting(AvailableSettings.CACHE_REGION_FACTORY, SingleNodeTestCase.TestInfinispanRegionFactory.class.getName())
|
||||
.applySetting(AvailableSettings.JTA_PLATFORM, BatchModeJtaPlatform.class.getName())
|
||||
.applySetting(AvailableSettings.TRANSACTION_COORDINATOR_STRATEGY, JtaTransactionCoordinatorBuilderImpl.class.getName());
|
||||
.applySetting(AvailableSettings.CACHE_REGION_FACTORY, TestInfinispanRegionFactory.class.getName());
|
||||
Properties properties = CacheTestUtil.toProperties( ssrb.getSettings() );
|
||||
List<StandardServiceRegistry> registries = new ArrayList<>();
|
||||
List<SessionFactory> sessionFactories = new ArrayList<>();
|
||||
|
@ -168,7 +148,7 @@ public abstract class AbstractGeneralDataRegionTestCase extends AbstractRegionIm
|
|||
region.evict(KEY);
|
||||
}
|
||||
|
||||
protected abstract String getStandardRegionName(String regionPrefix);
|
||||
protected abstract String getStandardRegionName(String regionPrefix);
|
||||
|
||||
/**
|
||||
* Test method for {@link QueryResultsRegion#evictAll()}.
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -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 <rvansa@redhat.com>
|
||||
*/
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -21,7 +21,7 @@ import org.infinispan.AdvancedCache;
|
|||
* @author Galder Zamarreño
|
||||
* @since 3.5
|
||||
*/
|
||||
public abstract class AbstractRegionImplTestCase extends AbstractNonFunctionalTestCase {
|
||||
public abstract class AbstractRegionImplTest extends AbstractNonFunctionalTest {
|
||||
|
||||
protected abstract AdvancedCache getInfinispanCache(InfinispanRegionFactory regionFactory);
|
||||
|
File diff suppressed because it is too large
Load Diff
|
@ -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);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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 );
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
|
@ -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() );
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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 <rvansa@redhat.com>
|
||||
*/
|
||||
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());
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -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"...
|
||||
}
|
||||
}
|
|
@ -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"...
|
||||
}
|
||||
}
|
|
@ -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 {
|
||||
}
|
||||
}
|
|
@ -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));
|
||||
}
|
||||
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
|
@ -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));
|
||||
}
|
||||
|
||||
}
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
|
||||
}
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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";
|
||||
}
|
||||
}
|
|
@ -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());
|
||||
}
|
||||
}
|
|
@ -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());
|
||||
}
|
||||
}
|
|
@ -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 {
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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";
|
||||
}
|
||||
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -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" );
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -9,9 +9,9 @@ package org.hibernate.test.cache.infinispan.functional;
|
|||
import java.io.PrintWriter;
|
||||
import java.io.StringWriter;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Random;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.Callable;
|
||||
|
@ -20,18 +20,12 @@ import java.util.concurrent.ExecutorService;
|
|||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.Future;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import javax.transaction.TransactionManager;
|
||||
|
||||
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.test.cache.infinispan.functional.cluster.DualNodeConnectionProviderImpl;
|
||||
import org.hibernate.test.cache.infinispan.functional.cluster.DualNodeJtaPlatformImpl;
|
||||
import org.hibernate.test.cache.infinispan.functional.cluster.DualNodeJtaTransactionManagerImpl;
|
||||
import org.hibernate.test.cache.infinispan.functional.cluster.DualNodeTestCase;
|
||||
import org.hibernate.test.cache.infinispan.functional.entities.Contact;
|
||||
import org.hibernate.test.cache.infinispan.functional.entities.Customer;
|
||||
import org.junit.Test;
|
||||
|
||||
import org.infinispan.util.logging.Log;
|
||||
|
@ -45,7 +39,7 @@ import static org.junit.Assert.assertNull;
|
|||
* @author nikita_tovstoles@mba.berkeley.edu
|
||||
* @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 boolean trace = log.isTraceEnabled();
|
||||
/**
|
||||
|
@ -67,35 +61,9 @@ public class ConcurrentWriteTest extends SingleNodeTestCase {
|
|||
*/
|
||||
private Set<Integer> customerIDs = new HashSet<Integer>();
|
||||
|
||||
private TransactionManager tm;
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
protected void addSettings(Map settings) {
|
||||
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;
|
||||
public List<Object[]> getParameters() {
|
||||
return Arrays.asList(TRANSACTIONAL, READ_WRITE);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -111,34 +79,18 @@ public class ConcurrentWriteTest extends SingleNodeTestCase {
|
|||
}
|
||||
finally {
|
||||
cleanup();
|
||||
// DualNodeJtaTransactionManagerImpl.cleanupTransactions();
|
||||
// DualNodeJtaTransactionManagerImpl.cleanupTransactionManagers();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPingDb() throws Exception {
|
||||
try {
|
||||
beginTx();
|
||||
sessionFactory()
|
||||
.getCurrentSession()
|
||||
.createQuery( "from " + Customer.class.getName() )
|
||||
.list();
|
||||
}
|
||||
catch (Exception e) {
|
||||
setRollbackOnlyTx( e );
|
||||
// setRollbackOnly();
|
||||
// fail("failed to query DB; exception=" + e);
|
||||
}
|
||||
finally {
|
||||
commitOrRollbackTx();
|
||||
}
|
||||
withTxSession(s -> s.createQuery( "from " + Customer.class.getName() ).list());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSingleUser() throws Exception {
|
||||
// setup
|
||||
sessionFactory().getStatistics().clear();
|
||||
sessionFactory().getStatistics().clear();
|
||||
Customer customer = createCustomer( 0 );
|
||||
final Integer customerId = customer.getId();
|
||||
getCustomerIDs().add( customerId );
|
||||
|
@ -215,36 +167,20 @@ sessionFactory().getStatistics().clear();
|
|||
getCustomerIDs().clear();
|
||||
String deleteContactHQL = "delete from Contact";
|
||||
String deleteCustomerHQL = "delete from Customer";
|
||||
beginTx();
|
||||
try {
|
||||
Session session = sessionFactory().getCurrentSession();
|
||||
session.createQuery( deleteContactHQL ).setFlushMode( FlushMode.AUTO ).executeUpdate();
|
||||
session.createQuery( deleteCustomerHQL ).setFlushMode( FlushMode.AUTO ).executeUpdate();
|
||||
}
|
||||
catch (Exception e) {
|
||||
setRollbackOnlyTx( e );
|
||||
}
|
||||
finally {
|
||||
commitOrRollbackTx();
|
||||
}
|
||||
withTxSession(s -> {
|
||||
s.createQuery(deleteContactHQL).setFlushMode(FlushMode.AUTO).executeUpdate();
|
||||
s.createQuery(deleteCustomerHQL).setFlushMode(FlushMode.AUTO).executeUpdate();
|
||||
});
|
||||
}
|
||||
|
||||
private Customer createCustomer(int nameSuffix) throws Exception {
|
||||
Customer customer = null;
|
||||
beginTx();
|
||||
try {
|
||||
customer = new Customer();
|
||||
return withTxSessionApply(s -> {
|
||||
Customer customer = new Customer();
|
||||
customer.setName( "customer_" + nameSuffix );
|
||||
customer.setContacts( new HashSet<Contact>() );
|
||||
sessionFactory().getCurrentSession().persist( customer );
|
||||
}
|
||||
catch (Exception e) {
|
||||
setRollbackOnlyTx( e );
|
||||
}
|
||||
finally {
|
||||
commitOrRollbackTx();
|
||||
}
|
||||
return customer;
|
||||
s.persist( customer );
|
||||
return customer;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -255,101 +191,75 @@ sessionFactory().getStatistics().clear();
|
|||
* @throws java.lang.Exception
|
||||
*/
|
||||
private void readEveryonesFirstContact() throws Exception {
|
||||
beginTx();
|
||||
try {
|
||||
withTxSession(s -> {
|
||||
for ( Integer customerId : getCustomerIDs() ) {
|
||||
if ( TERMINATE_ALL_USERS ) {
|
||||
setRollbackOnlyTx();
|
||||
markRollbackOnly(s);
|
||||
return;
|
||||
}
|
||||
Customer customer = (Customer) sessionFactory()
|
||||
.getCurrentSession()
|
||||
.load( Customer.class, customerId );
|
||||
Customer customer = s.load( Customer.class, customerId );
|
||||
Set<Contact> contacts = customer.getContacts();
|
||||
if ( !contacts.isEmpty() ) {
|
||||
contacts.iterator().next();
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception e) {
|
||||
setRollbackOnlyTx( e );
|
||||
}
|
||||
finally {
|
||||
commitOrRollbackTx();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* -load existing Customer -get customer's contacts; return 1st one
|
||||
*
|
||||
* @param customerId
|
||||
* @return first Contact or null if customer has none
|
||||
*/
|
||||
private Contact getFirstContact(Integer customerId) throws Exception {
|
||||
assert customerId != null;
|
||||
Contact firstContact = null;
|
||||
beginTx();
|
||||
try {
|
||||
final Customer customer = (Customer) sessionFactory()
|
||||
.getCurrentSession()
|
||||
.load(Customer.class, customerId);
|
||||
Set<Contact> contacts = customer.getContacts();
|
||||
firstContact = contacts.isEmpty() ? null : contacts.iterator().next();
|
||||
if (TERMINATE_ALL_USERS)
|
||||
setRollbackOnlyTx();
|
||||
} catch (Exception e) {
|
||||
setRollbackOnlyTx(e);
|
||||
} finally {
|
||||
commitOrRollbackTx();
|
||||
}
|
||||
return firstContact;
|
||||
}
|
||||
/**
|
||||
* -load existing Customer -get customer's contacts; return 1st one
|
||||
*
|
||||
* @param customerId
|
||||
* @return first Contact or null if customer has none
|
||||
*/
|
||||
private Contact getFirstContact(Integer customerId) throws Exception {
|
||||
assert customerId != null;
|
||||
return withTxSessionApply(s -> {
|
||||
Customer customer = s.load(Customer.class, customerId);
|
||||
Set<Contact> contacts = customer.getContacts();
|
||||
Contact firstContact = contacts.isEmpty() ? null : contacts.iterator().next();
|
||||
if (TERMINATE_ALL_USERS) {
|
||||
markRollbackOnly(s);
|
||||
}
|
||||
return firstContact;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* -load existing Customer -create a new Contact and add to customer's contacts
|
||||
*
|
||||
* @param customerId
|
||||
* @return added Contact
|
||||
*/
|
||||
private Contact addContact(Integer customerId) throws Exception {
|
||||
assert customerId != null;
|
||||
Contact contact = null;
|
||||
beginTx();
|
||||
try {
|
||||
final Customer customer = (Customer) sessionFactory()
|
||||
.getCurrentSession()
|
||||
.load(Customer.class, customerId);
|
||||
contact = new Contact();
|
||||
contact.setName("contact name");
|
||||
contact.setTlf("wtf is tlf?");
|
||||
contact.setCustomer(customer);
|
||||
customer.getContacts().add(contact);
|
||||
// assuming contact is persisted via cascade from customer
|
||||
if (TERMINATE_ALL_USERS)
|
||||
setRollbackOnlyTx();
|
||||
} catch (Exception e) {
|
||||
setRollbackOnlyTx(e);
|
||||
} finally {
|
||||
commitOrRollbackTx();
|
||||
}
|
||||
return contact;
|
||||
}
|
||||
/**
|
||||
* -load existing Customer -create a new Contact and add to customer's contacts
|
||||
*
|
||||
* @param customerId
|
||||
* @return added Contact
|
||||
*/
|
||||
private Contact addContact(Integer customerId) throws Exception {
|
||||
assert customerId != null;
|
||||
return withTxSessionApply(s -> {
|
||||
final Customer customer = s.load(Customer.class, customerId);
|
||||
Contact contact = new Contact();
|
||||
contact.setName("contact name");
|
||||
contact.setTlf("wtf is tlf?");
|
||||
contact.setCustomer(customer);
|
||||
customer.getContacts().add(contact);
|
||||
// assuming contact is persisted via cascade from customer
|
||||
if (TERMINATE_ALL_USERS) {
|
||||
markRollbackOnly(s);
|
||||
}
|
||||
return contact;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* remove existing 'contact' from customer's list of contacts
|
||||
*
|
||||
* @param customerId
|
||||
* @throws IllegalStateException
|
||||
* if customer does not own a contact
|
||||
*/
|
||||
private void removeContact(Integer customerId) throws Exception {
|
||||
assert customerId != null;
|
||||
/**
|
||||
* remove existing 'contact' from customer's list of contacts
|
||||
*
|
||||
* @param customerId
|
||||
* @throws IllegalStateException
|
||||
* if customer does not own a contact
|
||||
*/
|
||||
private void removeContact(Integer customerId) throws Exception {
|
||||
assert customerId != null;
|
||||
|
||||
beginTx();
|
||||
try {
|
||||
Customer customer = (Customer) sessionFactory()
|
||||
.getCurrentSession()
|
||||
.load( Customer.class, customerId );
|
||||
withTxSession(s -> {
|
||||
Customer customer = s.load( Customer.class, customerId );
|
||||
Set<Contact> contacts = customer.getContacts();
|
||||
if ( contacts.size() != 1 ) {
|
||||
throw new IllegalStateException(
|
||||
|
@ -369,15 +279,9 @@ sessionFactory().getStatistics().clear();
|
|||
// assuming contact is persisted via cascade from customer
|
||||
|
||||
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() );
|
||||
log.info( "Wait for all executions paths to be ready to perform calls" );
|
||||
try {
|
||||
// barrier.await();
|
||||
for ( int i = 0; i < ITERATION_COUNT && !TERMINATE_ALL_USERS; i++ ) {
|
||||
contactExists();
|
||||
if ( trace ) {
|
||||
|
@ -464,17 +367,6 @@ sessionFactory().getStatistics().clear();
|
|||
TERMINATE_ALL_USERS = true;
|
||||
log.error( "Error", 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 {
|
||||
log.info( "Wait for all execution paths to finish" );
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -1,12 +1,13 @@
|
|||
package org.hibernate.test.cache.infinispan.functional;
|
||||
|
||||
import java.util.concurrent.Callable;
|
||||
|
||||
import org.hibernate.Session;
|
||||
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 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.assertNotNull;
|
||||
import static org.junit.Assert.assertNull;
|
||||
|
@ -17,61 +18,50 @@ import static org.junit.Assert.assertTrue;
|
|||
*
|
||||
* @author Radim Vansa <rvansa@redhat.com>
|
||||
*/
|
||||
public class EqualityTest extends SingleNodeTestCase {
|
||||
@Override
|
||||
protected Class[] getAnnotatedClasses() {
|
||||
return new Class[] { Person.class };
|
||||
}
|
||||
public class EqualityTest extends SingleNodeTest {
|
||||
@Override
|
||||
public List<Object[]> getParameters() {
|
||||
return Arrays.asList(TRANSACTIONAL, READ_WRITE, READ_ONLY);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEqualityFromType() throws Exception {
|
||||
Person john = new Person("John", "Black", 26);
|
||||
Person peter = new Person("Peter", "White", 32);
|
||||
@Override
|
||||
protected Class[] getAnnotatedClasses() {
|
||||
return new Class[] { Person.class };
|
||||
}
|
||||
|
||||
withTx(tm, new Callable<Void>() {
|
||||
@Override
|
||||
public Void call() throws Exception {
|
||||
Session session = openSession();
|
||||
session.getTransaction().begin();
|
||||
session.persist(john);
|
||||
session.persist(peter);
|
||||
session.getTransaction().commit();
|
||||
session.close();
|
||||
return null;
|
||||
}
|
||||
});
|
||||
@Test
|
||||
public void testEqualityFromType() throws Exception {
|
||||
Person john = new Person("John", "Black", 26);
|
||||
Person peter = new Person("Peter", "White", 32);
|
||||
|
||||
Statistics statistics = sessionFactory().getStatistics();
|
||||
statistics.clear();
|
||||
withTxSession(s -> {
|
||||
s.persist(john);
|
||||
s.persist(peter);
|
||||
});
|
||||
|
||||
for (int i = 0; i < 5; ++i) {
|
||||
withTx(tm, new Callable<Void>() {
|
||||
@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;
|
||||
}
|
||||
});
|
||||
}
|
||||
Statistics statistics = sessionFactory().getStatistics();
|
||||
statistics.clear();
|
||||
|
||||
assertTrue(statistics.getSecondLevelCacheHitCount() > 0);
|
||||
assertTrue(statistics.getSecondLevelCacheMissCount() > 0);
|
||||
}
|
||||
for (int i = 0; i < 5; ++i) {
|
||||
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) {
|
||||
assertNotNull(person);
|
||||
assertNotNull(person.getName());
|
||||
assertEquals(expected.getName().getFirstName(), person.getName().getFirstName());
|
||||
assertEquals(expected.getName().getLastName(), person.getName().getLastName());
|
||||
assertEquals(expected.getAge(), person.getAge());
|
||||
}
|
||||
assertTrue(statistics.getSecondLevelCacheHitCount() > 0);
|
||||
assertTrue(statistics.getSecondLevelCacheMissCount() > 0);
|
||||
}
|
||||
|
||||
private static void assertPersonEquals(Person expected, Person person) {
|
||||
assertNotNull(person);
|
||||
assertNotNull(person.getName());
|
||||
assertEquals(expected.getName().getFirstName(), person.getName().getFirstName());
|
||||
assertEquals(expected.getName().getLastName(), person.getName().getLastName());
|
||||
assertEquals(expected.getAge(), person.getAge());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,6 +6,8 @@
|
|||
*/
|
||||
package org.hibernate.test.cache.infinispan.functional;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Properties;
|
||||
import javax.naming.Context;
|
||||
|
@ -15,16 +17,17 @@ import javax.naming.NameNotFoundException;
|
|||
import javax.naming.Reference;
|
||||
import javax.naming.StringRefAddr;
|
||||
|
||||
import org.hibernate.Session;
|
||||
import org.hibernate.boot.registry.StandardServiceRegistry;
|
||||
import org.hibernate.cache.infinispan.InfinispanRegionFactory;
|
||||
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.engine.config.spi.ConfigurationService;
|
||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||
import org.hibernate.resource.transaction.backend.jdbc.internal.JdbcResourceLocalTransactionCoordinatorBuilderImpl;
|
||||
import org.hibernate.stat.Statistics;
|
||||
|
||||
import org.hibernate.test.cache.infinispan.functional.entities.Item;
|
||||
import org.junit.Test;
|
||||
|
||||
import org.infinispan.Cache;
|
||||
|
@ -38,17 +41,15 @@ import org.jboss.util.naming.NonSerializableFactory;
|
|||
|
||||
import org.jnp.server.Main;
|
||||
import org.jnp.server.SingletonNamingServer;
|
||||
import org.junit.runners.Parameterized;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
/**
|
||||
* // TODO: Document this
|
||||
*
|
||||
* @author Galder Zamarreño
|
||||
* @since // TODO
|
||||
*/
|
||||
public class JndiRegionFactoryTestCase extends SingleNodeTestCase {
|
||||
private static final Log log = LogFactory.getLog( JndiRegionFactoryTestCase.class );
|
||||
public class JndiRegionFactoryTest extends SingleNodeTest {
|
||||
private static final Log log = LogFactory.getLog( JndiRegionFactoryTest.class );
|
||||
private static final String JNDI_NAME = "java:CacheManager";
|
||||
private Main namingMain;
|
||||
private SingletonNamingServer namingServer;
|
||||
|
@ -56,6 +57,11 @@ public class JndiRegionFactoryTestCase extends SingleNodeTestCase {
|
|||
private boolean bindToJndi = true;
|
||||
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
|
||||
protected void cleanupTest() throws Exception {
|
||||
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.
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Class<? extends RegionFactory> getCacheRegionFactory() {
|
||||
return JndiInfinispanRegionFactory.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void afterStandardServiceRegistryBuilt(StandardServiceRegistry ssr) {
|
||||
if ( bindToJndi ) {
|
||||
|
@ -118,46 +119,23 @@ public class JndiRegionFactoryTestCase extends SingleNodeTestCase {
|
|||
|
||||
addEntityCheckCache( sessionFactory() );
|
||||
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() );
|
||||
}
|
||||
|
||||
private void addEntityCheckCache(SessionFactoryImplementor sessionFactory) throws Exception {
|
||||
Item item = new Item( "chris", "Chris's Item" );
|
||||
beginTx();
|
||||
try {
|
||||
Session s = sessionFactory.openSession();
|
||||
s.getTransaction().begin();
|
||||
s.persist( item );
|
||||
s.getTransaction().commit();
|
||||
s.close();
|
||||
}
|
||||
catch (Exception e) {
|
||||
setRollbackOnlyTx( e );
|
||||
}
|
||||
finally {
|
||||
commitOrRollbackTx();
|
||||
}
|
||||
withTxSession(s -> s.persist( item ));
|
||||
|
||||
beginTx();
|
||||
try {
|
||||
Session s = sessionFactory.openSession();
|
||||
Item found = (Item) s.load( Item.class, item.getId() );
|
||||
withTxSession(s -> {
|
||||
Item found = s.load(Item.class, item.getId());
|
||||
Statistics stats = sessionFactory.getStatistics();
|
||||
log.info( stats.toString() );
|
||||
assertEquals( item.getDescription(), found.getDescription() );
|
||||
assertEquals( 0, stats.getSecondLevelCacheMissCount() );
|
||||
assertEquals( 1, stats.getSecondLevelCacheHitCount() );
|
||||
s.delete( found );
|
||||
s.close();
|
||||
}
|
||||
catch (Exception e) {
|
||||
setRollbackOnlyTx( e );
|
||||
}
|
||||
finally {
|
||||
commitOrRollbackTx();
|
||||
}
|
||||
|
||||
log.info(stats.toString());
|
||||
assertEquals(item.getDescription(), found.getDescription());
|
||||
assertEquals(0, stats.getSecondLevelCacheMissCount());
|
||||
assertEquals(1, stats.getSecondLevelCacheHitCount());
|
||||
s.delete(found);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
|
@ -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 <rvansa@redhat.com>
|
||||
*/
|
||||
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());
|
||||
}
|
||||
}
|
|
@ -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 <rvansa@redhat.com>
|
||||
*/
|
||||
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());
|
||||
}
|
||||
|
||||
}
|
|
@ -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 <rvansa@redhat.com>
|
||||
*/
|
||||
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());
|
||||
}
|
||||
|
||||
}
|
|
@ -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 <rvansa@redhat.com>
|
||||
*/
|
||||
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());
|
||||
}
|
||||
|
||||
}
|
232
hibernate-infinispan/src/test/java/org/hibernate/test/cache/infinispan/functional/ReadOnlyTest.java
vendored
Normal file
232
hibernate-infinispan/src/test/java/org/hibernate/test/cache/infinispan/functional/ReadOnlyTest.java
vendored
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
687
hibernate-infinispan/src/test/java/org/hibernate/test/cache/infinispan/functional/ReadWriteTest.java
vendored
Normal file
687
hibernate-infinispan/src/test/java/org/hibernate/test/cache/infinispan/functional/ReadWriteTest.java
vendored
Normal 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 );
|
||||
}
|
||||
|
||||
}
|
|
@ -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 <rvansa@redhat.com>
|
||||
*/
|
||||
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);
|
||||
}
|
||||
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -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" );
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -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)));
|
||||
}
|
||||
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -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" );
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -21,8 +21,7 @@ import org.hibernate.cache.spi.RegionFactory;
|
|||
import org.hibernate.cache.spi.TimestampsRegion;
|
||||
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.util.logging.Log;
|
||||
import org.infinispan.util.logging.LogFactory;
|
||||
|
@ -34,85 +33,89 @@ import org.infinispan.util.logging.LogFactory;
|
|||
* @since 3.5
|
||||
*/
|
||||
public class ClusterAwareRegionFactory implements RegionFactory {
|
||||
|
||||
private static final Log log = LogFactory.getLog(ClusterAwareRegionFactory.class);
|
||||
private static final Hashtable<String, EmbeddedCacheManager> cacheManagers = new Hashtable<String, EmbeddedCacheManager>();
|
||||
|
||||
private final InfinispanRegionFactory delegate =
|
||||
new SingleNodeTestCase.TestInfinispanRegionFactory();
|
||||
private String cacheManagerName;
|
||||
private boolean locallyAdded;
|
||||
|
||||
public ClusterAwareRegionFactory(Properties props) {
|
||||
}
|
||||
|
||||
public static EmbeddedCacheManager getCacheManager(String name) {
|
||||
return cacheManagers.get(name);
|
||||
}
|
||||
|
||||
public static void addCacheManager(String name, EmbeddedCacheManager manager) {
|
||||
cacheManagers.put(name, manager);
|
||||
}
|
||||
|
||||
public static void clearCacheManagers() {
|
||||
for (EmbeddedCacheManager manager : cacheManagers.values()) {
|
||||
try {
|
||||
manager.stop();
|
||||
} catch (Exception e) {
|
||||
log.error("Exception cleaning up CacheManager " + manager, e);
|
||||
}
|
||||
}
|
||||
cacheManagers.clear();
|
||||
}
|
||||
private static final Log log = LogFactory.getLog(ClusterAwareRegionFactory.class);
|
||||
private static final Hashtable<String, EmbeddedCacheManager> cacheManagers = new Hashtable<String, EmbeddedCacheManager>();
|
||||
|
||||
public void start(SessionFactoryOptions settings, Properties properties) throws CacheException {
|
||||
cacheManagerName = properties.getProperty(DualNodeTestCase.NODE_ID_PROP);
|
||||
|
||||
EmbeddedCacheManager existing = getCacheManager(cacheManagerName);
|
||||
locallyAdded = (existing == null);
|
||||
|
||||
if (locallyAdded) {
|
||||
delegate.start(settings, properties);
|
||||
cacheManagers.put(cacheManagerName, delegate.getCacheManager());
|
||||
} else {
|
||||
delegate.setCacheManager(existing);
|
||||
}
|
||||
}
|
||||
private InfinispanRegionFactory delegate;
|
||||
private String cacheManagerName;
|
||||
private boolean locallyAdded;
|
||||
|
||||
public void stop() {
|
||||
if (locallyAdded) cacheManagers.remove(cacheManagerName);
|
||||
delegate.stop();
|
||||
}
|
||||
public ClusterAwareRegionFactory(Properties props) {
|
||||
try {
|
||||
delegate = (InfinispanRegionFactory) ReflectHelper.classForName(props.getProperty(DualNodeTest.REGION_FACTORY_DELEGATE)).newInstance();
|
||||
} catch (Exception e) {
|
||||
throw new IllegalStateException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public CollectionRegion buildCollectionRegion(String regionName, Properties properties,
|
||||
CacheDataDescription metadata) throws CacheException {
|
||||
return delegate.buildCollectionRegion(regionName, properties, metadata);
|
||||
}
|
||||
public static EmbeddedCacheManager getCacheManager(String name) {
|
||||
return cacheManagers.get(name);
|
||||
}
|
||||
|
||||
public EntityRegion buildEntityRegion(String regionName, Properties properties,
|
||||
CacheDataDescription metadata) throws CacheException {
|
||||
return delegate.buildEntityRegion(regionName, properties, metadata);
|
||||
}
|
||||
|
||||
@Override
|
||||
public static void addCacheManager(String name, EmbeddedCacheManager manager) {
|
||||
cacheManagers.put(name, manager);
|
||||
}
|
||||
|
||||
public static void clearCacheManagers() {
|
||||
for (EmbeddedCacheManager manager : cacheManagers.values()) {
|
||||
try {
|
||||
manager.stop();
|
||||
} catch (Exception e) {
|
||||
log.error("Exception cleaning up CacheManager " + manager, e);
|
||||
}
|
||||
}
|
||||
cacheManagers.clear();
|
||||
}
|
||||
|
||||
public void start(SessionFactoryOptions settings, Properties properties) throws CacheException {
|
||||
cacheManagerName = properties.getProperty(DualNodeTest.NODE_ID_PROP);
|
||||
|
||||
EmbeddedCacheManager existing = getCacheManager(cacheManagerName);
|
||||
locallyAdded = (existing == null);
|
||||
|
||||
if (locallyAdded) {
|
||||
delegate.start(settings, properties);
|
||||
cacheManagers.put(cacheManagerName, delegate.getCacheManager());
|
||||
} else {
|
||||
delegate.setCacheManager(existing);
|
||||
}
|
||||
}
|
||||
|
||||
public void stop() {
|
||||
if (locallyAdded) cacheManagers.remove(cacheManagerName);
|
||||
delegate.stop();
|
||||
}
|
||||
|
||||
public CollectionRegion buildCollectionRegion(String regionName, Properties properties,
|
||||
CacheDataDescription metadata) throws CacheException {
|
||||
return delegate.buildCollectionRegion(regionName, properties, metadata);
|
||||
}
|
||||
|
||||
public EntityRegion buildEntityRegion(String regionName, Properties properties,
|
||||
CacheDataDescription metadata) throws CacheException {
|
||||
return delegate.buildEntityRegion(regionName, properties, metadata);
|
||||
}
|
||||
|
||||
@Override
|
||||
public NaturalIdRegion buildNaturalIdRegion(String regionName, Properties properties, CacheDataDescription metadata)
|
||||
throws CacheException {
|
||||
return delegate.buildNaturalIdRegion( regionName, properties, metadata );
|
||||
}
|
||||
|
||||
public QueryResultsRegion buildQueryResultsRegion(String regionName, Properties properties)
|
||||
throws CacheException {
|
||||
return delegate.buildQueryResultsRegion(regionName, properties);
|
||||
}
|
||||
public QueryResultsRegion buildQueryResultsRegion(String regionName, Properties properties)
|
||||
throws CacheException {
|
||||
return delegate.buildQueryResultsRegion(regionName, properties);
|
||||
}
|
||||
|
||||
public TimestampsRegion buildTimestampsRegion(String regionName, Properties properties)
|
||||
throws CacheException {
|
||||
return delegate.buildTimestampsRegion(regionName, properties);
|
||||
}
|
||||
public TimestampsRegion buildTimestampsRegion(String regionName, Properties properties)
|
||||
throws CacheException {
|
||||
return delegate.buildTimestampsRegion(regionName, properties);
|
||||
}
|
||||
|
||||
public boolean isMinimalPutsEnabledByDefault() {
|
||||
return delegate.isMinimalPutsEnabledByDefault();
|
||||
}
|
||||
public boolean isMinimalPutsEnabledByDefault() {
|
||||
return delegate.isMinimalPutsEnabledByDefault();
|
||||
}
|
||||
|
||||
@Override
|
||||
public AccessType getDefaultAccessType() {
|
||||
|
@ -120,6 +123,6 @@ public class ClusterAwareRegionFactory implements RegionFactory {
|
|||
}
|
||||
|
||||
public long nextTimestamp() {
|
||||
return delegate.nextTimestamp();
|
||||
}
|
||||
return delegate.nextTimestamp();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -30,9 +30,9 @@ public class DualNodeJtaPlatformImpl implements JtaPlatform, Configurable {
|
|||
|
||||
@Override
|
||||
public void configure(Map configurationValues) {
|
||||
nodeId = (String) configurationValues.get( DualNodeTestCase.NODE_ID_PROP );
|
||||
if ( nodeId == null ) {
|
||||
throw new HibernateException(DualNodeTestCase.NODE_ID_PROP + " not configured");
|
||||
nodeId = (String) configurationValues.get( DualNodeTest.NODE_ID_PROP );
|
||||
if ( nodeId == null ) {
|
||||
throw new HibernateException(DualNodeTest.NODE_ID_PROP + " not configured");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -6,37 +6,42 @@
|
|||
*/
|
||||
package org.hibernate.test.cache.infinispan.functional.cluster;
|
||||
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import org.hibernate.Session;
|
||||
import org.hibernate.SessionFactory;
|
||||
import org.hibernate.boot.Metadata;
|
||||
import org.hibernate.boot.MetadataSources;
|
||||
import org.hibernate.boot.registry.StandardServiceRegistry;
|
||||
import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
|
||||
import org.hibernate.cfg.AvailableSettings;
|
||||
import org.hibernate.cfg.Environment;
|
||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||
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.functional.AbstractFunctionalTest;
|
||||
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.LogFactory;
|
||||
import org.junit.Before;
|
||||
import org.junit.ClassRule;
|
||||
import org.junit.runners.Parameterized;
|
||||
|
||||
/**
|
||||
* @author Galder Zamarreño
|
||||
* @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
|
||||
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_FIELD = "nodeId";
|
||||
public static final String LOCAL = "local";
|
||||
|
@ -44,16 +49,20 @@ public abstract class DualNodeTestCase extends BaseNonConfigCoreFunctionalTestCa
|
|||
|
||||
private SecondNodeEnvironment secondNodeEnvironment;
|
||||
|
||||
@Override
|
||||
public String[] getMappings() {
|
||||
return new String[] {
|
||||
"cache/infinispan/functional/Contact.hbm.xml", "cache/infinispan/functional/Customer.hbm.xml"
|
||||
};
|
||||
protected void withTxSession(SessionFactory sessionFactory, TxUtil.ThrowingConsumer<Session, Exception> consumer) throws Exception {
|
||||
TxUtil.withTxSession(useJta, sessionFactory, consumer);
|
||||
}
|
||||
|
||||
protected <T> T withTxSessionApply(SessionFactory sessionFactory, TxUtil.ThrowingFunction<Session, T, Exception> consumer) throws Exception {
|
||||
return TxUtil.withTxSessionApply(useJta, sessionFactory, consumer);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getCacheConcurrencyStrategy() {
|
||||
return "transactional";
|
||||
public String[] getMappings() {
|
||||
return new String[] {
|
||||
"cache/infinispan/functional/entities/Contact.hbm.xml",
|
||||
"cache/infinispan/functional/entities/Customer.hbm.xml"
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -65,6 +74,7 @@ public abstract class DualNodeTestCase extends BaseNonConfigCoreFunctionalTestCa
|
|||
|
||||
settings.put( NODE_ID_PROP, LOCAL );
|
||||
settings.put( NODE_ID_FIELD, LOCAL );
|
||||
settings.put( REGION_FACTORY_DELEGATE, regionFactoryClass.getName() );
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -101,10 +111,6 @@ public abstract class DualNodeTestCase extends BaseNonConfigCoreFunctionalTestCa
|
|||
return ClusterAwareRegionFactory.class;
|
||||
}
|
||||
|
||||
protected Class getConnectionProviderClass() {
|
||||
return DualNodeConnectionProviderImpl.class;
|
||||
}
|
||||
|
||||
protected Class getJtaPlatformClass() {
|
||||
return DualNodeJtaPlatformImpl.class;
|
||||
}
|
||||
|
@ -122,20 +128,12 @@ public abstract class DualNodeTestCase extends BaseNonConfigCoreFunctionalTestCa
|
|||
}
|
||||
}
|
||||
|
||||
protected boolean getUseQueryCache() {
|
||||
return true;
|
||||
}
|
||||
|
||||
protected void configureSecondNode(StandardServiceRegistryBuilder ssrb) {
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
protected void applyStandardSettings(Map settings) {
|
||||
settings.put( Environment.CONNECTION_PROVIDER, getConnectionProviderClass().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() ) );
|
||||
settings.put( Environment.CACHE_REGION_FACTORY, ClusterAwareRegionFactory.class.getName() );
|
||||
}
|
||||
|
||||
public class SecondNodeEnvironment {
|
|
@ -6,9 +6,10 @@
|
|||
*/
|
||||
package org.hibernate.test.cache.infinispan.functional.cluster;
|
||||
|
||||
import javax.transaction.TransactionManager;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.Phaser;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
@ -18,8 +19,8 @@ import java.util.concurrent.atomic.AtomicReference;
|
|||
import org.hibernate.Session;
|
||||
import org.hibernate.SessionFactory;
|
||||
import org.hibernate.cache.infinispan.InfinispanRegionFactory;
|
||||
import org.hibernate.test.cache.infinispan.functional.Contact;
|
||||
import org.hibernate.test.cache.infinispan.functional.Customer;
|
||||
import org.hibernate.test.cache.infinispan.functional.entities.Contact;
|
||||
import org.hibernate.test.cache.infinispan.functional.entities.Customer;
|
||||
import org.hibernate.testing.TestForIssue;
|
||||
import org.infinispan.AdvancedCache;
|
||||
import org.infinispan.Cache;
|
||||
|
@ -36,7 +37,6 @@ 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.assertNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
@ -47,28 +47,30 @@ import static org.junit.Assert.assertTrue;
|
|||
* @author Galder Zamarreño
|
||||
* @since 3.5
|
||||
*/
|
||||
public class EntityCollectionInvalidationTestCase extends DualNodeTestCase {
|
||||
private static final Log log = LogFactory.getLog( EntityCollectionInvalidationTestCase.class );
|
||||
public class EntityCollectionInvalidationTest extends DualNodeTest {
|
||||
private static final Log log = LogFactory.getLog( EntityCollectionInvalidationTest.class );
|
||||
|
||||
private static final long SLEEP_TIME = 50l;
|
||||
private static final Integer CUSTOMER_ID = new Integer( 1 );
|
||||
|
||||
static int test = 0;
|
||||
|
||||
private EmbeddedCacheManager localManager, remoteManager;
|
||||
private Cache localCustomerCache, remoteCustomerCache;
|
||||
private Cache localContactCache, remoteContactCache;
|
||||
private Cache localCollectionCache, remoteCollectionCache;
|
||||
private MyListener localListener, remoteListener;
|
||||
private TransactionManager localTM, remoteTM;
|
||||
private SessionFactory localFactory, remoteFactory;
|
||||
|
||||
@Override
|
||||
public List<Object[]> getParameters() {
|
||||
return Arrays.asList(TRANSACTIONAL, READ_WRITE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void startUp() {
|
||||
super.startUp();
|
||||
// Bind a listener to the "local" cache
|
||||
// Our region factory makes its CacheManager available to us
|
||||
localManager = ClusterAwareRegionFactory.getCacheManager( DualNodeTestCase.LOCAL );
|
||||
localManager = ClusterAwareRegionFactory.getCacheManager( DualNodeTest.LOCAL );
|
||||
// Cache localCache = localManager.getCache("entity");
|
||||
localCustomerCache = localManager.getCache( Customer.class.getName() );
|
||||
localContactCache = localManager.getCache( Contact.class.getName() );
|
||||
|
@ -79,7 +81,7 @@ public class EntityCollectionInvalidationTestCase extends DualNodeTestCase {
|
|||
localCollectionCache.addListener( localListener );
|
||||
|
||||
// Bind a listener to the "remote" cache
|
||||
remoteManager = ClusterAwareRegionFactory.getCacheManager( DualNodeTestCase.REMOTE );
|
||||
remoteManager = ClusterAwareRegionFactory.getCacheManager( DualNodeTest.REMOTE );
|
||||
remoteCustomerCache = remoteManager.getCache( Customer.class.getName() );
|
||||
remoteContactCache = remoteManager.getCache( Contact.class.getName() );
|
||||
remoteCollectionCache = remoteManager.getCache( Customer.class.getName() + ".contacts" );
|
||||
|
@ -90,9 +92,6 @@ public class EntityCollectionInvalidationTestCase extends DualNodeTestCase {
|
|||
|
||||
localFactory = sessionFactory();
|
||||
remoteFactory = secondNodeEnvironment().getSessionFactory();
|
||||
|
||||
localTM = DualNodeJtaTransactionManagerImpl.getInstance( DualNodeTestCase.LOCAL );
|
||||
remoteTM = DualNodeJtaTransactionManagerImpl.getInstance( DualNodeTestCase.REMOTE );
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -102,9 +101,9 @@ public class EntityCollectionInvalidationTestCase extends DualNodeTestCase {
|
|||
|
||||
@Override
|
||||
protected void cleanupTest() throws Exception {
|
||||
cleanup(localFactory, localTM);
|
||||
localListener.clear();
|
||||
remoteListener.clear();
|
||||
cleanup(localFactory);
|
||||
localListener.clear();
|
||||
remoteListener.clear();
|
||||
// do not call super.cleanupTest becasue we would clean transaction managers
|
||||
}
|
||||
|
||||
|
@ -115,7 +114,7 @@ public class EntityCollectionInvalidationTestCase extends DualNodeTestCase {
|
|||
assertTrue( localListener.isEmpty() );
|
||||
|
||||
log.debug( "Create node 0" );
|
||||
IdContainer ids = createCustomer( localFactory, localTM );
|
||||
IdContainer ids = createCustomer( localFactory );
|
||||
|
||||
assertTrue( remoteListener.isEmpty() );
|
||||
assertTrue( localListener.isEmpty() );
|
||||
|
@ -126,7 +125,7 @@ public class EntityCollectionInvalidationTestCase extends DualNodeTestCase {
|
|||
|
||||
log.debug( "Find node 0" );
|
||||
// This actually brings the collection into the cache
|
||||
getCustomer( ids.customerId, localFactory, localTM );
|
||||
getCustomer( ids.customerId, localFactory );
|
||||
|
||||
sleep( SLEEP_TIME );
|
||||
|
||||
|
@ -134,7 +133,7 @@ public class EntityCollectionInvalidationTestCase extends DualNodeTestCase {
|
|||
// should read everything from the cache
|
||||
log.debug( "Find(2) node 0" );
|
||||
localListener.clear();
|
||||
getCustomer( ids.customerId, localFactory, localTM );
|
||||
getCustomer( ids.customerId, localFactory );
|
||||
|
||||
// Check the read came from the cache
|
||||
log.debug( "Check cache 0" );
|
||||
|
@ -142,13 +141,13 @@ public class EntityCollectionInvalidationTestCase extends DualNodeTestCase {
|
|||
|
||||
log.debug( "Find node 1" );
|
||||
// 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"
|
||||
// should read everything from the cache
|
||||
log.debug( "Find(2) node 1" );
|
||||
remoteListener.clear();
|
||||
getCustomer( ids.customerId, remoteFactory, remoteTM );
|
||||
getCustomer( ids.customerId, remoteFactory );
|
||||
|
||||
// Check the read came from the cache
|
||||
log.debug( "Check cache 1" );
|
||||
|
@ -156,7 +155,7 @@ public class EntityCollectionInvalidationTestCase extends DualNodeTestCase {
|
|||
|
||||
// Modify customer in remote
|
||||
remoteListener.clear();
|
||||
ids = modifyCustomer( ids.customerId, remoteFactory, remoteTM );
|
||||
ids = modifyCustomer( ids.customerId, remoteFactory );
|
||||
sleep( 250 );
|
||||
assertLoadedFromCache( remoteListener, ids.customerId, ids.contactIds );
|
||||
|
||||
|
@ -178,28 +177,18 @@ public class EntityCollectionInvalidationTestCase extends DualNodeTestCase {
|
|||
remotePPCache.getAdvancedCache().addInterceptor(hookInterceptor, 0);
|
||||
|
||||
IdContainer idContainer = new IdContainer();
|
||||
withTx(localTM, () -> {
|
||||
Session s = localFactory.getCurrentSession();
|
||||
s.getTransaction().begin();
|
||||
withTxSession(localFactory, s -> {
|
||||
Customer customer = new Customer();
|
||||
customer.setName( "JBoss" );
|
||||
s.persist(customer);
|
||||
s.getTransaction().commit();
|
||||
s.close();
|
||||
idContainer.customerId = customer.getId();
|
||||
return null;
|
||||
});
|
||||
// start loading
|
||||
|
||||
Thread getThread = new Thread(() -> {
|
||||
try {
|
||||
withTx(remoteTM, () -> {
|
||||
Session s = remoteFactory.getCurrentSession();
|
||||
s.getTransaction().begin();
|
||||
withTxSession(remoteFactory, s -> {
|
||||
s.get(Customer.class, idContainer.customerId);
|
||||
s.getTransaction().commit();
|
||||
s.close();
|
||||
return null;
|
||||
});
|
||||
} catch (Exception e) {
|
||||
log.error("Failure to get customer", e);
|
||||
|
@ -208,13 +197,9 @@ public class EntityCollectionInvalidationTestCase extends DualNodeTestCase {
|
|||
}, "get-thread");
|
||||
Thread deleteThread = new Thread(() -> {
|
||||
try {
|
||||
withTx(localTM, () -> {
|
||||
Session s = localFactory.getCurrentSession();
|
||||
s.getTransaction().begin();
|
||||
withTxSession(localFactory, s -> {
|
||||
Customer customer = s.get(Customer.class, idContainer.customerId);
|
||||
s.delete(customer);
|
||||
s.getTransaction().commit();
|
||||
return null;
|
||||
});
|
||||
} catch (Exception 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());
|
||||
}
|
||||
|
||||
Customer localCustomer = getCustomer(idContainer.customerId, localFactory, localTM);
|
||||
Customer localCustomer = getCustomer(idContainer.customerId, localFactory);
|
||||
assertNull(localCustomer);
|
||||
Customer remoteCustomer = getCustomer(idContainer.customerId, remoteFactory, remoteTM);
|
||||
Customer remoteCustomer = getCustomer(idContainer.customerId, remoteFactory);
|
||||
assertNull(remoteCustomer);
|
||||
assertTrue(remoteCustomerCache.isEmpty());
|
||||
}
|
||||
|
@ -255,83 +240,47 @@ public class EntityCollectionInvalidationTestCase extends DualNodeTestCase {
|
|||
assertTrue( remoteCollectionCache.isEmpty() );
|
||||
}
|
||||
|
||||
private IdContainer createCustomer(SessionFactory sessionFactory, TransactionManager tm)
|
||||
private IdContainer createCustomer(SessionFactory sessionFactory)
|
||||
throws Exception {
|
||||
log.debug( "CREATE CUSTOMER" );
|
||||
|
||||
tm.begin();
|
||||
Customer customer = new Customer();
|
||||
customer.setName("JBoss");
|
||||
Set<Contact> contacts = new HashSet<Contact>();
|
||||
|
||||
try {
|
||||
Session session = sessionFactory.getCurrentSession();
|
||||
Customer customer = new Customer();
|
||||
customer.setName( "JBoss" );
|
||||
Set<Contact> contacts = new HashSet<Contact>();
|
||||
Contact kabir = new Contact();
|
||||
kabir.setCustomer(customer);
|
||||
kabir.setName("Kabir");
|
||||
kabir.setTlf("1111");
|
||||
contacts.add(kabir);
|
||||
|
||||
Contact 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);
|
||||
|
||||
Contact bill = new Contact();
|
||||
bill.setCustomer( customer );
|
||||
bill.setName( "Bill" );
|
||||
bill.setTlf( "2222" );
|
||||
contacts.add( bill );
|
||||
customer.setContacts(contacts);
|
||||
|
||||
customer.setContacts( contacts );
|
||||
withTxSession(sessionFactory, session -> session.save(customer));
|
||||
|
||||
session.save( customer );
|
||||
tm.commit();
|
||||
IdContainer ids = new IdContainer();
|
||||
ids.customerId = customer.getId();
|
||||
Set contactIds = new HashSet();
|
||||
contactIds.add( kabir.getId() );
|
||||
contactIds.add( bill.getId() );
|
||||
ids.contactIds = contactIds;
|
||||
|
||||
IdContainer ids = new IdContainer();
|
||||
ids.customerId = customer.getId();
|
||||
Set contactIds = new HashSet();
|
||||
contactIds.add( kabir.getId() );
|
||||
contactIds.add( bill.getId() );
|
||||
ids.contactIds = contactIds;
|
||||
|
||||
return ids;
|
||||
}
|
||||
catch (Exception e) {
|
||||
log.error( "Caught exception creating customer", e );
|
||||
try {
|
||||
tm.rollback();
|
||||
}
|
||||
catch (Exception e1) {
|
||||
log.error( "Exception rolling back txn", e1 );
|
||||
}
|
||||
throw e;
|
||||
}
|
||||
finally {
|
||||
log.debug( "CREATE CUSTOMER - END" );
|
||||
}
|
||||
log.debug( "CREATE CUSTOMER - END" );
|
||||
return ids;
|
||||
}
|
||||
|
||||
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 );
|
||||
tm.begin();
|
||||
try {
|
||||
Session session = sessionFactory.getCurrentSession();
|
||||
Customer customer = doGetCustomer( id, session, tm );
|
||||
tm.commit();
|
||||
return customer;
|
||||
}
|
||||
catch (Exception e) {
|
||||
try {
|
||||
tm.rollback();
|
||||
}
|
||||
catch (Exception e1) {
|
||||
log.error( "Exception rolling back txn", e1 );
|
||||
}
|
||||
throw e;
|
||||
}
|
||||
finally {
|
||||
log.debug( "Find customer ended." );
|
||||
}
|
||||
return withTxSessionApply(sessionFactory, session -> doGetCustomer(id, session));
|
||||
}
|
||||
|
||||
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 );
|
||||
if (customer == null) {
|
||||
return null;
|
||||
|
@ -346,15 +295,13 @@ public class EntityCollectionInvalidationTestCase extends DualNodeTestCase {
|
|||
return customer;
|
||||
}
|
||||
|
||||
private IdContainer modifyCustomer(Integer id, SessionFactory sessionFactory, TransactionManager tm)
|
||||
private IdContainer modifyCustomer(Integer id, SessionFactory sessionFactory)
|
||||
throws Exception {
|
||||
log.debug( "Modify customer with id=" + id );
|
||||
tm.begin();
|
||||
try {
|
||||
Session session = sessionFactory.getCurrentSession();
|
||||
return withTxSessionApply(sessionFactory, session -> {
|
||||
IdContainer ids = new IdContainer();
|
||||
Set contactIds = new HashSet();
|
||||
Customer customer = doGetCustomer( id, session, tm );
|
||||
Customer customer = doGetCustomer( id, session );
|
||||
customer.setName( "NewJBoss" );
|
||||
ids.customerId = customer.getId();
|
||||
Set<Contact> contacts = customer.getContacts();
|
||||
|
@ -368,51 +315,26 @@ public class EntityCollectionInvalidationTestCase extends DualNodeTestCase {
|
|||
contact.setCustomer( null );
|
||||
|
||||
session.save( customer );
|
||||
tm.commit();
|
||||
return ids;
|
||||
}
|
||||
catch (Exception e) {
|
||||
try {
|
||||
tm.rollback();
|
||||
}
|
||||
catch (Exception e1) {
|
||||
log.error( "Exception rolling back txn", e1 );
|
||||
}
|
||||
throw e;
|
||||
}
|
||||
finally {
|
||||
log.debug( "Find customer ended." );
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void cleanup(SessionFactory sessionFactory, TransactionManager tm) throws Exception {
|
||||
tm.begin();
|
||||
try {
|
||||
Session session = sessionFactory.getCurrentSession();
|
||||
Customer c = (Customer) session.get( Customer.class, CUSTOMER_ID );
|
||||
if ( c != null ) {
|
||||
private void cleanup(SessionFactory sessionFactory) throws Exception {
|
||||
withTxSession(sessionFactory, session -> {
|
||||
Customer c = (Customer) session.get(Customer.class, CUSTOMER_ID);
|
||||
if (c != null) {
|
||||
Set contacts = c.getContacts();
|
||||
for ( Iterator it = contacts.iterator(); it.hasNext(); ) {
|
||||
session.delete( it.next() );
|
||||
for (Iterator it = contacts.iterator(); it.hasNext(); ) {
|
||||
session.delete(it.next());
|
||||
}
|
||||
c.setContacts( null );
|
||||
session.delete( c );
|
||||
c.setContacts(null);
|
||||
session.delete(c);
|
||||
}
|
||||
// since we don't use orphan removal, some contacts may persist
|
||||
for (Object contact : session.createCriteria(Contact.class).list()) {
|
||||
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) {
|
||||
|
@ -477,7 +399,7 @@ public class EntityCollectionInvalidationTestCase extends DualNodeTestCase {
|
|||
String key = event.getCache().getName() + "#" + event.getKey();
|
||||
log.debug( "MyListener[" + name + "] - Visiting key " + key );
|
||||
// String name = fqn.toString();
|
||||
String token = ".functional.";
|
||||
String token = ".entities.";
|
||||
int index = key.indexOf( token );
|
||||
if ( index > -1 ) {
|
||||
index += token.length();
|
|
@ -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 );
|
||||
// }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -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 );
|
||||
// }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -13,12 +13,12 @@ package org.hibernate.test.cache.infinispan.functional.cluster;
|
|||
* @author Galder Zamarreño
|
||||
* @since 3.5
|
||||
*/
|
||||
public class RepeatableSessionRefreshTest extends SessionRefreshTestCase {
|
||||
private static final String CACHE_CONFIG = "entity-repeatable";
|
||||
public class RepeatableSessionRefreshTest extends SessionRefreshTest {
|
||||
private static final String CACHE_CONFIG = "entity-repeatable";
|
||||
|
||||
@Override
|
||||
protected String getEntityCacheConfigName() {
|
||||
return CACHE_CONFIG;
|
||||
}
|
||||
@Override
|
||||
protected String getEntityCacheConfigName() {
|
||||
return CACHE_CONFIG;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -6,16 +6,16 @@
|
|||
*/
|
||||
package org.hibernate.test.cache.infinispan.functional.cluster;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import javax.transaction.TransactionManager;
|
||||
|
||||
import org.hibernate.SessionFactory;
|
||||
import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
|
||||
import org.hibernate.cache.infinispan.InfinispanRegionFactory;
|
||||
import org.hibernate.cfg.Environment;
|
||||
|
||||
import org.hibernate.test.cache.infinispan.functional.classloader.Account;
|
||||
import org.hibernate.test.cache.infinispan.functional.classloader.ClassLoaderTestDAO;
|
||||
import org.hibernate.test.cache.infinispan.functional.entities.Account;
|
||||
import org.junit.Test;
|
||||
|
||||
import org.infinispan.Cache;
|
||||
|
@ -33,12 +33,16 @@ import static org.junit.Assert.assertNotNull;
|
|||
* @author Galder Zamarreño
|
||||
* @since 3.5
|
||||
*/
|
||||
public class SessionRefreshTestCase extends DualNodeTestCase {
|
||||
private static final Logger log = Logger.getLogger( SessionRefreshTestCase.class );
|
||||
public class SessionRefreshTest extends DualNodeTest {
|
||||
private static final Logger log = Logger.getLogger( SessionRefreshTest.class );
|
||||
|
||||
static int test = 0;
|
||||
private Cache localCache;
|
||||
|
||||
@Override
|
||||
public List<Object[]> getParameters() {
|
||||
return Arrays.asList(TRANSACTIONAL, READ_WRITE);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void configureSecondNode(StandardServiceRegistryBuilder ssrb) {
|
||||
super.configureSecondNode( ssrb );
|
||||
|
@ -57,7 +61,7 @@ public class SessionRefreshTestCase extends DualNodeTestCase {
|
|||
|
||||
@Override
|
||||
public String[] getMappings() {
|
||||
return new String[] {"cache/infinispan/functional/classloader/Account.hbm.xml"};
|
||||
return new String[] {"cache/infinispan/functional/entities/Account.hbm.xml"};
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -70,49 +74,47 @@ public class SessionRefreshTestCase extends DualNodeTestCase {
|
|||
@Test
|
||||
public void testRefreshAfterExternalChange() throws Exception {
|
||||
// First session factory uses a cache
|
||||
CacheContainer localManager = ClusterAwareRegionFactory.getCacheManager( DualNodeTestCase.LOCAL );
|
||||
CacheContainer localManager = ClusterAwareRegionFactory.getCacheManager( DualNodeTest.LOCAL );
|
||||
localCache = localManager.getCache( Account.class.getName() );
|
||||
TransactionManager localTM = DualNodeJtaTransactionManagerImpl.getInstance( DualNodeTestCase.LOCAL );
|
||||
SessionFactory localFactory = sessionFactory();
|
||||
|
||||
// Second session factory doesn't; just needs a transaction manager
|
||||
TransactionManager remoteTM = DualNodeJtaTransactionManagerImpl.getInstance( DualNodeTestCase.REMOTE );
|
||||
SessionFactory remoteFactory = secondNodeEnvironment().getSessionFactory();
|
||||
|
||||
ClassLoaderTestDAO dao0 = new ClassLoaderTestDAO( localFactory, localTM );
|
||||
ClassLoaderTestDAO dao1 = new ClassLoaderTestDAO( remoteFactory, remoteTM );
|
||||
AccountDAO dao0 = new AccountDAO(useJta, localFactory );
|
||||
AccountDAO dao1 = new AccountDAO(useJta, remoteFactory );
|
||||
|
||||
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
|
||||
Account acct1 = dao1.getAccount( id );
|
||||
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
|
||||
dao1.updateAccountBranch( id, DualNodeTestCase.REMOTE );
|
||||
dao1.updateAccountBranch( id, DualNodeTest.REMOTE );
|
||||
|
||||
// dao1's session doesn't touch the cache,
|
||||
// so reading from dao0 should show a stale value from the cache
|
||||
// (we check to confirm the cache is used)
|
||||
Account acct0 = dao0.getAccount( id );
|
||||
assertNotNull( acct0 );
|
||||
assertEquals( DualNodeTestCase.LOCAL, acct0.getBranch() );
|
||||
assertEquals( DualNodeTest.LOCAL, acct0.getBranch() );
|
||||
log.debug( "Contents when re-reading from local: " + TestingUtil.printCache( localCache ) );
|
||||
|
||||
// Now call session.refresh and confirm we get the correct value
|
||||
acct0 = dao0.getAccountWithRefresh( id );
|
||||
assertNotNull( acct0 );
|
||||
assertEquals( DualNodeTestCase.REMOTE, acct0.getBranch() );
|
||||
assertEquals( DualNodeTest.REMOTE, acct0.getBranch() );
|
||||
log.debug( "Contents after refreshing in remote: " + TestingUtil.printCache( localCache ) );
|
||||
|
||||
// Double check with a brand new session, in case the other session
|
||||
// for some reason bypassed the 2nd level cache
|
||||
ClassLoaderTestDAO dao0A = new ClassLoaderTestDAO( localFactory, localTM );
|
||||
AccountDAO dao0A = new AccountDAO(useJta, localFactory );
|
||||
Account acct0A = dao0A.getAccount( id );
|
||||
assertNotNull( acct0A );
|
||||
assertEquals( DualNodeTestCase.REMOTE, acct0A.getBranch() );
|
||||
assertEquals( DualNodeTest.REMOTE, acct0A.getBranch() );
|
||||
log.debug( "Contents after creating a new session: " + TestingUtil.printCache( localCache ) );
|
||||
}
|
||||
}
|
|
@ -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)));
|
||||
}
|
||||
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -5,7 +5,7 @@
|
|||
* 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.GeneratedValue;
|
||||
import javax.persistence.Id;
|
|
@ -6,7 +6,7 @@
|
|||
*/
|
||||
|
||||
//$Id$
|
||||
package org.hibernate.test.cache.infinispan.functional;
|
||||
package org.hibernate.test.cache.infinispan.functional.entities;
|
||||
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.GeneratedValue;
|
73
hibernate-infinispan/src/test/java/org/hibernate/test/cache/infinispan/functional/entities/Contact.java
vendored
Executable file
73
hibernate-infinispan/src/test/java/org/hibernate/test/cache/infinispan/functional/entities/Contact.java
vendored
Executable 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;
|
||||
}
|
||||
|
||||
}
|
49
hibernate-infinispan/src/test/java/org/hibernate/test/cache/infinispan/functional/entities/Customer.java
vendored
Executable file
49
hibernate-infinispan/src/test/java/org/hibernate/test/cache/infinispan/functional/entities/Customer.java
vendored
Executable 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;
|
||||
}
|
||||
}
|
|
@ -4,7 +4,7 @@
|
|||
* 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;
|
||||
package org.hibernate.test.cache.infinispan.functional.entities;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
|
@ -1,4 +1,4 @@
|
|||
package org.hibernate.test.cache.infinispan.functional;
|
||||
package org.hibernate.test.cache.infinispan.functional.entities;
|
||||
|
||||
import javax.persistence.Embeddable;
|
||||
import java.io.Serializable;
|
|
@ -4,7 +4,7 @@
|
|||
* 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;
|
||||
package org.hibernate.test.cache.infinispan.functional.entities;
|
||||
|
||||
import org.hibernate.annotations.NaturalId;
|
||||
import org.hibernate.annotations.NaturalIdCache;
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue