HHH-10030 Add read-write cache concurrency strategy to Infinispan 2LC
* AccessType.READ_WRITE is now supported cache concurrency strategy
* Added checks that we're caching in local or invalidation cache (distributed and replicated cache does not work ATM)
* Refactored test-suite: Running on both transactional and read-write caches (these should yield the same results)
** CustomParemeterized runner is used for that
** Moved all entities used in functional tests to one package
** Removed already disabled tests related to class loaders (not needed since Infinispan 5.1)
(cherry picked from commit 3689924d74
)
This commit is contained in:
parent
b63da4bcdc
commit
64f91f1a15
|
@ -104,12 +104,12 @@ public class InfinispanRegionFactory implements RegionFactory {
|
||||||
*/
|
*/
|
||||||
public static final String INFINISPAN_CONFIG_RESOURCE_PROP = "hibernate.cache.infinispan.cfg";
|
public static final String INFINISPAN_CONFIG_RESOURCE_PROP = "hibernate.cache.infinispan.cfg";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Property name that controls whether Infinispan statistics are enabled.
|
* Property name that controls whether Infinispan statistics are enabled.
|
||||||
* The property value is expected to be a boolean true or false, and it
|
* The property value is expected to be a boolean true or false, and it
|
||||||
* overrides statistic configuration in base Infinispan configuration,
|
* overrides statistic configuration in base Infinispan configuration,
|
||||||
* if provided.
|
* if provided.
|
||||||
*/
|
*/
|
||||||
public static final String INFINISPAN_GLOBAL_STATISTICS_PROP = "hibernate.cache.infinispan.statistics";
|
public static final String INFINISPAN_GLOBAL_STATISTICS_PROP = "hibernate.cache.infinispan.statistics";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
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.CacheException;
|
||||||
import org.hibernate.cache.infinispan.impl.BaseRegion;
|
import org.hibernate.cache.infinispan.impl.BaseRegion;
|
||||||
import org.hibernate.cache.infinispan.util.Caches;
|
import org.hibernate.cache.infinispan.util.Caches;
|
||||||
import org.hibernate.cache.spi.access.SoftLock;
|
|
||||||
import org.hibernate.engine.spi.SessionImplementor;
|
import org.hibernate.engine.spi.SessionImplementor;
|
||||||
import org.infinispan.AdvancedCache;
|
import org.infinispan.AdvancedCache;
|
||||||
import org.infinispan.util.logging.Log;
|
import org.infinispan.util.logging.Log;
|
||||||
import org.infinispan.util.logging.LogFactory;
|
import org.infinispan.util.logging.LogFactory;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Defines the strategy for transactional access to entity or collection data in a Infinispan instance.
|
|
||||||
* <p/>
|
|
||||||
* The intent of this class is to encapsulate common code and serve as a delegate for
|
|
||||||
* {@link org.hibernate.cache.spi.access.EntityRegionAccessStrategy}
|
|
||||||
* and {@link org.hibernate.cache.spi.access.CollectionRegionAccessStrategy} implementations.
|
|
||||||
*
|
*
|
||||||
* @author Brian Stansberry
|
* @author Brian Stansberry
|
||||||
* @author Galder Zamarreño
|
* @author Galder Zamarreño
|
||||||
* @since 3.5
|
* @since 3.5
|
||||||
*/
|
*/
|
||||||
public abstract class TransactionalAccessDelegate {
|
public abstract class InvalidationCacheAccessDelegate implements AccessDelegate {
|
||||||
protected static final Log log = LogFactory.getLog( TransactionalAccessDelegate.class );
|
protected static final Log log = LogFactory.getLog( InvalidationCacheAccessDelegate.class );
|
||||||
protected static final boolean TRACE_ENABLED = log.isTraceEnabled();
|
protected static final boolean TRACE_ENABLED = log.isTraceEnabled();
|
||||||
protected final AdvancedCache cache;
|
protected final AdvancedCache cache;
|
||||||
protected final BaseRegion region;
|
protected final BaseRegion region;
|
||||||
protected final PutFromLoadValidator putValidator;
|
protected final PutFromLoadValidator putValidator;
|
||||||
protected final AdvancedCache<Object, Object> writeCache;
|
protected final AdvancedCache<Object, Object> writeCache;
|
||||||
|
|
||||||
public static TransactionalAccessDelegate create(BaseRegion region, PutFromLoadValidator validator) {
|
public static InvalidationCacheAccessDelegate create(BaseRegion region, PutFromLoadValidator validator) {
|
||||||
if (region.getCache().getCacheConfiguration().transaction().transactionMode().isTransactional()) {
|
if (region.getCache().getCacheConfiguration().transaction().transactionMode().isTransactional()) {
|
||||||
return new TxTransactionalAccessDelegate(region, validator);
|
return new TxInvalidationCacheAccessDelegate(region, validator);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
return new NonTxTransactionalAccessDelegate(region, validator);
|
return new NonTxInvalidationCacheAccessDelegate(region, validator);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -50,7 +44,7 @@ public abstract class TransactionalAccessDelegate {
|
||||||
* @param validator put from load validator
|
* @param validator put from load validator
|
||||||
*/
|
*/
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
protected TransactionalAccessDelegate(BaseRegion region, PutFromLoadValidator validator) {
|
protected InvalidationCacheAccessDelegate(BaseRegion region, PutFromLoadValidator validator) {
|
||||||
this.region = region;
|
this.region = region;
|
||||||
this.cache = region.getCache();
|
this.cache = region.getCache();
|
||||||
this.putValidator = validator;
|
this.putValidator = validator;
|
||||||
|
@ -67,6 +61,7 @@ public abstract class TransactionalAccessDelegate {
|
||||||
* @return the cached object or <tt>null</tt>
|
* @return the cached object or <tt>null</tt>
|
||||||
* @throws CacheException if the cache retrieval failed
|
* @throws CacheException if the cache retrieval failed
|
||||||
*/
|
*/
|
||||||
|
@Override
|
||||||
@SuppressWarnings("UnusedParameters")
|
@SuppressWarnings("UnusedParameters")
|
||||||
public Object get(SessionImplementor session, Object key, long txTimestamp) throws CacheException {
|
public Object get(SessionImplementor session, Object key, long txTimestamp) throws CacheException {
|
||||||
if ( !region.checkValid() ) {
|
if ( !region.checkValid() ) {
|
||||||
|
@ -79,16 +74,7 @@ public abstract class TransactionalAccessDelegate {
|
||||||
return val;
|
return val;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
@Override
|
||||||
* Attempt to cache an object, after loading from the database.
|
|
||||||
*
|
|
||||||
* @param session Current session
|
|
||||||
* @param key The item key
|
|
||||||
* @param value The item
|
|
||||||
* @param txTimestamp a timestamp prior to the transaction start time
|
|
||||||
* @param version the item version number
|
|
||||||
* @return <tt>true</tt> if the object was successfully cached
|
|
||||||
*/
|
|
||||||
public boolean putFromLoad(SessionImplementor session, Object key, Object value, long txTimestamp, Object version) {
|
public boolean putFromLoad(SessionImplementor session, Object key, Object value, long txTimestamp, Object version) {
|
||||||
return putFromLoad(session, key, value, txTimestamp, version, false );
|
return putFromLoad(session, key, value, txTimestamp, version, false );
|
||||||
}
|
}
|
||||||
|
@ -106,6 +92,7 @@ public abstract class TransactionalAccessDelegate {
|
||||||
* @return <tt>true</tt> if the object was successfully cached
|
* @return <tt>true</tt> if the object was successfully cached
|
||||||
* @throws CacheException if storing the object failed
|
* @throws CacheException if storing the object failed
|
||||||
*/
|
*/
|
||||||
|
@Override
|
||||||
@SuppressWarnings("UnusedParameters")
|
@SuppressWarnings("UnusedParameters")
|
||||||
public boolean putFromLoad(SessionImplementor session, Object key, Object value, long txTimestamp, Object version, boolean minimalPutOverride)
|
public boolean putFromLoad(SessionImplementor session, Object key, Object value, long txTimestamp, Object version, boolean minimalPutOverride)
|
||||||
throws CacheException {
|
throws CacheException {
|
||||||
|
@ -143,41 +130,7 @@ public abstract class TransactionalAccessDelegate {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
@Override
|
||||||
* Called after an item has been inserted (before the transaction completes),
|
|
||||||
* instead of calling evict().
|
|
||||||
*
|
|
||||||
* @param session Current session
|
|
||||||
* @param key The item key
|
|
||||||
* @param value The item
|
|
||||||
* @param version The item's version value
|
|
||||||
* @return Were the contents of the cache actual changed by this operation?
|
|
||||||
* @throws CacheException if the insert fails
|
|
||||||
*/
|
|
||||||
public abstract boolean insert(SessionImplementor session, Object key, Object value, Object version) throws CacheException;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Called after an item has been updated (before the transaction completes),
|
|
||||||
* instead of calling evict().
|
|
||||||
*
|
|
||||||
* @param session Current session
|
|
||||||
* @param key The item key
|
|
||||||
* @param value The item
|
|
||||||
* @param currentVersion The item's current version value
|
|
||||||
* @param previousVersion The item's previous version value
|
|
||||||
* @return Whether the contents of the cache actual changed by this operation
|
|
||||||
* @throws CacheException if the update fails
|
|
||||||
*/
|
|
||||||
public abstract boolean update(SessionImplementor session, Object key, Object value, Object currentVersion, Object previousVersion)
|
|
||||||
throws CacheException;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Called after an item has become stale (before the transaction completes).
|
|
||||||
*
|
|
||||||
* @param session Current session
|
|
||||||
* @param key The key of the item to remove
|
|
||||||
* @throws CacheException if removing the cached item fails
|
|
||||||
*/
|
|
||||||
public void remove(SessionImplementor session, Object key) throws CacheException {
|
public void remove(SessionImplementor session, Object key) throws CacheException {
|
||||||
if ( !putValidator.beginInvalidatingKey(session, key)) {
|
if ( !putValidator.beginInvalidatingKey(session, key)) {
|
||||||
throw new CacheException(
|
throw new CacheException(
|
||||||
|
@ -196,11 +149,7 @@ public abstract class TransactionalAccessDelegate {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
@Override
|
||||||
* Called to evict data from the entire region
|
|
||||||
*
|
|
||||||
* @throws CacheException if eviction the region fails
|
|
||||||
*/
|
|
||||||
public void removeAll() throws CacheException {
|
public void removeAll() throws CacheException {
|
||||||
try {
|
try {
|
||||||
if (!putValidator.beginInvalidatingRegion()) {
|
if (!putValidator.beginInvalidatingRegion()) {
|
||||||
|
@ -213,23 +162,12 @@ public abstract class TransactionalAccessDelegate {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
@Override
|
||||||
* Forcibly evict an item from the cache immediately without regard for transaction
|
|
||||||
* isolation.
|
|
||||||
*
|
|
||||||
* @param key The key of the item to remove
|
|
||||||
* @throws CacheException if evicting the item fails
|
|
||||||
*/
|
|
||||||
public void evict(Object key) throws CacheException {
|
public void evict(Object key) throws CacheException {
|
||||||
writeCache.remove( key );
|
writeCache.remove( key );
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
@Override
|
||||||
* Forcibly evict all items from the cache immediately without regard for transaction
|
|
||||||
* isolation.
|
|
||||||
*
|
|
||||||
* @throws CacheException if evicting items fails
|
|
||||||
*/
|
|
||||||
public void evictAll() throws CacheException {
|
public void evictAll() throws CacheException {
|
||||||
try {
|
try {
|
||||||
if (!putValidator.beginInvalidatingRegion()) {
|
if (!putValidator.beginInvalidatingRegion()) {
|
||||||
|
@ -245,16 +183,7 @@ public abstract class TransactionalAccessDelegate {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
@Override
|
||||||
* Called when we have finished the attempted update/delete (which may or
|
|
||||||
* may not have been successful), after transaction completion. This method
|
|
||||||
* is used by "asynchronous" concurrency strategies.
|
|
||||||
*
|
|
||||||
*
|
|
||||||
* @param session
|
|
||||||
* @param key The item key
|
|
||||||
* @throws org.hibernate.cache.CacheException Propogated from underlying {@link org.hibernate.cache.spi.Region}
|
|
||||||
*/
|
|
||||||
public void unlockItem(SessionImplementor session, Object key) throws CacheException {
|
public void unlockItem(SessionImplementor session, Object key) throws CacheException {
|
||||||
if ( !putValidator.endInvalidatingKey(session, key) ) {
|
if ( !putValidator.endInvalidatingKey(session, key) ) {
|
||||||
// TODO: localization
|
// TODO: localization
|
||||||
|
@ -262,50 +191,4 @@ public abstract class TransactionalAccessDelegate {
|
||||||
+ region.getName() + "; the key won't be cached until invalidation expires.");
|
+ region.getName() + "; the key won't be cached until invalidation expires.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Called after an item has been inserted (after the transaction completes),
|
|
||||||
* instead of calling release().
|
|
||||||
* This method is used by "asynchronous" concurrency strategies.
|
|
||||||
*
|
|
||||||
*
|
|
||||||
* @param session
|
|
||||||
* @param key The item key
|
|
||||||
* @param value The item
|
|
||||||
* @param version The item's version value
|
|
||||||
* @return Were the contents of the cache actual changed by this operation?
|
|
||||||
* @throws CacheException Propagated from underlying {@link org.hibernate.cache.spi.Region}
|
|
||||||
*/
|
|
||||||
public boolean afterInsert(SessionImplementor session, Object key, Object value, Object version) {
|
|
||||||
if ( !putValidator.endInvalidatingKey(session, key) ) {
|
|
||||||
// TODO: localization
|
|
||||||
log.warn("Failed to end invalidating pending putFromLoad calls for key " + key + " from region "
|
|
||||||
+ region.getName() + "; the key won't be cached until invalidation expires.");
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Called after an item has been updated (after the transaction completes),
|
|
||||||
* instead of calling release(). This method is used by "asynchronous"
|
|
||||||
* concurrency strategies.
|
|
||||||
*
|
|
||||||
*
|
|
||||||
* @param session
|
|
||||||
* @param key The item key
|
|
||||||
* @param value The item
|
|
||||||
* @param currentVersion The item's current version value
|
|
||||||
* @param previousVersion The item's previous version value
|
|
||||||
* @param lock The lock previously obtained from {@link #lockItem}
|
|
||||||
* @return Were the contents of the cache actual changed by this operation?
|
|
||||||
* @throws CacheException Propagated from underlying {@link org.hibernate.cache.spi.Region}
|
|
||||||
*/
|
|
||||||
public boolean afterUpdate(SessionImplementor session, Object key, Object value, Object currentVersion, Object previousVersion, SoftLock lock) {
|
|
||||||
if ( !putValidator.endInvalidatingKey(session, key) ) {
|
|
||||||
// TODO: localization
|
|
||||||
log.warn("Failed to end invalidating pending putFromLoad calls for key " + key + " from region "
|
|
||||||
+ region.getName() + "; the key won't be cached until invalidation expires.");
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
}
|
|
@ -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.engine.spi.SessionImplementor;
|
||||||
import org.hibernate.resource.transaction.TransactionCoordinator;
|
import org.hibernate.resource.transaction.TransactionCoordinator;
|
||||||
import org.infinispan.AdvancedCache;
|
import org.infinispan.AdvancedCache;
|
||||||
|
import org.infinispan.configuration.cache.CacheMode;
|
||||||
import org.infinispan.configuration.cache.Configuration;
|
import org.infinispan.configuration.cache.Configuration;
|
||||||
import org.infinispan.configuration.cache.ConfigurationBuilder;
|
import org.infinispan.configuration.cache.ConfigurationBuilder;
|
||||||
import org.infinispan.interceptors.EntryWrappingInterceptor;
|
import org.infinispan.interceptors.EntryWrappingInterceptor;
|
||||||
|
@ -34,8 +35,8 @@ import org.infinispan.util.logging.Log;
|
||||||
import org.infinispan.util.logging.LogFactory;
|
import org.infinispan.util.logging.LogFactory;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Encapsulates logic to allow a {@link TransactionalAccessDelegate} to determine
|
* Encapsulates logic to allow a {@link InvalidationCacheAccessDelegate} to determine
|
||||||
* whether a {@link TransactionalAccessDelegate#putFromLoad(org.hibernate.engine.spi.SessionImplementor, Object, Object, long, Object, boolean)}
|
* whether a {@link InvalidationCacheAccessDelegate#putFromLoad(org.hibernate.engine.spi.SessionImplementor, Object, Object, long, Object, boolean)}
|
||||||
* call should be allowed to update the cache. A <code>putFromLoad</code> has
|
* call should be allowed to update the cache. A <code>putFromLoad</code> has
|
||||||
* the potential to store stale data, since the data may have been removed from the
|
* the potential to store stale data, since the data may have been removed from the
|
||||||
* database and the cache between the time when the data was read from the database
|
* database and the cache between the time when the data was read from the database
|
||||||
|
@ -134,13 +135,10 @@ public class PutFromLoadValidator {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new put from load validator instance.
|
* Creates a new put from load validator instance.
|
||||||
*
|
* @param cache Cache instance on which to store pending put information.
|
||||||
* @param cache Cache instance on which to store pending put information.
|
* @param cacheManager where to find a cache to store pending put information
|
||||||
* @param cacheManager where to find a cache to store pending put information
|
*/
|
||||||
*/
|
public PutFromLoadValidator(AdvancedCache cache, EmbeddedCacheManager cacheManager) {
|
||||||
public PutFromLoadValidator(AdvancedCache cache,
|
|
||||||
EmbeddedCacheManager cacheManager) {
|
|
||||||
|
|
||||||
Configuration cacheConfiguration = cache.getCacheConfiguration();
|
Configuration cacheConfiguration = cache.getCacheConfiguration();
|
||||||
Configuration pendingPutsConfiguration = cacheManager.getCacheConfiguration(InfinispanRegionFactory.PENDING_PUTS_CACHE_NAME);
|
Configuration pendingPutsConfiguration = cacheManager.getCacheConfiguration(InfinispanRegionFactory.PENDING_PUTS_CACHE_NAME);
|
||||||
ConfigurationBuilder configurationBuilder = new ConfigurationBuilder();
|
ConfigurationBuilder configurationBuilder = new ConfigurationBuilder();
|
||||||
|
@ -155,11 +153,14 @@ public class PutFromLoadValidator {
|
||||||
else {
|
else {
|
||||||
throw new IllegalArgumentException("Pending puts cache needs to have maxIdle expiration set!");
|
throw new IllegalArgumentException("Pending puts cache needs to have maxIdle expiration set!");
|
||||||
}
|
}
|
||||||
|
CacheMode cacheMode = cache.getCacheConfiguration().clustering().cacheMode();
|
||||||
// Since we need to intercept both invalidations of entries that are in the cache and those
|
// Since we need to intercept both invalidations of entries that are in the cache and those
|
||||||
// that are not, we need to use custom interceptor, not listeners (which fire only for present entries).
|
// that are not, we need to use custom interceptor, not listeners (which fire only for present entries).
|
||||||
NonTxPutFromLoadInterceptor nonTxPutFromLoadInterceptor = null;
|
NonTxPutFromLoadInterceptor nonTxPutFromLoadInterceptor = null;
|
||||||
if (cacheConfiguration.clustering().cacheMode().isClustered()) {
|
if (cacheMode.isClustered()) {
|
||||||
|
if (!cacheMode.isInvalidation()) {
|
||||||
|
throw new IllegalArgumentException("PutFromLoadValidator in clustered caches requires invalidation mode.");
|
||||||
|
}
|
||||||
List<CommandInterceptor> interceptorChain = cache.getInterceptorChain();
|
List<CommandInterceptor> interceptorChain = cache.getInterceptorChain();
|
||||||
log.debug("Interceptor chain was: " + interceptorChain);
|
log.debug("Interceptor chain was: " + interceptorChain);
|
||||||
int position = 0;
|
int position = 0;
|
||||||
|
|
|
@ -8,17 +8,16 @@ package org.hibernate.cache.infinispan.access;
|
||||||
|
|
||||||
import org.hibernate.cache.CacheException;
|
import org.hibernate.cache.CacheException;
|
||||||
import org.hibernate.cache.infinispan.impl.BaseRegion;
|
import org.hibernate.cache.infinispan.impl.BaseRegion;
|
||||||
|
import org.hibernate.cache.spi.access.SoftLock;
|
||||||
import org.hibernate.engine.spi.SessionImplementor;
|
import org.hibernate.engine.spi.SessionImplementor;
|
||||||
import org.hibernate.resource.transaction.TransactionCoordinator;
|
|
||||||
import org.hibernate.resource.transaction.spi.TransactionStatus;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Delegate for non-transactional caches
|
* Delegate for transactional caches
|
||||||
*
|
*
|
||||||
* @author Radim Vansa <rvansa@redhat.com>
|
* @author Radim Vansa <rvansa@redhat.com>
|
||||||
*/
|
*/
|
||||||
public class NonTxTransactionalAccessDelegate extends TransactionalAccessDelegate {
|
public class TxInvalidationCacheAccessDelegate extends InvalidationCacheAccessDelegate {
|
||||||
public NonTxTransactionalAccessDelegate(BaseRegion region, PutFromLoadValidator validator) {
|
public TxInvalidationCacheAccessDelegate(BaseRegion region, PutFromLoadValidator validator) {
|
||||||
super(region, validator);
|
super(region, validator);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -32,14 +31,14 @@ public class NonTxTransactionalAccessDelegate extends TransactionalAccessDelegat
|
||||||
// We need to be invalidating even for regular writes; if we were not and the write was followed by eviction
|
// We need to be invalidating even for regular writes; if we were not and the write was followed by eviction
|
||||||
// (or any other invalidation), naked put that was started after the eviction ended but before this insert
|
// (or any other invalidation), naked put that was started after the eviction ended but before this insert
|
||||||
// ended could insert the stale entry into the cache (since the entry was removed by eviction).
|
// ended could insert the stale entry into the cache (since the entry was removed by eviction).
|
||||||
if ( !putValidator.beginInvalidatingWithPFER(session, key, value)) {
|
if ( !putValidator.beginInvalidatingKey(session, key)) {
|
||||||
throw new CacheException(
|
throw new CacheException(
|
||||||
"Failed to invalidate pending putFromLoad calls for key " + key + " from region " + region.getName()
|
"Failed to invalidate pending putFromLoad calls for key " + key + " from region " + region.getName()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
putValidator.setCurrentSession(session);
|
putValidator.setCurrentSession(session);
|
||||||
try {
|
try {
|
||||||
writeCache.remove(key);
|
writeCache.put(key, value);
|
||||||
}
|
}
|
||||||
finally {
|
finally {
|
||||||
putValidator.resetCurrentSession();
|
putValidator.resetCurrentSession();
|
||||||
|
@ -58,14 +57,14 @@ public class NonTxTransactionalAccessDelegate extends TransactionalAccessDelegat
|
||||||
// We need to be invalidating even for regular writes; if we were not and the write was followed by eviction
|
// We need to be invalidating even for regular writes; if we were not and the write was followed by eviction
|
||||||
// (or any other invalidation), naked put that was started after the eviction ended but before this update
|
// (or any other invalidation), naked put that was started after the eviction ended but before this update
|
||||||
// ended could insert the stale entry into the cache (since the entry was removed by eviction).
|
// ended could insert the stale entry into the cache (since the entry was removed by eviction).
|
||||||
if ( !putValidator.beginInvalidatingWithPFER(session, key, value)) {
|
if ( !putValidator.beginInvalidatingKey(session, key)) {
|
||||||
throw new CacheException(
|
throw new CacheException(
|
||||||
"Failed to invalidate pending putFromLoad calls for key " + key + " from region " + region.getName()
|
"Failed to invalidate pending putFromLoad calls for key " + key + " from region " + region.getName()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
putValidator.setCurrentSession(session);
|
putValidator.setCurrentSession(session);
|
||||||
try {
|
try {
|
||||||
writeCache.remove(key);
|
writeCache.put(key, value);
|
||||||
}
|
}
|
||||||
finally {
|
finally {
|
||||||
putValidator.resetCurrentSession();
|
putValidator.resetCurrentSession();
|
||||||
|
@ -74,13 +73,22 @@ public class NonTxTransactionalAccessDelegate extends TransactionalAccessDelegat
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void unlockItem(SessionImplementor session, Object key) throws CacheException {
|
public boolean afterInsert(SessionImplementor session, Object key, Object value, Object version) {
|
||||||
TransactionCoordinator tc = session.getTransactionCoordinator();
|
if ( !putValidator.endInvalidatingKey(session, key) ) {
|
||||||
boolean doPFER = tc != null && tc.getTransactionDriverControl().getStatus() == TransactionStatus.COMMITTED;
|
|
||||||
if ( !putValidator.endInvalidatingKey(session, key, doPFER) ) {
|
|
||||||
// TODO: localization
|
// TODO: localization
|
||||||
log.warn("Failed to end invalidating pending putFromLoad calls for key " + key + " from region "
|
log.warn("Failed to end invalidating pending putFromLoad calls for key " + key + " from region "
|
||||||
+ region.getName() + "; the key won't be cached until invalidation expires.");
|
+ region.getName() + "; the key won't be cached until invalidation expires.");
|
||||||
}
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean afterUpdate(SessionImplementor session, Object key, Object value, Object currentVersion, Object previousVersion, SoftLock lock) {
|
||||||
|
if ( !putValidator.endInvalidatingKey(session, key) ) {
|
||||||
|
// TODO: localization
|
||||||
|
log.warn("Failed to end invalidating pending putFromLoad calls for key " + key + " from region "
|
||||||
|
+ region.getName() + "; the key won't be cached until invalidation expires.");
|
||||||
|
}
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -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;
|
package org.hibernate.cache.infinispan.collection;
|
||||||
|
|
||||||
import org.hibernate.cache.CacheException;
|
import org.hibernate.cache.CacheException;
|
||||||
import org.hibernate.cache.infinispan.access.TransactionalAccessDelegate;
|
import org.hibernate.cache.infinispan.access.AccessDelegate;
|
||||||
import org.hibernate.cache.spi.CollectionRegion;
|
import org.hibernate.cache.spi.CollectionRegion;
|
||||||
import org.hibernate.cache.spi.access.CollectionRegionAccessStrategy;
|
import org.hibernate.cache.spi.access.CollectionRegionAccessStrategy;
|
||||||
import org.hibernate.cache.spi.access.SoftLock;
|
import org.hibernate.cache.spi.access.SoftLock;
|
||||||
|
@ -16,21 +16,19 @@ import org.hibernate.engine.spi.SessionImplementor;
|
||||||
import org.hibernate.persister.collection.CollectionPersister;
|
import org.hibernate.persister.collection.CollectionPersister;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Transactional collection region access for Infinispan.
|
* Collection region access for Infinispan.
|
||||||
*
|
*
|
||||||
* @author Chris Bredesen
|
* @author Chris Bredesen
|
||||||
* @author Galder Zamarreño
|
* @author Galder Zamarreño
|
||||||
* @since 3.5
|
* @since 3.5
|
||||||
*/
|
*/
|
||||||
class TransactionalAccess implements CollectionRegionAccessStrategy {
|
class CollectionAccess implements CollectionRegionAccessStrategy {
|
||||||
|
|
||||||
private final CollectionRegionImpl region;
|
private final CollectionRegionImpl region;
|
||||||
|
private final AccessDelegate delegate;
|
||||||
|
|
||||||
private final TransactionalAccessDelegate delegate;
|
CollectionAccess(CollectionRegionImpl region, AccessDelegate delegate) {
|
||||||
|
|
||||||
TransactionalAccess(CollectionRegionImpl region) {
|
|
||||||
this.region = region;
|
this.region = region;
|
||||||
this.delegate = TransactionalAccessDelegate.create( region, region.getPutFromLoadValidator() );
|
this.delegate = delegate;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void evict(Object key) throws CacheException {
|
public void evict(Object key) throws CacheException {
|
|
@ -7,7 +7,8 @@
|
||||||
package org.hibernate.cache.infinispan.collection;
|
package org.hibernate.cache.infinispan.collection;
|
||||||
|
|
||||||
import org.hibernate.cache.CacheException;
|
import org.hibernate.cache.CacheException;
|
||||||
import org.hibernate.cache.infinispan.access.PutFromLoadValidator;
|
import org.hibernate.cache.infinispan.access.AccessDelegate;
|
||||||
|
import org.hibernate.cache.infinispan.access.InvalidationCacheAccessDelegate;
|
||||||
import org.hibernate.cache.infinispan.impl.BaseTransactionalDataRegion;
|
import org.hibernate.cache.infinispan.impl.BaseTransactionalDataRegion;
|
||||||
import org.hibernate.cache.spi.CacheDataDescription;
|
import org.hibernate.cache.spi.CacheDataDescription;
|
||||||
import org.hibernate.cache.spi.CacheKeysFactory;
|
import org.hibernate.cache.spi.CacheKeysFactory;
|
||||||
|
@ -27,17 +28,16 @@ import javax.transaction.TransactionManager;
|
||||||
* @since 3.5
|
* @since 3.5
|
||||||
*/
|
*/
|
||||||
public class CollectionRegionImpl extends BaseTransactionalDataRegion implements CollectionRegion {
|
public class CollectionRegionImpl extends BaseTransactionalDataRegion implements CollectionRegion {
|
||||||
|
/**
|
||||||
/**
|
* Construct a collection region
|
||||||
* Construct a collection region
|
*
|
||||||
*
|
* @param cache instance to store collection instances
|
||||||
* @param cache instance to store collection instances
|
* @param name of collection type
|
||||||
* @param name of collection type
|
|
||||||
* @param transactionManager
|
* @param transactionManager
|
||||||
* @param metadata for the collection type
|
* @param metadata for the collection type
|
||||||
* @param factory for the region
|
* @param factory for the region
|
||||||
* @param cacheKeysFactory factory for cache keys
|
* @param cacheKeysFactory factory for cache keys
|
||||||
*/
|
*/
|
||||||
public CollectionRegionImpl(
|
public CollectionRegionImpl(
|
||||||
AdvancedCache cache, String name, TransactionManager transactionManager,
|
AdvancedCache cache, String name, TransactionManager transactionManager,
|
||||||
CacheDataDescription metadata, RegionFactory factory, CacheKeysFactory cacheKeysFactory) {
|
CacheDataDescription metadata, RegionFactory factory, CacheKeysFactory cacheKeysFactory) {
|
||||||
|
@ -46,16 +46,16 @@ public class CollectionRegionImpl extends BaseTransactionalDataRegion implements
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public CollectionRegionAccessStrategy buildAccessStrategy(AccessType accessType) throws CacheException {
|
public CollectionRegionAccessStrategy buildAccessStrategy(AccessType accessType) throws CacheException {
|
||||||
if ( AccessType.READ_ONLY.equals( accessType )
|
checkAccessType( accessType );
|
||||||
|| AccessType.TRANSACTIONAL.equals( accessType ) ) {
|
getValidator();
|
||||||
return new TransactionalAccess( this );
|
AccessDelegate delegate = InvalidationCacheAccessDelegate.create(this, getValidator());
|
||||||
|
switch ( accessType ) {
|
||||||
|
case READ_ONLY:
|
||||||
|
case READ_WRITE:
|
||||||
|
case TRANSACTIONAL:
|
||||||
|
return new CollectionAccess( this, delegate );
|
||||||
|
default:
|
||||||
|
throw new CacheException( "Unsupported access type [" + accessType.getExternalName() + "]" );
|
||||||
}
|
}
|
||||||
|
|
||||||
throw new CacheException( "Unsupported access type [" + accessType.getExternalName() + "]" );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public PutFromLoadValidator getPutFromLoadValidator() {
|
|
||||||
return new PutFromLoadValidator( cache );
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
package org.hibernate.cache.infinispan.entity;
|
package org.hibernate.cache.infinispan.entity;
|
||||||
|
|
||||||
import org.hibernate.cache.CacheException;
|
import org.hibernate.cache.CacheException;
|
||||||
import org.hibernate.cache.infinispan.access.PutFromLoadValidator;
|
import org.hibernate.cache.infinispan.access.InvalidationCacheAccessDelegate;
|
||||||
import org.hibernate.cache.infinispan.impl.BaseTransactionalDataRegion;
|
import org.hibernate.cache.infinispan.impl.BaseTransactionalDataRegion;
|
||||||
import org.hibernate.cache.spi.CacheDataDescription;
|
import org.hibernate.cache.spi.CacheDataDescription;
|
||||||
import org.hibernate.cache.spi.CacheKeysFactory;
|
import org.hibernate.cache.spi.CacheKeysFactory;
|
||||||
|
@ -28,17 +28,16 @@ import javax.transaction.TransactionManager;
|
||||||
* @since 3.5
|
* @since 3.5
|
||||||
*/
|
*/
|
||||||
public class EntityRegionImpl extends BaseTransactionalDataRegion implements EntityRegion {
|
public class EntityRegionImpl extends BaseTransactionalDataRegion implements EntityRegion {
|
||||||
|
/**
|
||||||
/**
|
* Construct a entity region
|
||||||
* Construct a entity region
|
*
|
||||||
*
|
* @param cache instance to store entity instances
|
||||||
* @param cache instance to store entity instances
|
* @param name of entity type
|
||||||
* @param name of entity type
|
|
||||||
* @param transactionManager
|
* @param transactionManager
|
||||||
* @param metadata for the entity type
|
* @param metadata for the entity type
|
||||||
* @param factory for the region
|
* @param factory for the region
|
||||||
* @param cacheKeysFactory factory for cache keys
|
* @param cacheKeysFactory factory for cache keys
|
||||||
*/
|
*/
|
||||||
public EntityRegionImpl(
|
public EntityRegionImpl(
|
||||||
AdvancedCache cache, String name, TransactionManager transactionManager,
|
AdvancedCache cache, String name, TransactionManager transactionManager,
|
||||||
CacheDataDescription metadata, RegionFactory factory, CacheKeysFactory cacheKeysFactory) {
|
CacheDataDescription metadata, RegionFactory factory, CacheKeysFactory cacheKeysFactory) {
|
||||||
|
@ -47,22 +46,19 @@ public class EntityRegionImpl extends BaseTransactionalDataRegion implements Ent
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public EntityRegionAccessStrategy buildAccessStrategy(AccessType accessType) throws CacheException {
|
public EntityRegionAccessStrategy buildAccessStrategy(AccessType accessType) throws CacheException {
|
||||||
|
checkAccessType(accessType);
|
||||||
|
if ( !getCacheDataDescription().isMutable() ) {
|
||||||
|
accessType = AccessType.READ_ONLY;
|
||||||
|
}
|
||||||
|
InvalidationCacheAccessDelegate accessDelegate = InvalidationCacheAccessDelegate.create(this, getValidator());
|
||||||
switch ( accessType ) {
|
switch ( accessType ) {
|
||||||
case READ_ONLY:
|
case READ_ONLY:
|
||||||
return new ReadOnlyAccess( this );
|
return new ReadOnlyAccess( this, accessDelegate);
|
||||||
|
case READ_WRITE:
|
||||||
case TRANSACTIONAL:
|
case TRANSACTIONAL:
|
||||||
if ( getCacheDataDescription().isMutable() ) {
|
return new ReadWriteAccess( this, accessDelegate);
|
||||||
return new TransactionalAccess( this );
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
return new ReadOnlyAccess( this );
|
|
||||||
}
|
|
||||||
default:
|
default:
|
||||||
throw new CacheException( "Unsupported access type [" + accessType.getExternalName() + "]" );
|
throw new CacheException( "Unsupported access type [" + accessType.getExternalName() + "]" );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public PutFromLoadValidator getPutFromLoadValidator() {
|
|
||||||
return new PutFromLoadValidator( cache );
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,21 +7,66 @@
|
||||||
package org.hibernate.cache.infinispan.entity;
|
package org.hibernate.cache.infinispan.entity;
|
||||||
|
|
||||||
import org.hibernate.cache.CacheException;
|
import org.hibernate.cache.CacheException;
|
||||||
|
import org.hibernate.cache.infinispan.access.InvalidationCacheAccessDelegate;
|
||||||
|
import org.hibernate.cache.spi.EntityRegion;
|
||||||
|
import org.hibernate.cache.spi.access.EntityRegionAccessStrategy;
|
||||||
import org.hibernate.cache.spi.access.SoftLock;
|
import org.hibernate.cache.spi.access.SoftLock;
|
||||||
|
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||||
import org.hibernate.engine.spi.SessionImplementor;
|
import org.hibernate.engine.spi.SessionImplementor;
|
||||||
|
import org.hibernate.persister.entity.EntityPersister;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A specialization of {@link TransactionalAccess} that ensures we never update data. Infinispan
|
* A specialization of {@link ReadWriteAccess} that ensures we never update data.
|
||||||
* access is always transactional.
|
|
||||||
*
|
*
|
||||||
* @author Chris Bredesen
|
* @author Chris Bredesen
|
||||||
* @author Galder Zamarreño
|
* @author Galder Zamarreño
|
||||||
* @since 3.5
|
* @since 3.5
|
||||||
*/
|
*/
|
||||||
class ReadOnlyAccess extends TransactionalAccess {
|
class ReadOnlyAccess implements EntityRegionAccessStrategy {
|
||||||
|
|
||||||
ReadOnlyAccess(EntityRegionImpl region) {
|
protected final EntityRegionImpl region;
|
||||||
super( region );
|
protected final InvalidationCacheAccessDelegate delegate;
|
||||||
|
|
||||||
|
ReadOnlyAccess(EntityRegionImpl region, InvalidationCacheAccessDelegate delegate) {
|
||||||
|
this.region = region;
|
||||||
|
this.delegate = delegate;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void evict(Object key) throws CacheException {
|
||||||
|
delegate.evict( key );
|
||||||
|
}
|
||||||
|
|
||||||
|
public void evictAll() throws CacheException {
|
||||||
|
delegate.evictAll();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Object get(SessionImplementor session, Object key, long txTimestamp) throws CacheException {
|
||||||
|
return delegate.get( session, key, txTimestamp );
|
||||||
|
}
|
||||||
|
|
||||||
|
public EntityRegion getRegion() {
|
||||||
|
return this.region;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean putFromLoad(SessionImplementor session, Object key, Object value, long txTimestamp, Object version) throws CacheException {
|
||||||
|
return delegate.putFromLoad( session, key, value, txTimestamp, version );
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean putFromLoad(SessionImplementor session, Object key, Object value, long txTimestamp, Object version, boolean minimalPutOverride)
|
||||||
|
throws CacheException {
|
||||||
|
return delegate.putFromLoad( session, key, value, txTimestamp, version, minimalPutOverride );
|
||||||
|
}
|
||||||
|
|
||||||
|
public void remove(SessionImplementor session, Object key) throws CacheException {
|
||||||
|
delegate.remove ( session, key );
|
||||||
|
}
|
||||||
|
|
||||||
|
public void removeAll() throws CacheException {
|
||||||
|
delegate.removeAll();
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean insert(SessionImplementor session, Object key, Object value, Object version) throws CacheException {
|
||||||
|
return delegate.insert( session, key, value, version );
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -31,6 +76,25 @@ class ReadOnlyAccess extends TransactionalAccess {
|
||||||
throw new UnsupportedOperationException( "Illegal attempt to edit read only item" );
|
throw new UnsupportedOperationException( "Illegal attempt to edit read only item" );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public SoftLock lockItem(SessionImplementor session, Object key, Object version) throws CacheException {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public SoftLock lockRegion() throws CacheException {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void unlockItem(SessionImplementor session, Object key, SoftLock lock) throws CacheException {
|
||||||
|
delegate.unlockItem( session, key );
|
||||||
|
}
|
||||||
|
|
||||||
|
public void unlockRegion(SoftLock lock) throws CacheException {
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean afterInsert(SessionImplementor session, Object key, Object value, Object version) throws CacheException {
|
||||||
|
return delegate.afterInsert( session, key, value, version );
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean afterUpdate(
|
public boolean afterUpdate(
|
||||||
SessionImplementor session, Object key, Object value, Object currentVersion,
|
SessionImplementor session, Object key, Object value, Object currentVersion,
|
||||||
|
@ -38,4 +102,13 @@ class ReadOnlyAccess extends TransactionalAccess {
|
||||||
throw new UnsupportedOperationException( "Illegal attempt to edit read only item" );
|
throw new UnsupportedOperationException( "Illegal attempt to edit read only item" );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object generateCacheKey(Object id, EntityPersister persister, SessionFactoryImplementor factory, String tenantIdentifier) {
|
||||||
|
return region.getCacheKeysFactory().createEntityKey(id, persister, factory, tenantIdentifier);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object getCacheKeyId(Object cacheKey) {
|
||||||
|
return region.getCacheKeysFactory().getEntityId(cacheKey);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
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 javax.transaction.TransactionManager;
|
||||||
|
|
||||||
import org.hibernate.cache.CacheException;
|
import org.hibernate.cache.CacheException;
|
||||||
|
import org.hibernate.cache.infinispan.access.PutFromLoadValidator;
|
||||||
import org.hibernate.cache.infinispan.util.Caches;
|
import org.hibernate.cache.infinispan.util.Caches;
|
||||||
import org.hibernate.cache.spi.Region;
|
import org.hibernate.cache.spi.Region;
|
||||||
import org.hibernate.cache.spi.RegionFactory;
|
import org.hibernate.cache.spi.RegionFactory;
|
||||||
|
|
||||||
|
import org.hibernate.cache.spi.access.AccessType;
|
||||||
import org.infinispan.AdvancedCache;
|
import org.infinispan.AdvancedCache;
|
||||||
import org.infinispan.context.Flag;
|
import org.infinispan.context.Flag;
|
||||||
import org.infinispan.util.logging.Log;
|
import org.infinispan.util.logging.Log;
|
||||||
|
@ -35,7 +37,6 @@ import org.infinispan.util.logging.LogFactory;
|
||||||
public abstract class BaseRegion implements Region {
|
public abstract class BaseRegion implements Region {
|
||||||
|
|
||||||
private static final Log log = LogFactory.getLog( BaseRegion.class );
|
private static final Log log = LogFactory.getLog( BaseRegion.class );
|
||||||
private Transaction currentTransaction;
|
|
||||||
|
|
||||||
private enum InvalidateState {
|
private enum InvalidateState {
|
||||||
INVALID, CLEARING, VALID
|
INVALID, CLEARING, VALID
|
||||||
|
@ -48,12 +49,13 @@ public abstract class BaseRegion implements Region {
|
||||||
private final Object invalidationMutex = new Object();
|
private final Object invalidationMutex = new Object();
|
||||||
private final AtomicReference<InvalidateState> invalidateState =
|
private final AtomicReference<InvalidateState> invalidateState =
|
||||||
new AtomicReference<InvalidateState>( InvalidateState.VALID );
|
new AtomicReference<InvalidateState>( InvalidateState.VALID );
|
||||||
private volatile Transaction invalidateTransaction;
|
|
||||||
|
|
||||||
private final RegionFactory factory;
|
private final RegionFactory factory;
|
||||||
|
|
||||||
protected final AdvancedCache cache;
|
protected final AdvancedCache cache;
|
||||||
|
|
||||||
|
private PutFromLoadValidator validator;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Base region constructor.
|
* Base region constructor.
|
||||||
*
|
*
|
||||||
|
@ -110,7 +112,7 @@ public abstract class BaseRegion implements Region {
|
||||||
@Override
|
@Override
|
||||||
public int getTimeout() {
|
public int getTimeout() {
|
||||||
// 60 seconds
|
// 60 seconds
|
||||||
return 600;
|
return 60000;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -149,21 +151,7 @@ public abstract class BaseRegion implements Region {
|
||||||
synchronized (invalidationMutex) {
|
synchronized (invalidationMutex) {
|
||||||
if ( invalidateState.compareAndSet( InvalidateState.INVALID, InvalidateState.CLEARING ) ) {
|
if ( invalidateState.compareAndSet( InvalidateState.INVALID, InvalidateState.CLEARING ) ) {
|
||||||
try {
|
try {
|
||||||
// If we're running inside a transaction, we need to remove elements one-by-one
|
runInvalidation( getCurrentTransaction() != null );
|
||||||
// to clean the context as well (cache.clear() does not do that).
|
|
||||||
// When we don't have transaction, we can do a clear operation (since we don't
|
|
||||||
// case about context) and can't do the one-by-one remove: remove() on tx cache
|
|
||||||
// requires transactional context.
|
|
||||||
Transaction tx = getCurrentTransaction();
|
|
||||||
if ( tx != null ) {
|
|
||||||
log.tracef( "Transaction, clearing one element at the time" );
|
|
||||||
Caches.removeAll( localAndSkipLoadCache );
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
log.tracef( "Non-transactional, clear in one go" );
|
|
||||||
localAndSkipLoadCache.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
log.tracef( "Transition state from CLEARING to VALID" );
|
log.tracef( "Transition state from CLEARING to VALID" );
|
||||||
invalidateState.compareAndSet(
|
invalidateState.compareAndSet(
|
||||||
InvalidateState.CLEARING, InvalidateState.VALID
|
InvalidateState.CLEARING, InvalidateState.VALID
|
||||||
|
@ -228,7 +216,7 @@ public abstract class BaseRegion implements Region {
|
||||||
if (log.isTraceEnabled()) {
|
if (log.isTraceEnabled()) {
|
||||||
log.trace( "Invalidate region: " + name );
|
log.trace( "Invalidate region: " + name );
|
||||||
}
|
}
|
||||||
invalidateState.set( InvalidateState.INVALID );
|
invalidateState.set(InvalidateState.INVALID);
|
||||||
}
|
}
|
||||||
|
|
||||||
public TransactionManager getTransactionManager() {
|
public TransactionManager getTransactionManager() {
|
||||||
|
@ -255,4 +243,35 @@ public abstract class BaseRegion implements Region {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected void checkAccessType(AccessType accessType) {
|
||||||
|
if (accessType == AccessType.TRANSACTIONAL && !cache.getCacheConfiguration().transaction().transactionMode().isTransactional()) {
|
||||||
|
log.warn("Requesting TRANSACTIONAL cache concurrency strategy but the cache is not configured as transactional.");
|
||||||
|
}
|
||||||
|
else if (accessType == AccessType.READ_WRITE && cache.getCacheConfiguration().transaction().transactionMode().isTransactional()) {
|
||||||
|
log.warn("Requesting READ_WRITE cache concurrency strategy but the cache was configured as transactional.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected synchronized PutFromLoadValidator getValidator() {
|
||||||
|
if (validator == null) {
|
||||||
|
validator = new PutFromLoadValidator(cache);
|
||||||
|
}
|
||||||
|
return validator;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void runInvalidation(boolean inTransaction) {
|
||||||
|
// If we're running inside a transaction, we need to remove elements one-by-one
|
||||||
|
// to clean the context as well (cache.clear() does not do that).
|
||||||
|
// When we don't have transaction, we can do a clear operation (since we don't
|
||||||
|
// case about context) and can't do the one-by-one remove: remove() on tx cache
|
||||||
|
// requires transactional context.
|
||||||
|
if ( inTransaction ) {
|
||||||
|
log.tracef( "Transaction, clearing one element at the time" );
|
||||||
|
Caches.removeAll( localAndSkipLoadCache );
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
log.tracef( "Non-transactional, clear in one go" );
|
||||||
|
localAndSkipLoadCache.clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,7 +7,9 @@
|
||||||
package org.hibernate.cache.infinispan.naturalid;
|
package org.hibernate.cache.infinispan.naturalid;
|
||||||
|
|
||||||
import org.hibernate.cache.CacheException;
|
import org.hibernate.cache.CacheException;
|
||||||
|
import org.hibernate.cache.infinispan.access.AccessDelegate;
|
||||||
import org.hibernate.cache.infinispan.access.PutFromLoadValidator;
|
import org.hibernate.cache.infinispan.access.PutFromLoadValidator;
|
||||||
|
import org.hibernate.cache.infinispan.access.InvalidationCacheAccessDelegate;
|
||||||
import org.hibernate.cache.infinispan.impl.BaseTransactionalDataRegion;
|
import org.hibernate.cache.infinispan.impl.BaseTransactionalDataRegion;
|
||||||
import org.hibernate.cache.spi.CacheDataDescription;
|
import org.hibernate.cache.spi.CacheDataDescription;
|
||||||
import org.hibernate.cache.spi.CacheKeysFactory;
|
import org.hibernate.cache.spi.CacheKeysFactory;
|
||||||
|
@ -46,18 +48,19 @@ public class NaturalIdRegionImpl extends BaseTransactionalDataRegion
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public NaturalIdRegionAccessStrategy buildAccessStrategy(AccessType accessType) throws CacheException {
|
public NaturalIdRegionAccessStrategy buildAccessStrategy(AccessType accessType) throws CacheException {
|
||||||
|
checkAccessType( accessType );
|
||||||
|
if (!getCacheDataDescription().isMutable()) {
|
||||||
|
accessType = AccessType.READ_ONLY;
|
||||||
|
}
|
||||||
|
AccessDelegate delegate = InvalidationCacheAccessDelegate.create( this, getValidator());
|
||||||
switch ( accessType ) {
|
switch ( accessType ) {
|
||||||
case READ_ONLY:
|
case READ_ONLY:
|
||||||
return new ReadOnlyAccess( this );
|
return new ReadOnlyAccess( this, delegate );
|
||||||
|
case READ_WRITE:
|
||||||
case TRANSACTIONAL:
|
case TRANSACTIONAL:
|
||||||
return new TransactionalAccess( this );
|
return new ReadWriteAccess( this, delegate );
|
||||||
default:
|
default:
|
||||||
throw new CacheException( "Unsupported access type [" + accessType.getExternalName() + "]" );
|
throw new CacheException( "Unsupported access type [" + accessType.getExternalName() + "]" );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public PutFromLoadValidator getPutFromLoadValidator() {
|
|
||||||
return new PutFromLoadValidator( cache );
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,16 +7,29 @@
|
||||||
package org.hibernate.cache.infinispan.naturalid;
|
package org.hibernate.cache.infinispan.naturalid;
|
||||||
|
|
||||||
import org.hibernate.cache.CacheException;
|
import org.hibernate.cache.CacheException;
|
||||||
|
import org.hibernate.cache.infinispan.access.AccessDelegate;
|
||||||
|
import org.hibernate.cache.spi.NaturalIdRegion;
|
||||||
|
import org.hibernate.cache.spi.access.NaturalIdRegionAccessStrategy;
|
||||||
import org.hibernate.cache.spi.access.SoftLock;
|
import org.hibernate.cache.spi.access.SoftLock;
|
||||||
import org.hibernate.engine.spi.SessionImplementor;
|
import org.hibernate.engine.spi.SessionImplementor;
|
||||||
|
import org.hibernate.persister.entity.EntityPersister;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Strong Liu <stliu@hibernate.org>
|
* @author Strong Liu <stliu@hibernate.org>
|
||||||
*/
|
*/
|
||||||
class ReadOnlyAccess extends TransactionalAccess {
|
class ReadOnlyAccess implements NaturalIdRegionAccessStrategy {
|
||||||
|
|
||||||
ReadOnlyAccess(NaturalIdRegionImpl naturalIdRegion) {
|
protected final NaturalIdRegionImpl region;
|
||||||
super( naturalIdRegion );
|
protected final AccessDelegate delegate;
|
||||||
|
|
||||||
|
ReadOnlyAccess(NaturalIdRegionImpl region, AccessDelegate delegate) {
|
||||||
|
this.region = region;
|
||||||
|
this.delegate = delegate;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean insert(SessionImplementor session, Object key, Object value) throws CacheException {
|
||||||
|
return delegate.insert( session, key, value, null );
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -24,9 +37,83 @@ class ReadOnlyAccess extends TransactionalAccess {
|
||||||
throw new UnsupportedOperationException( "Illegal attempt to edit read only item" );
|
throw new UnsupportedOperationException( "Illegal attempt to edit read only item" );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public NaturalIdRegion getRegion() {
|
||||||
|
return region;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void evict(Object key) throws CacheException {
|
||||||
|
delegate.evict( key );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void evictAll() throws CacheException {
|
||||||
|
delegate.evictAll();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object get(SessionImplementor session, Object key, long txTimestamp) throws CacheException {
|
||||||
|
return delegate.get( session, key, txTimestamp );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean putFromLoad(SessionImplementor session, Object key, Object value, long txTimestamp, Object version) throws CacheException {
|
||||||
|
return delegate.putFromLoad( session, key, value, txTimestamp, version );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean putFromLoad(SessionImplementor session, Object key, Object value, long txTimestamp, Object version, boolean minimalPutOverride)
|
||||||
|
throws CacheException {
|
||||||
|
return delegate.putFromLoad( session, key, value, txTimestamp, version, minimalPutOverride );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void remove(SessionImplementor session, Object key) throws CacheException {
|
||||||
|
delegate.remove( session, key );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void removeAll() throws CacheException {
|
||||||
|
delegate.removeAll();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SoftLock lockItem(SessionImplementor session, Object key, Object version) throws CacheException {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SoftLock lockRegion() throws CacheException {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void unlockItem(SessionImplementor session, Object key, SoftLock lock) throws CacheException {
|
||||||
|
delegate.unlockItem( session, key );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void unlockRegion(SoftLock lock) throws CacheException {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean afterInsert(SessionImplementor session, Object key, Object value) throws CacheException {
|
||||||
|
return delegate.afterInsert( session, key, value, null );
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean afterUpdate(SessionImplementor session, Object key, Object value, SoftLock lock) throws CacheException {
|
public boolean afterUpdate(SessionImplementor session, Object key, Object value, SoftLock lock) throws CacheException {
|
||||||
throw new UnsupportedOperationException( "Illegal attempt to edit read only item" );
|
throw new UnsupportedOperationException( "Illegal attempt to edit read only item" );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object generateCacheKey(Object[] naturalIdValues, EntityPersister persister, SessionImplementor session) {
|
||||||
|
return region.getCacheKeysFactory().createNaturalIdKey(naturalIdValues, persister, session);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object[] getNaturalIdValues(Object cacheKey) {
|
||||||
|
return region.getCacheKeysFactory().getNaturalIdValues(cacheKey);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
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
|
* @return a cache that ignores return values
|
||||||
*/
|
*/
|
||||||
public static AdvancedCache ignoreReturnValuesCache(AdvancedCache cache) {
|
public static AdvancedCache ignoreReturnValuesCache(AdvancedCache cache) {
|
||||||
return cache.withFlags( Flag.SKIP_CACHE_LOAD, Flag.SKIP_REMOTE_LOOKUP );
|
return cache.withFlags( Flag.SKIP_CACHE_LOAD, Flag.SKIP_REMOTE_LOOKUP, Flag.IGNORE_RETURN_VALUES );
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -139,7 +139,7 @@ public class Caches {
|
||||||
public static AdvancedCache ignoreReturnValuesCache(
|
public static AdvancedCache ignoreReturnValuesCache(
|
||||||
AdvancedCache cache, Flag extraFlag) {
|
AdvancedCache cache, Flag extraFlag) {
|
||||||
return cache.withFlags(
|
return cache.withFlags(
|
||||||
Flag.SKIP_CACHE_LOAD, Flag.SKIP_REMOTE_LOOKUP, extraFlag
|
Flag.SKIP_CACHE_LOAD, Flag.SKIP_REMOTE_LOOKUP, Flag.IGNORE_RETURN_VALUES, extraFlag
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -270,6 +270,11 @@ public class Caches {
|
||||||
.clustering().cacheMode().isClustered();
|
.clustering().cacheMode().isClustered();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static boolean isTransactionalCache(AdvancedCache cache) {
|
||||||
|
return cache.getCacheConfiguration().transaction().transactionMode().isTransactional();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
public static void removeAll(AdvancedCache cache) {
|
public static void removeAll(AdvancedCache cache) {
|
||||||
CloseableIterator it = keys(cache).iterator();
|
CloseableIterator it = keys(cache).iterator();
|
||||||
try {
|
try {
|
||||||
|
|
|
@ -20,40 +20,23 @@
|
||||||
|
|
||||||
<!-- Default configuration is appropriate for entity/collection caching. -->
|
<!-- Default configuration is appropriate for entity/collection caching. -->
|
||||||
<invalidation-cache name="entity" mode="SYNC" remote-timeout="20000">
|
<invalidation-cache name="entity" mode="SYNC" remote-timeout="20000">
|
||||||
<locking isolation="READ_COMMITTED" concurrency-level="1000" acquire-timeout="15000" striping="false"/>
|
<locking concurrency-level="1000" acquire-timeout="15000"/>
|
||||||
<transaction mode="NON_XA" locking="OPTIMISTIC" auto-commit="false"/>
|
<transaction mode="NONE" />
|
||||||
<eviction max-entries="10000" strategy="LRU"/>
|
<eviction max-entries="10000" strategy="LRU"/>
|
||||||
<expiration max-idle="100000" interval="5000"/>
|
<expiration max-idle="100000" interval="5000"/>
|
||||||
</invalidation-cache>
|
</invalidation-cache>
|
||||||
|
|
||||||
<!-- Default configuration for immutable entities -->
|
<!-- Default configuration for immutable entities -->
|
||||||
<invalidation-cache name="immutable-entity" mode="SYNC" remote-timeout="20000">
|
<invalidation-cache name="immutable-entity" mode="SYNC" remote-timeout="20000">
|
||||||
<locking isolation="READ_COMMITTED" concurrency-level="1000" acquire-timeout="15000" striping="false"/>
|
<locking concurrency-level="1000" acquire-timeout="15000"/>
|
||||||
<transaction mode="NONE"/>
|
<transaction mode="NONE"/>
|
||||||
<eviction max-entries="10000" strategy="LRU"/>
|
<eviction max-entries="10000" strategy="LRU"/>
|
||||||
<expiration max-idle="100000" interval="5000"/>
|
<expiration max-idle="100000" interval="5000"/>
|
||||||
</invalidation-cache>
|
</invalidation-cache>
|
||||||
|
|
||||||
<!-- Default configuration is appropriate for entity/collection caching. -->
|
|
||||||
<invalidation-cache name="entity-repeatable" mode="SYNC" remote-timeout="20000">
|
|
||||||
<locking isolation="REPEATABLE_READ" concurrency-level="1000" acquire-timeout="15000" striping="false"/>
|
|
||||||
<transaction mode="NON_XA" locking="OPTIMISTIC" auto-commit="false"/>
|
|
||||||
<eviction max-entries="10000" strategy="LRU"/>
|
|
||||||
<expiration max-idle="100000" interval="5000"/>
|
|
||||||
</invalidation-cache>
|
|
||||||
|
|
||||||
<!-- An alternative configuration for entity/collection caching that uses replication instead of invalidation -->
|
|
||||||
<replicated-cache name="replicated-entity" mode="SYNC" remote-timeout="20000">
|
|
||||||
<locking isolation="READ_COMMITTED" concurrency-level="1000" acquire-timeout="15000" striping="false"/>
|
|
||||||
<transaction mode="NON_XA" locking="OPTIMISTIC" auto-commit="false"/>
|
|
||||||
<eviction max-entries="10000" strategy="LRU"/>
|
|
||||||
<expiration max-idle="100000" interval="5000"/>
|
|
||||||
<state-transfer enabled="false"/>
|
|
||||||
</replicated-cache>
|
|
||||||
|
|
||||||
<!-- A config appropriate for query caching. Does not replicate queries. -->
|
<!-- A config appropriate for query caching. Does not replicate queries. -->
|
||||||
<local-cache name="local-query">
|
<local-cache name="local-query">
|
||||||
<locking isolation="READ_COMMITTED" concurrency-level="1000" acquire-timeout="15000" striping="false"/>
|
<locking concurrency-level="1000" acquire-timeout="15000"/>
|
||||||
<transaction mode="NONE" />
|
<transaction mode="NONE" />
|
||||||
<eviction max-entries="10000" strategy="LRU"/>
|
<eviction max-entries="10000" strategy="LRU"/>
|
||||||
<expiration max-idle="100000" interval="5000"/>
|
<expiration max-idle="100000" interval="5000"/>
|
||||||
|
@ -61,7 +44,7 @@
|
||||||
|
|
||||||
<!-- A query cache that replicates queries. Replication is asynchronous. -->
|
<!-- A query cache that replicates queries. Replication is asynchronous. -->
|
||||||
<replicated-cache name="replicated-query" mode="ASYNC">
|
<replicated-cache name="replicated-query" mode="ASYNC">
|
||||||
<locking isolation="READ_COMMITTED" concurrency-level="1000" acquire-timeout="15000" striping="false"/>
|
<locking concurrency-level="1000" acquire-timeout="15000"/>
|
||||||
<transaction mode="NONE" />
|
<transaction mode="NONE" />
|
||||||
<eviction max-entries="10000" strategy="LRU"/>
|
<eviction max-entries="10000" strategy="LRU"/>
|
||||||
<expiration max-idle="100000" interval="5000"/>
|
<expiration max-idle="100000" interval="5000"/>
|
||||||
|
@ -71,7 +54,7 @@
|
||||||
is required if query caching is used, even if the query cache
|
is required if query caching is used, even if the query cache
|
||||||
itself is configured with CacheMode=LOCAL. -->
|
itself is configured with CacheMode=LOCAL. -->
|
||||||
<replicated-cache name="timestamps" mode="ASYNC">
|
<replicated-cache name="timestamps" mode="ASYNC">
|
||||||
<locking isolation="READ_COMMITTED" concurrency-level="1000" acquire-timeout="15000" striping="false"/>
|
<locking concurrency-level="1000" acquire-timeout="15000"/>
|
||||||
<!-- Explicitly non transactional -->
|
<!-- Explicitly non transactional -->
|
||||||
<transaction mode="NONE"/>
|
<transaction mode="NONE"/>
|
||||||
<!-- Don't ever evict modification timestamps -->
|
<!-- Don't ever evict modification timestamps -->
|
||||||
|
|
|
@ -30,7 +30,7 @@ import static org.junit.Assert.assertTrue;
|
||||||
* @author Galder Zamarreño
|
* @author Galder Zamarreño
|
||||||
* @since 3.5
|
* @since 3.5
|
||||||
*/
|
*/
|
||||||
public abstract class AbstractEntityCollectionRegionTestCase extends AbstractRegionImplTestCase {
|
public abstract class AbstractEntityCollectionRegionTest extends AbstractRegionImplTest {
|
||||||
protected static CacheDataDescription MUTABLE_NON_VERSIONED = new CacheDataDescriptionImpl(true, false, ComparableComparator.INSTANCE, null);
|
protected static CacheDataDescription MUTABLE_NON_VERSIONED = new CacheDataDescriptionImpl(true, false, ComparableComparator.INSTANCE, null);
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -43,7 +43,8 @@ public abstract class AbstractEntityCollectionRegionTestCase extends AbstractReg
|
||||||
"test",
|
"test",
|
||||||
InfinispanRegionFactory.class,
|
InfinispanRegionFactory.class,
|
||||||
true,
|
true,
|
||||||
false
|
false,
|
||||||
|
jtaPlatform
|
||||||
);
|
);
|
||||||
ssrb.applySetting( InfinispanRegionFactory.ENTITY_CACHE_RESOURCE_PROP, "entity" );
|
ssrb.applySetting( InfinispanRegionFactory.ENTITY_CACHE_RESOURCE_PROP, "entity" );
|
||||||
final StandardServiceRegistry registry = ssrb.build();
|
final StandardServiceRegistry registry = ssrb.build();
|
||||||
|
@ -72,7 +73,8 @@ public abstract class AbstractEntityCollectionRegionTestCase extends AbstractReg
|
||||||
"test",
|
"test",
|
||||||
InfinispanRegionFactory.class,
|
InfinispanRegionFactory.class,
|
||||||
true,
|
true,
|
||||||
false
|
false,
|
||||||
|
jtaPlatform
|
||||||
);
|
);
|
||||||
final StandardServiceRegistry registry = ssrb.build();
|
final StandardServiceRegistry registry = ssrb.build();
|
||||||
try {
|
try {
|
||||||
|
@ -101,7 +103,8 @@ public abstract class AbstractEntityCollectionRegionTestCase extends AbstractReg
|
||||||
"test",
|
"test",
|
||||||
InfinispanRegionFactory.class,
|
InfinispanRegionFactory.class,
|
||||||
true,
|
true,
|
||||||
false
|
false,
|
||||||
|
jtaPlatform
|
||||||
);
|
);
|
||||||
final StandardServiceRegistry registry = ssrb.build();
|
final StandardServiceRegistry registry = ssrb.build();
|
||||||
try {
|
try {
|
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.Set;
|
||||||
import java.util.concurrent.Callable;
|
import java.util.concurrent.Callable;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
import java.util.function.BiConsumer;
|
|
||||||
|
|
||||||
import org.hibernate.Session;
|
import org.hibernate.Session;
|
||||||
import org.hibernate.SessionFactory;
|
import org.hibernate.SessionFactory;
|
||||||
|
@ -29,18 +28,12 @@ import org.hibernate.cache.spi.Region;
|
||||||
import org.hibernate.cache.spi.RegionFactory;
|
import org.hibernate.cache.spi.RegionFactory;
|
||||||
import org.hibernate.cfg.AvailableSettings;
|
import org.hibernate.cfg.AvailableSettings;
|
||||||
import org.hibernate.engine.spi.SessionImplementor;
|
import org.hibernate.engine.spi.SessionImplementor;
|
||||||
import org.hibernate.resource.transaction.backend.jta.internal.JtaTransactionCoordinatorBuilderImpl;
|
|
||||||
import org.hibernate.test.cache.infinispan.functional.SingleNodeTestCase;
|
|
||||||
import org.hibernate.test.cache.infinispan.util.BatchModeJtaPlatform;
|
|
||||||
import org.hibernate.test.cache.infinispan.util.CacheTestUtil;
|
import org.hibernate.test.cache.infinispan.util.CacheTestUtil;
|
||||||
|
import org.hibernate.test.cache.infinispan.util.TestInfinispanRegionFactory;
|
||||||
import org.infinispan.AdvancedCache;
|
import org.infinispan.AdvancedCache;
|
||||||
import org.infinispan.commons.util.CloseableIterable;
|
|
||||||
import org.infinispan.transaction.tm.BatchModeTransactionManager;
|
|
||||||
import org.jboss.logging.Logger;
|
import org.jboss.logging.Logger;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
import javax.transaction.TransactionManager;
|
|
||||||
|
|
||||||
import static org.hibernate.test.cache.infinispan.util.CacheTestUtil.assertEqualsEventually;
|
import static org.hibernate.test.cache.infinispan.util.CacheTestUtil.assertEqualsEventually;
|
||||||
import static org.junit.Assert.assertEquals;
|
import static org.junit.Assert.assertEquals;
|
||||||
import static org.junit.Assert.assertNull;
|
import static org.junit.Assert.assertNull;
|
||||||
|
@ -51,8 +44,8 @@ import static org.junit.Assert.assertNull;
|
||||||
* @author Galder Zamarreño
|
* @author Galder Zamarreño
|
||||||
* @since 3.5
|
* @since 3.5
|
||||||
*/
|
*/
|
||||||
public abstract class AbstractGeneralDataRegionTestCase extends AbstractRegionImplTestCase {
|
public abstract class AbstractGeneralDataRegionTest extends AbstractRegionImplTest {
|
||||||
private static final Logger log = Logger.getLogger( AbstractGeneralDataRegionTestCase.class );
|
private static final Logger log = Logger.getLogger( AbstractGeneralDataRegionTest.class );
|
||||||
|
|
||||||
protected static final String KEY = "Key";
|
protected static final String KEY = "Key";
|
||||||
|
|
||||||
|
@ -60,17 +53,6 @@ public abstract class AbstractGeneralDataRegionTestCase extends AbstractRegionIm
|
||||||
protected static final String VALUE2 = "value2";
|
protected static final String VALUE2 = "value2";
|
||||||
protected static final String VALUE3 = "value3";
|
protected static final String VALUE3 = "value3";
|
||||||
|
|
||||||
protected TransactionManager tm = BatchModeTransactionManager.getInstance();
|
|
||||||
|
|
||||||
protected StandardServiceRegistryBuilder createStandardServiceRegistryBuilder() {
|
|
||||||
return CacheTestUtil.buildBaselineStandardServiceRegistryBuilder(
|
|
||||||
"test",
|
|
||||||
InfinispanRegionFactory.class,
|
|
||||||
false,
|
|
||||||
true
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void putInRegion(Region region, Object key, Object value) {
|
protected void putInRegion(Region region, Object key, Object value) {
|
||||||
((GeneralDataRegion) region).put(null, key, value );
|
((GeneralDataRegion) region).put(null, key, value );
|
||||||
|
@ -92,9 +74,7 @@ public abstract class AbstractGeneralDataRegionTestCase extends AbstractRegionIm
|
||||||
|
|
||||||
protected void withSessionFactoriesAndRegions(int num, SFRConsumer consumer) throws Exception {
|
protected void withSessionFactoriesAndRegions(int num, SFRConsumer consumer) throws Exception {
|
||||||
StandardServiceRegistryBuilder ssrb = createStandardServiceRegistryBuilder()
|
StandardServiceRegistryBuilder ssrb = createStandardServiceRegistryBuilder()
|
||||||
.applySetting(AvailableSettings.CACHE_REGION_FACTORY, SingleNodeTestCase.TestInfinispanRegionFactory.class.getName())
|
.applySetting(AvailableSettings.CACHE_REGION_FACTORY, TestInfinispanRegionFactory.class.getName());
|
||||||
.applySetting(AvailableSettings.JTA_PLATFORM, BatchModeJtaPlatform.class.getName())
|
|
||||||
.applySetting(AvailableSettings.TRANSACTION_COORDINATOR_STRATEGY, JtaTransactionCoordinatorBuilderImpl.class.getName());
|
|
||||||
Properties properties = CacheTestUtil.toProperties( ssrb.getSettings() );
|
Properties properties = CacheTestUtil.toProperties( ssrb.getSettings() );
|
||||||
List<StandardServiceRegistry> registries = new ArrayList<>();
|
List<StandardServiceRegistry> registries = new ArrayList<>();
|
||||||
List<SessionFactory> sessionFactories = new ArrayList<>();
|
List<SessionFactory> sessionFactories = new ArrayList<>();
|
||||||
|
@ -168,7 +148,7 @@ public abstract class AbstractGeneralDataRegionTestCase extends AbstractRegionIm
|
||||||
region.evict(KEY);
|
region.evict(KEY);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected abstract String getStandardRegionName(String regionPrefix);
|
protected abstract String getStandardRegionName(String regionPrefix);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Test method for {@link QueryResultsRegion#evictAll()}.
|
* Test method for {@link QueryResultsRegion#evictAll()}.
|
|
@ -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
|
* @author Galder Zamarreño
|
||||||
* @since 3.5
|
* @since 3.5
|
||||||
*/
|
*/
|
||||||
public abstract class AbstractRegionImplTestCase extends AbstractNonFunctionalTestCase {
|
public abstract class AbstractRegionImplTest extends AbstractNonFunctionalTest {
|
||||||
|
|
||||||
protected abstract AdvancedCache getInfinispanCache(InfinispanRegionFactory regionFactory);
|
protected abstract AdvancedCache getInfinispanCache(InfinispanRegionFactory regionFactory);
|
||||||
|
|
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.PrintWriter;
|
||||||
import java.io.StringWriter;
|
import java.io.StringWriter;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Random;
|
import java.util.Random;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.concurrent.Callable;
|
import java.util.concurrent.Callable;
|
||||||
|
@ -20,18 +20,12 @@ import java.util.concurrent.ExecutorService;
|
||||||
import java.util.concurrent.Executors;
|
import java.util.concurrent.Executors;
|
||||||
import java.util.concurrent.Future;
|
import java.util.concurrent.Future;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
import javax.transaction.TransactionManager;
|
|
||||||
|
|
||||||
import org.hibernate.FlushMode;
|
import org.hibernate.FlushMode;
|
||||||
import org.hibernate.Session;
|
|
||||||
import org.hibernate.engine.jdbc.connections.spi.ConnectionProvider;
|
|
||||||
import org.hibernate.engine.transaction.jta.platform.spi.JtaPlatform;
|
|
||||||
import org.hibernate.stat.SecondLevelCacheStatistics;
|
import org.hibernate.stat.SecondLevelCacheStatistics;
|
||||||
|
|
||||||
import org.hibernate.test.cache.infinispan.functional.cluster.DualNodeConnectionProviderImpl;
|
import org.hibernate.test.cache.infinispan.functional.entities.Contact;
|
||||||
import org.hibernate.test.cache.infinispan.functional.cluster.DualNodeJtaPlatformImpl;
|
import org.hibernate.test.cache.infinispan.functional.entities.Customer;
|
||||||
import org.hibernate.test.cache.infinispan.functional.cluster.DualNodeJtaTransactionManagerImpl;
|
|
||||||
import org.hibernate.test.cache.infinispan.functional.cluster.DualNodeTestCase;
|
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
import org.infinispan.util.logging.Log;
|
import org.infinispan.util.logging.Log;
|
||||||
|
@ -45,7 +39,7 @@ import static org.junit.Assert.assertNull;
|
||||||
* @author nikita_tovstoles@mba.berkeley.edu
|
* @author nikita_tovstoles@mba.berkeley.edu
|
||||||
* @author Galder Zamarreño
|
* @author Galder Zamarreño
|
||||||
*/
|
*/
|
||||||
public class ConcurrentWriteTest extends SingleNodeTestCase {
|
public class ConcurrentWriteTest extends SingleNodeTest {
|
||||||
private static final Log log = LogFactory.getLog( ConcurrentWriteTest.class );
|
private static final Log log = LogFactory.getLog( ConcurrentWriteTest.class );
|
||||||
private static final boolean trace = log.isTraceEnabled();
|
private static final boolean trace = log.isTraceEnabled();
|
||||||
/**
|
/**
|
||||||
|
@ -67,35 +61,9 @@ public class ConcurrentWriteTest extends SingleNodeTestCase {
|
||||||
*/
|
*/
|
||||||
private Set<Integer> customerIDs = new HashSet<Integer>();
|
private Set<Integer> customerIDs = new HashSet<Integer>();
|
||||||
|
|
||||||
private TransactionManager tm;
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@SuppressWarnings("unchecked")
|
public List<Object[]> getParameters() {
|
||||||
protected void addSettings(Map settings) {
|
return Arrays.asList(TRANSACTIONAL, READ_WRITE);
|
||||||
super.addSettings( settings );
|
|
||||||
|
|
||||||
settings.put( DualNodeTestCase.NODE_ID_PROP, DualNodeTestCase.LOCAL );
|
|
||||||
settings.put( DualNodeTestCase.NODE_ID_FIELD, DualNodeTestCase.LOCAL );
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected boolean getUseQueryCache() {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected TransactionManager getTransactionManager() {
|
|
||||||
return DualNodeJtaTransactionManagerImpl.getInstance( DualNodeTestCase.LOCAL );
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected Class<? extends ConnectionProvider> getConnectionProviderClass() {
|
|
||||||
return DualNodeConnectionProviderImpl.class;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected Class<? extends JtaPlatform> getJtaPlatform() {
|
|
||||||
return DualNodeJtaPlatformImpl.class;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -111,34 +79,18 @@ public class ConcurrentWriteTest extends SingleNodeTestCase {
|
||||||
}
|
}
|
||||||
finally {
|
finally {
|
||||||
cleanup();
|
cleanup();
|
||||||
// DualNodeJtaTransactionManagerImpl.cleanupTransactions();
|
|
||||||
// DualNodeJtaTransactionManagerImpl.cleanupTransactionManagers();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testPingDb() throws Exception {
|
public void testPingDb() throws Exception {
|
||||||
try {
|
withTxSession(s -> s.createQuery( "from " + Customer.class.getName() ).list());
|
||||||
beginTx();
|
|
||||||
sessionFactory()
|
|
||||||
.getCurrentSession()
|
|
||||||
.createQuery( "from " + Customer.class.getName() )
|
|
||||||
.list();
|
|
||||||
}
|
|
||||||
catch (Exception e) {
|
|
||||||
setRollbackOnlyTx( e );
|
|
||||||
// setRollbackOnly();
|
|
||||||
// fail("failed to query DB; exception=" + e);
|
|
||||||
}
|
|
||||||
finally {
|
|
||||||
commitOrRollbackTx();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testSingleUser() throws Exception {
|
public void testSingleUser() throws Exception {
|
||||||
// setup
|
// setup
|
||||||
sessionFactory().getStatistics().clear();
|
sessionFactory().getStatistics().clear();
|
||||||
Customer customer = createCustomer( 0 );
|
Customer customer = createCustomer( 0 );
|
||||||
final Integer customerId = customer.getId();
|
final Integer customerId = customer.getId();
|
||||||
getCustomerIDs().add( customerId );
|
getCustomerIDs().add( customerId );
|
||||||
|
@ -215,36 +167,20 @@ sessionFactory().getStatistics().clear();
|
||||||
getCustomerIDs().clear();
|
getCustomerIDs().clear();
|
||||||
String deleteContactHQL = "delete from Contact";
|
String deleteContactHQL = "delete from Contact";
|
||||||
String deleteCustomerHQL = "delete from Customer";
|
String deleteCustomerHQL = "delete from Customer";
|
||||||
beginTx();
|
withTxSession(s -> {
|
||||||
try {
|
s.createQuery(deleteContactHQL).setFlushMode(FlushMode.AUTO).executeUpdate();
|
||||||
Session session = sessionFactory().getCurrentSession();
|
s.createQuery(deleteCustomerHQL).setFlushMode(FlushMode.AUTO).executeUpdate();
|
||||||
session.createQuery( deleteContactHQL ).setFlushMode( FlushMode.AUTO ).executeUpdate();
|
});
|
||||||
session.createQuery( deleteCustomerHQL ).setFlushMode( FlushMode.AUTO ).executeUpdate();
|
|
||||||
}
|
|
||||||
catch (Exception e) {
|
|
||||||
setRollbackOnlyTx( e );
|
|
||||||
}
|
|
||||||
finally {
|
|
||||||
commitOrRollbackTx();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private Customer createCustomer(int nameSuffix) throws Exception {
|
private Customer createCustomer(int nameSuffix) throws Exception {
|
||||||
Customer customer = null;
|
return withTxSessionApply(s -> {
|
||||||
beginTx();
|
Customer customer = new Customer();
|
||||||
try {
|
|
||||||
customer = new Customer();
|
|
||||||
customer.setName( "customer_" + nameSuffix );
|
customer.setName( "customer_" + nameSuffix );
|
||||||
customer.setContacts( new HashSet<Contact>() );
|
customer.setContacts( new HashSet<Contact>() );
|
||||||
sessionFactory().getCurrentSession().persist( customer );
|
s.persist( customer );
|
||||||
}
|
return customer;
|
||||||
catch (Exception e) {
|
});
|
||||||
setRollbackOnlyTx( e );
|
|
||||||
}
|
|
||||||
finally {
|
|
||||||
commitOrRollbackTx();
|
|
||||||
}
|
|
||||||
return customer;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -255,101 +191,75 @@ sessionFactory().getStatistics().clear();
|
||||||
* @throws java.lang.Exception
|
* @throws java.lang.Exception
|
||||||
*/
|
*/
|
||||||
private void readEveryonesFirstContact() throws Exception {
|
private void readEveryonesFirstContact() throws Exception {
|
||||||
beginTx();
|
withTxSession(s -> {
|
||||||
try {
|
|
||||||
for ( Integer customerId : getCustomerIDs() ) {
|
for ( Integer customerId : getCustomerIDs() ) {
|
||||||
if ( TERMINATE_ALL_USERS ) {
|
if ( TERMINATE_ALL_USERS ) {
|
||||||
setRollbackOnlyTx();
|
markRollbackOnly(s);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
Customer customer = (Customer) sessionFactory()
|
Customer customer = s.load( Customer.class, customerId );
|
||||||
.getCurrentSession()
|
|
||||||
.load( Customer.class, customerId );
|
|
||||||
Set<Contact> contacts = customer.getContacts();
|
Set<Contact> contacts = customer.getContacts();
|
||||||
if ( !contacts.isEmpty() ) {
|
if ( !contacts.isEmpty() ) {
|
||||||
contacts.iterator().next();
|
contacts.iterator().next();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
});
|
||||||
catch (Exception e) {
|
|
||||||
setRollbackOnlyTx( e );
|
|
||||||
}
|
|
||||||
finally {
|
|
||||||
commitOrRollbackTx();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* -load existing Customer -get customer's contacts; return 1st one
|
* -load existing Customer -get customer's contacts; return 1st one
|
||||||
*
|
*
|
||||||
* @param customerId
|
* @param customerId
|
||||||
* @return first Contact or null if customer has none
|
* @return first Contact or null if customer has none
|
||||||
*/
|
*/
|
||||||
private Contact getFirstContact(Integer customerId) throws Exception {
|
private Contact getFirstContact(Integer customerId) throws Exception {
|
||||||
assert customerId != null;
|
assert customerId != null;
|
||||||
Contact firstContact = null;
|
return withTxSessionApply(s -> {
|
||||||
beginTx();
|
Customer customer = s.load(Customer.class, customerId);
|
||||||
try {
|
Set<Contact> contacts = customer.getContacts();
|
||||||
final Customer customer = (Customer) sessionFactory()
|
Contact firstContact = contacts.isEmpty() ? null : contacts.iterator().next();
|
||||||
.getCurrentSession()
|
if (TERMINATE_ALL_USERS) {
|
||||||
.load(Customer.class, customerId);
|
markRollbackOnly(s);
|
||||||
Set<Contact> contacts = customer.getContacts();
|
}
|
||||||
firstContact = contacts.isEmpty() ? null : contacts.iterator().next();
|
return firstContact;
|
||||||
if (TERMINATE_ALL_USERS)
|
});
|
||||||
setRollbackOnlyTx();
|
}
|
||||||
} catch (Exception e) {
|
|
||||||
setRollbackOnlyTx(e);
|
|
||||||
} finally {
|
|
||||||
commitOrRollbackTx();
|
|
||||||
}
|
|
||||||
return firstContact;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* -load existing Customer -create a new Contact and add to customer's contacts
|
* -load existing Customer -create a new Contact and add to customer's contacts
|
||||||
*
|
*
|
||||||
* @param customerId
|
* @param customerId
|
||||||
* @return added Contact
|
* @return added Contact
|
||||||
*/
|
*/
|
||||||
private Contact addContact(Integer customerId) throws Exception {
|
private Contact addContact(Integer customerId) throws Exception {
|
||||||
assert customerId != null;
|
assert customerId != null;
|
||||||
Contact contact = null;
|
return withTxSessionApply(s -> {
|
||||||
beginTx();
|
final Customer customer = s.load(Customer.class, customerId);
|
||||||
try {
|
Contact contact = new Contact();
|
||||||
final Customer customer = (Customer) sessionFactory()
|
contact.setName("contact name");
|
||||||
.getCurrentSession()
|
contact.setTlf("wtf is tlf?");
|
||||||
.load(Customer.class, customerId);
|
contact.setCustomer(customer);
|
||||||
contact = new Contact();
|
customer.getContacts().add(contact);
|
||||||
contact.setName("contact name");
|
// assuming contact is persisted via cascade from customer
|
||||||
contact.setTlf("wtf is tlf?");
|
if (TERMINATE_ALL_USERS) {
|
||||||
contact.setCustomer(customer);
|
markRollbackOnly(s);
|
||||||
customer.getContacts().add(contact);
|
}
|
||||||
// assuming contact is persisted via cascade from customer
|
return contact;
|
||||||
if (TERMINATE_ALL_USERS)
|
});
|
||||||
setRollbackOnlyTx();
|
}
|
||||||
} catch (Exception e) {
|
|
||||||
setRollbackOnlyTx(e);
|
|
||||||
} finally {
|
|
||||||
commitOrRollbackTx();
|
|
||||||
}
|
|
||||||
return contact;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* remove existing 'contact' from customer's list of contacts
|
* remove existing 'contact' from customer's list of contacts
|
||||||
*
|
*
|
||||||
* @param customerId
|
* @param customerId
|
||||||
* @throws IllegalStateException
|
* @throws IllegalStateException
|
||||||
* if customer does not own a contact
|
* if customer does not own a contact
|
||||||
*/
|
*/
|
||||||
private void removeContact(Integer customerId) throws Exception {
|
private void removeContact(Integer customerId) throws Exception {
|
||||||
assert customerId != null;
|
assert customerId != null;
|
||||||
|
|
||||||
beginTx();
|
withTxSession(s -> {
|
||||||
try {
|
Customer customer = s.load( Customer.class, customerId );
|
||||||
Customer customer = (Customer) sessionFactory()
|
|
||||||
.getCurrentSession()
|
|
||||||
.load( Customer.class, customerId );
|
|
||||||
Set<Contact> contacts = customer.getContacts();
|
Set<Contact> contacts = customer.getContacts();
|
||||||
if ( contacts.size() != 1 ) {
|
if ( contacts.size() != 1 ) {
|
||||||
throw new IllegalStateException(
|
throw new IllegalStateException(
|
||||||
|
@ -369,15 +279,9 @@ sessionFactory().getStatistics().clear();
|
||||||
// assuming contact is persisted via cascade from customer
|
// assuming contact is persisted via cascade from customer
|
||||||
|
|
||||||
if ( TERMINATE_ALL_USERS ) {
|
if ( TERMINATE_ALL_USERS ) {
|
||||||
setRollbackOnlyTx();
|
markRollbackOnly(s);
|
||||||
}
|
}
|
||||||
}
|
});
|
||||||
catch (Exception e) {
|
|
||||||
setRollbackOnlyTx( e );
|
|
||||||
}
|
|
||||||
finally {
|
|
||||||
commitOrRollbackTx();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -423,7 +327,6 @@ sessionFactory().getStatistics().clear();
|
||||||
Thread.currentThread().setName( "UserRunnerThread-" + getCustomerId() );
|
Thread.currentThread().setName( "UserRunnerThread-" + getCustomerId() );
|
||||||
log.info( "Wait for all executions paths to be ready to perform calls" );
|
log.info( "Wait for all executions paths to be ready to perform calls" );
|
||||||
try {
|
try {
|
||||||
// barrier.await();
|
|
||||||
for ( int i = 0; i < ITERATION_COUNT && !TERMINATE_ALL_USERS; i++ ) {
|
for ( int i = 0; i < ITERATION_COUNT && !TERMINATE_ALL_USERS; i++ ) {
|
||||||
contactExists();
|
contactExists();
|
||||||
if ( trace ) {
|
if ( trace ) {
|
||||||
|
@ -464,17 +367,6 @@ sessionFactory().getStatistics().clear();
|
||||||
TERMINATE_ALL_USERS = true;
|
TERMINATE_ALL_USERS = true;
|
||||||
log.error( "Error", t );
|
log.error( "Error", t );
|
||||||
throw new Exception( t );
|
throw new Exception( t );
|
||||||
// rollback current transaction if any
|
|
||||||
// really should not happen since above methods all follow begin-commit-rollback pattern
|
|
||||||
// try {
|
|
||||||
// if
|
|
||||||
// (DualNodeJtaTransactionManagerImpl.getInstance(DualNodeTestUtil.LOCAL).getTransaction()
|
|
||||||
// != null) {
|
|
||||||
// DualNodeJtaTransactionManagerImpl.getInstance(DualNodeTestUtil.LOCAL).rollback();
|
|
||||||
// }
|
|
||||||
// } catch (SystemException ex) {
|
|
||||||
// throw new RuntimeException("failed to rollback tx", ex);
|
|
||||||
// }
|
|
||||||
}
|
}
|
||||||
finally {
|
finally {
|
||||||
log.info( "Wait for all execution paths to finish" );
|
log.info( "Wait for all execution paths to finish" );
|
||||||
|
|
|
@ -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;
|
package org.hibernate.test.cache.infinispan.functional;
|
||||||
|
|
||||||
import java.util.concurrent.Callable;
|
|
||||||
|
|
||||||
import org.hibernate.Session;
|
|
||||||
import org.hibernate.stat.Statistics;
|
import org.hibernate.stat.Statistics;
|
||||||
|
import org.hibernate.test.cache.infinispan.functional.entities.Name;
|
||||||
|
import org.hibernate.test.cache.infinispan.functional.entities.Person;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
import static org.infinispan.test.TestingUtil.withTx;
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
import static org.junit.Assert.assertEquals;
|
import static org.junit.Assert.assertEquals;
|
||||||
import static org.junit.Assert.assertNotNull;
|
import static org.junit.Assert.assertNotNull;
|
||||||
import static org.junit.Assert.assertNull;
|
import static org.junit.Assert.assertNull;
|
||||||
|
@ -17,61 +18,50 @@ import static org.junit.Assert.assertTrue;
|
||||||
*
|
*
|
||||||
* @author Radim Vansa <rvansa@redhat.com>
|
* @author Radim Vansa <rvansa@redhat.com>
|
||||||
*/
|
*/
|
||||||
public class EqualityTest extends SingleNodeTestCase {
|
public class EqualityTest extends SingleNodeTest {
|
||||||
@Override
|
@Override
|
||||||
protected Class[] getAnnotatedClasses() {
|
public List<Object[]> getParameters() {
|
||||||
return new Class[] { Person.class };
|
return Arrays.asList(TRANSACTIONAL, READ_WRITE, READ_ONLY);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Override
|
||||||
public void testEqualityFromType() throws Exception {
|
protected Class[] getAnnotatedClasses() {
|
||||||
Person john = new Person("John", "Black", 26);
|
return new Class[] { Person.class };
|
||||||
Person peter = new Person("Peter", "White", 32);
|
}
|
||||||
|
|
||||||
withTx(tm, new Callable<Void>() {
|
@Test
|
||||||
@Override
|
public void testEqualityFromType() throws Exception {
|
||||||
public Void call() throws Exception {
|
Person john = new Person("John", "Black", 26);
|
||||||
Session session = openSession();
|
Person peter = new Person("Peter", "White", 32);
|
||||||
session.getTransaction().begin();
|
|
||||||
session.persist(john);
|
|
||||||
session.persist(peter);
|
|
||||||
session.getTransaction().commit();
|
|
||||||
session.close();
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
Statistics statistics = sessionFactory().getStatistics();
|
withTxSession(s -> {
|
||||||
statistics.clear();
|
s.persist(john);
|
||||||
|
s.persist(peter);
|
||||||
|
});
|
||||||
|
|
||||||
for (int i = 0; i < 5; ++i) {
|
Statistics statistics = sessionFactory().getStatistics();
|
||||||
withTx(tm, new Callable<Void>() {
|
statistics.clear();
|
||||||
@Override
|
|
||||||
public Void call() throws Exception {
|
|
||||||
Session session = openSession();
|
|
||||||
session.getTransaction().begin();
|
|
||||||
Person p1 = session.get(Person.class, john.name);
|
|
||||||
assertPersonEquals(john, p1);
|
|
||||||
Person p2 = session.get(Person.class, peter.name);
|
|
||||||
assertPersonEquals(peter, p2);
|
|
||||||
Person p3 = session.get(Person.class, new Name("Foo", "Bar"));
|
|
||||||
assertNull(p3);
|
|
||||||
session.getTransaction().commit();
|
|
||||||
session.close();
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
assertTrue(statistics.getSecondLevelCacheHitCount() > 0);
|
for (int i = 0; i < 5; ++i) {
|
||||||
assertTrue(statistics.getSecondLevelCacheMissCount() > 0);
|
withTxSession(s -> {
|
||||||
}
|
Person p1 = s.get(Person.class, john.getName());
|
||||||
|
assertPersonEquals(john, p1);
|
||||||
|
Person p2 = s.get(Person.class, peter.getName());
|
||||||
|
assertPersonEquals(peter, p2);
|
||||||
|
Person p3 = s.get(Person.class, new Name("Foo", "Bar"));
|
||||||
|
assertNull(p3);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
private static void assertPersonEquals(Person expected, Person person) {
|
assertTrue(statistics.getSecondLevelCacheHitCount() > 0);
|
||||||
assertNotNull(person);
|
assertTrue(statistics.getSecondLevelCacheMissCount() > 0);
|
||||||
assertNotNull(person.getName());
|
}
|
||||||
assertEquals(expected.getName().getFirstName(), person.getName().getFirstName());
|
|
||||||
assertEquals(expected.getName().getLastName(), person.getName().getLastName());
|
private static void assertPersonEquals(Person expected, Person person) {
|
||||||
assertEquals(expected.getAge(), person.getAge());
|
assertNotNull(person);
|
||||||
}
|
assertNotNull(person.getName());
|
||||||
|
assertEquals(expected.getName().getFirstName(), person.getName().getFirstName());
|
||||||
|
assertEquals(expected.getName().getLastName(), person.getName().getLastName());
|
||||||
|
assertEquals(expected.getAge(), person.getAge());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,6 +6,8 @@
|
||||||
*/
|
*/
|
||||||
package org.hibernate.test.cache.infinispan.functional;
|
package org.hibernate.test.cache.infinispan.functional;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Properties;
|
import java.util.Properties;
|
||||||
import javax.naming.Context;
|
import javax.naming.Context;
|
||||||
|
@ -15,16 +17,17 @@ import javax.naming.NameNotFoundException;
|
||||||
import javax.naming.Reference;
|
import javax.naming.Reference;
|
||||||
import javax.naming.StringRefAddr;
|
import javax.naming.StringRefAddr;
|
||||||
|
|
||||||
import org.hibernate.Session;
|
|
||||||
import org.hibernate.boot.registry.StandardServiceRegistry;
|
import org.hibernate.boot.registry.StandardServiceRegistry;
|
||||||
import org.hibernate.cache.infinispan.InfinispanRegionFactory;
|
import org.hibernate.cache.infinispan.InfinispanRegionFactory;
|
||||||
import org.hibernate.cache.infinispan.JndiInfinispanRegionFactory;
|
import org.hibernate.cache.infinispan.JndiInfinispanRegionFactory;
|
||||||
import org.hibernate.cache.spi.RegionFactory;
|
import org.hibernate.cache.spi.access.AccessType;
|
||||||
import org.hibernate.cfg.Environment;
|
import org.hibernate.cfg.Environment;
|
||||||
import org.hibernate.engine.config.spi.ConfigurationService;
|
import org.hibernate.engine.config.spi.ConfigurationService;
|
||||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||||
|
import org.hibernate.resource.transaction.backend.jdbc.internal.JdbcResourceLocalTransactionCoordinatorBuilderImpl;
|
||||||
import org.hibernate.stat.Statistics;
|
import org.hibernate.stat.Statistics;
|
||||||
|
|
||||||
|
import org.hibernate.test.cache.infinispan.functional.entities.Item;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
import org.infinispan.Cache;
|
import org.infinispan.Cache;
|
||||||
|
@ -38,17 +41,15 @@ import org.jboss.util.naming.NonSerializableFactory;
|
||||||
|
|
||||||
import org.jnp.server.Main;
|
import org.jnp.server.Main;
|
||||||
import org.jnp.server.SingletonNamingServer;
|
import org.jnp.server.SingletonNamingServer;
|
||||||
|
import org.junit.runners.Parameterized;
|
||||||
|
|
||||||
import static org.junit.Assert.assertEquals;
|
import static org.junit.Assert.assertEquals;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* // TODO: Document this
|
|
||||||
*
|
|
||||||
* @author Galder Zamarreño
|
* @author Galder Zamarreño
|
||||||
* @since // TODO
|
|
||||||
*/
|
*/
|
||||||
public class JndiRegionFactoryTestCase extends SingleNodeTestCase {
|
public class JndiRegionFactoryTest extends SingleNodeTest {
|
||||||
private static final Log log = LogFactory.getLog( JndiRegionFactoryTestCase.class );
|
private static final Log log = LogFactory.getLog( JndiRegionFactoryTest.class );
|
||||||
private static final String JNDI_NAME = "java:CacheManager";
|
private static final String JNDI_NAME = "java:CacheManager";
|
||||||
private Main namingMain;
|
private Main namingMain;
|
||||||
private SingletonNamingServer namingServer;
|
private SingletonNamingServer namingServer;
|
||||||
|
@ -56,6 +57,11 @@ public class JndiRegionFactoryTestCase extends SingleNodeTestCase {
|
||||||
private boolean bindToJndi = true;
|
private boolean bindToJndi = true;
|
||||||
private EmbeddedCacheManager manager;
|
private EmbeddedCacheManager manager;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<Object[]> getParameters() {
|
||||||
|
return Collections.singletonList(new Object[]{"read-write", null, JdbcResourceLocalTransactionCoordinatorBuilderImpl.class, null, AccessType.READ_WRITE, JndiInfinispanRegionFactory.class});
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void cleanupTest() throws Exception {
|
protected void cleanupTest() throws Exception {
|
||||||
Context ctx = new InitialContext( props );
|
Context ctx = new InitialContext( props );
|
||||||
|
@ -65,11 +71,6 @@ public class JndiRegionFactoryTestCase extends SingleNodeTestCase {
|
||||||
manager.stop(); // Need to stop cos JNDI region factory does not stop it.
|
manager.stop(); // Need to stop cos JNDI region factory does not stop it.
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
protected Class<? extends RegionFactory> getCacheRegionFactory() {
|
|
||||||
return JndiInfinispanRegionFactory.class;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void afterStandardServiceRegistryBuilt(StandardServiceRegistry ssr) {
|
protected void afterStandardServiceRegistryBuilt(StandardServiceRegistry ssr) {
|
||||||
if ( bindToJndi ) {
|
if ( bindToJndi ) {
|
||||||
|
@ -118,46 +119,23 @@ public class JndiRegionFactoryTestCase extends SingleNodeTestCase {
|
||||||
|
|
||||||
addEntityCheckCache( sessionFactory() );
|
addEntityCheckCache( sessionFactory() );
|
||||||
JndiInfinispanRegionFactory regionFactory = (JndiInfinispanRegionFactory) sessionFactory().getSettings().getRegionFactory();
|
JndiInfinispanRegionFactory regionFactory = (JndiInfinispanRegionFactory) sessionFactory().getSettings().getRegionFactory();
|
||||||
Cache cache = regionFactory.getCacheManager().getCache( "org.hibernate.test.cache.infinispan.functional.Item" );
|
Cache cache = regionFactory.getCacheManager().getCache( Item.class.getName() );
|
||||||
assertEquals( ComponentStatus.RUNNING, cache.getStatus() );
|
assertEquals( ComponentStatus.RUNNING, cache.getStatus() );
|
||||||
}
|
}
|
||||||
|
|
||||||
private void addEntityCheckCache(SessionFactoryImplementor sessionFactory) throws Exception {
|
private void addEntityCheckCache(SessionFactoryImplementor sessionFactory) throws Exception {
|
||||||
Item item = new Item( "chris", "Chris's Item" );
|
Item item = new Item( "chris", "Chris's Item" );
|
||||||
beginTx();
|
withTxSession(s -> s.persist( item ));
|
||||||
try {
|
|
||||||
Session s = sessionFactory.openSession();
|
|
||||||
s.getTransaction().begin();
|
|
||||||
s.persist( item );
|
|
||||||
s.getTransaction().commit();
|
|
||||||
s.close();
|
|
||||||
}
|
|
||||||
catch (Exception e) {
|
|
||||||
setRollbackOnlyTx( e );
|
|
||||||
}
|
|
||||||
finally {
|
|
||||||
commitOrRollbackTx();
|
|
||||||
}
|
|
||||||
|
|
||||||
beginTx();
|
withTxSession(s -> {
|
||||||
try {
|
Item found = s.load(Item.class, item.getId());
|
||||||
Session s = sessionFactory.openSession();
|
|
||||||
Item found = (Item) s.load( Item.class, item.getId() );
|
|
||||||
Statistics stats = sessionFactory.getStatistics();
|
Statistics stats = sessionFactory.getStatistics();
|
||||||
log.info( stats.toString() );
|
log.info(stats.toString());
|
||||||
assertEquals( item.getDescription(), found.getDescription() );
|
assertEquals(item.getDescription(), found.getDescription());
|
||||||
assertEquals( 0, stats.getSecondLevelCacheMissCount() );
|
assertEquals(0, stats.getSecondLevelCacheMissCount());
|
||||||
assertEquals( 1, stats.getSecondLevelCacheHitCount() );
|
assertEquals(1, stats.getSecondLevelCacheHitCount());
|
||||||
s.delete( found );
|
s.delete(found);
|
||||||
s.close();
|
});
|
||||||
}
|
|
||||||
catch (Exception e) {
|
|
||||||
setRollbackOnlyTx( e );
|
|
||||||
}
|
|
||||||
finally {
|
|
||||||
commitOrRollbackTx();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
|
@ -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.TimestampsRegion;
|
||||||
import org.hibernate.cache.spi.access.AccessType;
|
import org.hibernate.cache.spi.access.AccessType;
|
||||||
|
|
||||||
import org.hibernate.test.cache.infinispan.functional.SingleNodeTestCase;
|
import org.hibernate.internal.util.ReflectHelper;
|
||||||
|
|
||||||
import org.infinispan.manager.EmbeddedCacheManager;
|
import org.infinispan.manager.EmbeddedCacheManager;
|
||||||
import org.infinispan.util.logging.Log;
|
import org.infinispan.util.logging.Log;
|
||||||
import org.infinispan.util.logging.LogFactory;
|
import org.infinispan.util.logging.LogFactory;
|
||||||
|
@ -34,85 +33,89 @@ import org.infinispan.util.logging.LogFactory;
|
||||||
* @since 3.5
|
* @since 3.5
|
||||||
*/
|
*/
|
||||||
public class ClusterAwareRegionFactory implements RegionFactory {
|
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 =
|
private static final Log log = LogFactory.getLog(ClusterAwareRegionFactory.class);
|
||||||
new SingleNodeTestCase.TestInfinispanRegionFactory();
|
private static final Hashtable<String, EmbeddedCacheManager> cacheManagers = new Hashtable<String, EmbeddedCacheManager>();
|
||||||
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();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void start(SessionFactoryOptions settings, Properties properties) throws CacheException {
|
private InfinispanRegionFactory delegate;
|
||||||
cacheManagerName = properties.getProperty(DualNodeTestCase.NODE_ID_PROP);
|
private String cacheManagerName;
|
||||||
|
private boolean locallyAdded;
|
||||||
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() {
|
public ClusterAwareRegionFactory(Properties props) {
|
||||||
if (locallyAdded) cacheManagers.remove(cacheManagerName);
|
try {
|
||||||
delegate.stop();
|
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,
|
public static EmbeddedCacheManager getCacheManager(String name) {
|
||||||
CacheDataDescription metadata) throws CacheException {
|
return cacheManagers.get(name);
|
||||||
return delegate.buildCollectionRegion(regionName, properties, metadata);
|
}
|
||||||
}
|
|
||||||
|
|
||||||
public EntityRegion buildEntityRegion(String regionName, Properties properties,
|
public static void addCacheManager(String name, EmbeddedCacheManager manager) {
|
||||||
CacheDataDescription metadata) throws CacheException {
|
cacheManagers.put(name, manager);
|
||||||
return delegate.buildEntityRegion(regionName, properties, metadata);
|
}
|
||||||
}
|
|
||||||
|
public static void clearCacheManagers() {
|
||||||
@Override
|
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)
|
public NaturalIdRegion buildNaturalIdRegion(String regionName, Properties properties, CacheDataDescription metadata)
|
||||||
throws CacheException {
|
throws CacheException {
|
||||||
return delegate.buildNaturalIdRegion( regionName, properties, metadata );
|
return delegate.buildNaturalIdRegion( regionName, properties, metadata );
|
||||||
}
|
}
|
||||||
|
|
||||||
public QueryResultsRegion buildQueryResultsRegion(String regionName, Properties properties)
|
public QueryResultsRegion buildQueryResultsRegion(String regionName, Properties properties)
|
||||||
throws CacheException {
|
throws CacheException {
|
||||||
return delegate.buildQueryResultsRegion(regionName, properties);
|
return delegate.buildQueryResultsRegion(regionName, properties);
|
||||||
}
|
}
|
||||||
|
|
||||||
public TimestampsRegion buildTimestampsRegion(String regionName, Properties properties)
|
public TimestampsRegion buildTimestampsRegion(String regionName, Properties properties)
|
||||||
throws CacheException {
|
throws CacheException {
|
||||||
return delegate.buildTimestampsRegion(regionName, properties);
|
return delegate.buildTimestampsRegion(regionName, properties);
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isMinimalPutsEnabledByDefault() {
|
public boolean isMinimalPutsEnabledByDefault() {
|
||||||
return delegate.isMinimalPutsEnabledByDefault();
|
return delegate.isMinimalPutsEnabledByDefault();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public AccessType getDefaultAccessType() {
|
public AccessType getDefaultAccessType() {
|
||||||
|
@ -120,6 +123,6 @@ public class ClusterAwareRegionFactory implements RegionFactory {
|
||||||
}
|
}
|
||||||
|
|
||||||
public long nextTimestamp() {
|
public long nextTimestamp() {
|
||||||
return delegate.nextTimestamp();
|
return delegate.nextTimestamp();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,9 +30,9 @@ public class DualNodeJtaPlatformImpl implements JtaPlatform, Configurable {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void configure(Map configurationValues) {
|
public void configure(Map configurationValues) {
|
||||||
nodeId = (String) configurationValues.get( DualNodeTestCase.NODE_ID_PROP );
|
nodeId = (String) configurationValues.get( DualNodeTest.NODE_ID_PROP );
|
||||||
if ( nodeId == null ) {
|
if ( nodeId == null ) {
|
||||||
throw new HibernateException(DualNodeTestCase.NODE_ID_PROP + " not configured");
|
throw new HibernateException(DualNodeTest.NODE_ID_PROP + " not configured");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -6,37 +6,42 @@
|
||||||
*/
|
*/
|
||||||
package org.hibernate.test.cache.infinispan.functional.cluster;
|
package org.hibernate.test.cache.infinispan.functional.cluster;
|
||||||
|
|
||||||
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
|
import org.hibernate.Session;
|
||||||
|
import org.hibernate.SessionFactory;
|
||||||
import org.hibernate.boot.Metadata;
|
import org.hibernate.boot.Metadata;
|
||||||
import org.hibernate.boot.MetadataSources;
|
import org.hibernate.boot.MetadataSources;
|
||||||
import org.hibernate.boot.registry.StandardServiceRegistry;
|
import org.hibernate.boot.registry.StandardServiceRegistry;
|
||||||
import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
|
import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
|
||||||
import org.hibernate.cfg.AvailableSettings;
|
|
||||||
import org.hibernate.cfg.Environment;
|
import org.hibernate.cfg.Environment;
|
||||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||||
import org.hibernate.resource.transaction.TransactionCoordinatorBuilder;
|
import org.hibernate.resource.transaction.TransactionCoordinatorBuilder;
|
||||||
|
import org.hibernate.resource.transaction.backend.jdbc.internal.JdbcResourceLocalTransactionCoordinatorBuilderImpl;
|
||||||
import org.hibernate.resource.transaction.backend.jta.internal.JtaTransactionCoordinatorBuilderImpl;
|
import org.hibernate.resource.transaction.backend.jta.internal.JtaTransactionCoordinatorBuilderImpl;
|
||||||
|
|
||||||
|
import org.hibernate.test.cache.infinispan.functional.AbstractFunctionalTest;
|
||||||
import org.hibernate.test.cache.infinispan.util.InfinispanTestingSetup;
|
import org.hibernate.test.cache.infinispan.util.InfinispanTestingSetup;
|
||||||
import org.hibernate.testing.junit4.BaseNonConfigCoreFunctionalTestCase;
|
|
||||||
|
|
||||||
|
import org.hibernate.test.cache.infinispan.util.TxUtil;
|
||||||
import org.infinispan.util.logging.Log;
|
import org.infinispan.util.logging.Log;
|
||||||
import org.infinispan.util.logging.LogFactory;
|
import org.infinispan.util.logging.LogFactory;
|
||||||
import org.junit.Before;
|
|
||||||
import org.junit.ClassRule;
|
import org.junit.ClassRule;
|
||||||
|
import org.junit.runners.Parameterized;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Galder Zamarreño
|
* @author Galder Zamarreño
|
||||||
* @since 3.5
|
* @since 3.5
|
||||||
*/
|
*/
|
||||||
public abstract class DualNodeTestCase extends BaseNonConfigCoreFunctionalTestCase {
|
public abstract class DualNodeTest extends AbstractFunctionalTest {
|
||||||
|
|
||||||
private static final Log log = LogFactory.getLog( DualNodeTestCase.class );
|
private static final Log log = LogFactory.getLog( DualNodeTest.class );
|
||||||
|
|
||||||
@ClassRule
|
@ClassRule
|
||||||
public static final InfinispanTestingSetup infinispanTestIdentifier = new InfinispanTestingSetup();
|
public static final InfinispanTestingSetup infinispanTestIdentifier = new InfinispanTestingSetup();
|
||||||
|
|
||||||
|
public static final String REGION_FACTORY_DELEGATE = "hibernate.cache.region.factory_delegate";
|
||||||
public static final String NODE_ID_PROP = "hibernate.test.cluster.node.id";
|
public static final String NODE_ID_PROP = "hibernate.test.cluster.node.id";
|
||||||
public static final String NODE_ID_FIELD = "nodeId";
|
public static final String NODE_ID_FIELD = "nodeId";
|
||||||
public static final String LOCAL = "local";
|
public static final String LOCAL = "local";
|
||||||
|
@ -44,16 +49,20 @@ public abstract class DualNodeTestCase extends BaseNonConfigCoreFunctionalTestCa
|
||||||
|
|
||||||
private SecondNodeEnvironment secondNodeEnvironment;
|
private SecondNodeEnvironment secondNodeEnvironment;
|
||||||
|
|
||||||
@Override
|
protected void withTxSession(SessionFactory sessionFactory, TxUtil.ThrowingConsumer<Session, Exception> consumer) throws Exception {
|
||||||
public String[] getMappings() {
|
TxUtil.withTxSession(useJta, sessionFactory, consumer);
|
||||||
return new String[] {
|
}
|
||||||
"cache/infinispan/functional/Contact.hbm.xml", "cache/infinispan/functional/Customer.hbm.xml"
|
|
||||||
};
|
protected <T> T withTxSessionApply(SessionFactory sessionFactory, TxUtil.ThrowingFunction<Session, T, Exception> consumer) throws Exception {
|
||||||
|
return TxUtil.withTxSessionApply(useJta, sessionFactory, consumer);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getCacheConcurrencyStrategy() {
|
public String[] getMappings() {
|
||||||
return "transactional";
|
return new String[] {
|
||||||
|
"cache/infinispan/functional/entities/Contact.hbm.xml",
|
||||||
|
"cache/infinispan/functional/entities/Customer.hbm.xml"
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -65,6 +74,7 @@ public abstract class DualNodeTestCase extends BaseNonConfigCoreFunctionalTestCa
|
||||||
|
|
||||||
settings.put( NODE_ID_PROP, LOCAL );
|
settings.put( NODE_ID_PROP, LOCAL );
|
||||||
settings.put( NODE_ID_FIELD, LOCAL );
|
settings.put( NODE_ID_FIELD, LOCAL );
|
||||||
|
settings.put( REGION_FACTORY_DELEGATE, regionFactoryClass.getName() );
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -101,10 +111,6 @@ public abstract class DualNodeTestCase extends BaseNonConfigCoreFunctionalTestCa
|
||||||
return ClusterAwareRegionFactory.class;
|
return ClusterAwareRegionFactory.class;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected Class getConnectionProviderClass() {
|
|
||||||
return DualNodeConnectionProviderImpl.class;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected Class getJtaPlatformClass() {
|
protected Class getJtaPlatformClass() {
|
||||||
return DualNodeJtaPlatformImpl.class;
|
return DualNodeJtaPlatformImpl.class;
|
||||||
}
|
}
|
||||||
|
@ -122,20 +128,12 @@ public abstract class DualNodeTestCase extends BaseNonConfigCoreFunctionalTestCa
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected boolean getUseQueryCache() {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void configureSecondNode(StandardServiceRegistryBuilder ssrb) {
|
protected void configureSecondNode(StandardServiceRegistryBuilder ssrb) {
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
protected void applyStandardSettings(Map settings) {
|
protected void applyStandardSettings(Map settings) {
|
||||||
settings.put( Environment.CONNECTION_PROVIDER, getConnectionProviderClass().getName() );
|
settings.put( Environment.CACHE_REGION_FACTORY, ClusterAwareRegionFactory.class.getName() );
|
||||||
settings.put( AvailableSettings.JTA_PLATFORM, getJtaPlatformClass().getName() );
|
|
||||||
settings.put( Environment.TRANSACTION_COORDINATOR_STRATEGY, getTransactionCoordinatorBuilder().getName() );
|
|
||||||
settings.put( Environment.CACHE_REGION_FACTORY, getCacheRegionFactory().getName() );
|
|
||||||
settings.put( Environment.USE_QUERY_CACHE, String.valueOf( getUseQueryCache() ) );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public class SecondNodeEnvironment {
|
public class SecondNodeEnvironment {
|
|
@ -6,9 +6,10 @@
|
||||||
*/
|
*/
|
||||||
package org.hibernate.test.cache.infinispan.functional.cluster;
|
package org.hibernate.test.cache.infinispan.functional.cluster;
|
||||||
|
|
||||||
import javax.transaction.TransactionManager;
|
import java.util.Arrays;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
|
import java.util.List;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.concurrent.Phaser;
|
import java.util.concurrent.Phaser;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
@ -18,8 +19,8 @@ import java.util.concurrent.atomic.AtomicReference;
|
||||||
import org.hibernate.Session;
|
import org.hibernate.Session;
|
||||||
import org.hibernate.SessionFactory;
|
import org.hibernate.SessionFactory;
|
||||||
import org.hibernate.cache.infinispan.InfinispanRegionFactory;
|
import org.hibernate.cache.infinispan.InfinispanRegionFactory;
|
||||||
import org.hibernate.test.cache.infinispan.functional.Contact;
|
import org.hibernate.test.cache.infinispan.functional.entities.Contact;
|
||||||
import org.hibernate.test.cache.infinispan.functional.Customer;
|
import org.hibernate.test.cache.infinispan.functional.entities.Customer;
|
||||||
import org.hibernate.testing.TestForIssue;
|
import org.hibernate.testing.TestForIssue;
|
||||||
import org.infinispan.AdvancedCache;
|
import org.infinispan.AdvancedCache;
|
||||||
import org.infinispan.Cache;
|
import org.infinispan.Cache;
|
||||||
|
@ -36,7 +37,6 @@ import org.infinispan.util.logging.LogFactory;
|
||||||
import org.jboss.util.collection.ConcurrentSet;
|
import org.jboss.util.collection.ConcurrentSet;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
import static org.infinispan.test.TestingUtil.withTx;
|
|
||||||
import static org.junit.Assert.assertEquals;
|
import static org.junit.Assert.assertEquals;
|
||||||
import static org.junit.Assert.assertNull;
|
import static org.junit.Assert.assertNull;
|
||||||
import static org.junit.Assert.assertTrue;
|
import static org.junit.Assert.assertTrue;
|
||||||
|
@ -47,28 +47,30 @@ import static org.junit.Assert.assertTrue;
|
||||||
* @author Galder Zamarreño
|
* @author Galder Zamarreño
|
||||||
* @since 3.5
|
* @since 3.5
|
||||||
*/
|
*/
|
||||||
public class EntityCollectionInvalidationTestCase extends DualNodeTestCase {
|
public class EntityCollectionInvalidationTest extends DualNodeTest {
|
||||||
private static final Log log = LogFactory.getLog( EntityCollectionInvalidationTestCase.class );
|
private static final Log log = LogFactory.getLog( EntityCollectionInvalidationTest.class );
|
||||||
|
|
||||||
private static final long SLEEP_TIME = 50l;
|
private static final long SLEEP_TIME = 50l;
|
||||||
private static final Integer CUSTOMER_ID = new Integer( 1 );
|
private static final Integer CUSTOMER_ID = new Integer( 1 );
|
||||||
|
|
||||||
static int test = 0;
|
|
||||||
|
|
||||||
private EmbeddedCacheManager localManager, remoteManager;
|
private EmbeddedCacheManager localManager, remoteManager;
|
||||||
private Cache localCustomerCache, remoteCustomerCache;
|
private Cache localCustomerCache, remoteCustomerCache;
|
||||||
private Cache localContactCache, remoteContactCache;
|
private Cache localContactCache, remoteContactCache;
|
||||||
private Cache localCollectionCache, remoteCollectionCache;
|
private Cache localCollectionCache, remoteCollectionCache;
|
||||||
private MyListener localListener, remoteListener;
|
private MyListener localListener, remoteListener;
|
||||||
private TransactionManager localTM, remoteTM;
|
|
||||||
private SessionFactory localFactory, remoteFactory;
|
private SessionFactory localFactory, remoteFactory;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<Object[]> getParameters() {
|
||||||
|
return Arrays.asList(TRANSACTIONAL, READ_WRITE);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void startUp() {
|
public void startUp() {
|
||||||
super.startUp();
|
super.startUp();
|
||||||
// Bind a listener to the "local" cache
|
// Bind a listener to the "local" cache
|
||||||
// Our region factory makes its CacheManager available to us
|
// Our region factory makes its CacheManager available to us
|
||||||
localManager = ClusterAwareRegionFactory.getCacheManager( DualNodeTestCase.LOCAL );
|
localManager = ClusterAwareRegionFactory.getCacheManager( DualNodeTest.LOCAL );
|
||||||
// Cache localCache = localManager.getCache("entity");
|
// Cache localCache = localManager.getCache("entity");
|
||||||
localCustomerCache = localManager.getCache( Customer.class.getName() );
|
localCustomerCache = localManager.getCache( Customer.class.getName() );
|
||||||
localContactCache = localManager.getCache( Contact.class.getName() );
|
localContactCache = localManager.getCache( Contact.class.getName() );
|
||||||
|
@ -79,7 +81,7 @@ public class EntityCollectionInvalidationTestCase extends DualNodeTestCase {
|
||||||
localCollectionCache.addListener( localListener );
|
localCollectionCache.addListener( localListener );
|
||||||
|
|
||||||
// Bind a listener to the "remote" cache
|
// Bind a listener to the "remote" cache
|
||||||
remoteManager = ClusterAwareRegionFactory.getCacheManager( DualNodeTestCase.REMOTE );
|
remoteManager = ClusterAwareRegionFactory.getCacheManager( DualNodeTest.REMOTE );
|
||||||
remoteCustomerCache = remoteManager.getCache( Customer.class.getName() );
|
remoteCustomerCache = remoteManager.getCache( Customer.class.getName() );
|
||||||
remoteContactCache = remoteManager.getCache( Contact.class.getName() );
|
remoteContactCache = remoteManager.getCache( Contact.class.getName() );
|
||||||
remoteCollectionCache = remoteManager.getCache( Customer.class.getName() + ".contacts" );
|
remoteCollectionCache = remoteManager.getCache( Customer.class.getName() + ".contacts" );
|
||||||
|
@ -90,9 +92,6 @@ public class EntityCollectionInvalidationTestCase extends DualNodeTestCase {
|
||||||
|
|
||||||
localFactory = sessionFactory();
|
localFactory = sessionFactory();
|
||||||
remoteFactory = secondNodeEnvironment().getSessionFactory();
|
remoteFactory = secondNodeEnvironment().getSessionFactory();
|
||||||
|
|
||||||
localTM = DualNodeJtaTransactionManagerImpl.getInstance( DualNodeTestCase.LOCAL );
|
|
||||||
remoteTM = DualNodeJtaTransactionManagerImpl.getInstance( DualNodeTestCase.REMOTE );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -102,9 +101,9 @@ public class EntityCollectionInvalidationTestCase extends DualNodeTestCase {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void cleanupTest() throws Exception {
|
protected void cleanupTest() throws Exception {
|
||||||
cleanup(localFactory, localTM);
|
cleanup(localFactory);
|
||||||
localListener.clear();
|
localListener.clear();
|
||||||
remoteListener.clear();
|
remoteListener.clear();
|
||||||
// do not call super.cleanupTest becasue we would clean transaction managers
|
// do not call super.cleanupTest becasue we would clean transaction managers
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -115,7 +114,7 @@ public class EntityCollectionInvalidationTestCase extends DualNodeTestCase {
|
||||||
assertTrue( localListener.isEmpty() );
|
assertTrue( localListener.isEmpty() );
|
||||||
|
|
||||||
log.debug( "Create node 0" );
|
log.debug( "Create node 0" );
|
||||||
IdContainer ids = createCustomer( localFactory, localTM );
|
IdContainer ids = createCustomer( localFactory );
|
||||||
|
|
||||||
assertTrue( remoteListener.isEmpty() );
|
assertTrue( remoteListener.isEmpty() );
|
||||||
assertTrue( localListener.isEmpty() );
|
assertTrue( localListener.isEmpty() );
|
||||||
|
@ -126,7 +125,7 @@ public class EntityCollectionInvalidationTestCase extends DualNodeTestCase {
|
||||||
|
|
||||||
log.debug( "Find node 0" );
|
log.debug( "Find node 0" );
|
||||||
// This actually brings the collection into the cache
|
// This actually brings the collection into the cache
|
||||||
getCustomer( ids.customerId, localFactory, localTM );
|
getCustomer( ids.customerId, localFactory );
|
||||||
|
|
||||||
sleep( SLEEP_TIME );
|
sleep( SLEEP_TIME );
|
||||||
|
|
||||||
|
@ -134,7 +133,7 @@ public class EntityCollectionInvalidationTestCase extends DualNodeTestCase {
|
||||||
// should read everything from the cache
|
// should read everything from the cache
|
||||||
log.debug( "Find(2) node 0" );
|
log.debug( "Find(2) node 0" );
|
||||||
localListener.clear();
|
localListener.clear();
|
||||||
getCustomer( ids.customerId, localFactory, localTM );
|
getCustomer( ids.customerId, localFactory );
|
||||||
|
|
||||||
// Check the read came from the cache
|
// Check the read came from the cache
|
||||||
log.debug( "Check cache 0" );
|
log.debug( "Check cache 0" );
|
||||||
|
@ -142,13 +141,13 @@ public class EntityCollectionInvalidationTestCase extends DualNodeTestCase {
|
||||||
|
|
||||||
log.debug( "Find node 1" );
|
log.debug( "Find node 1" );
|
||||||
// This actually brings the collection into the cache since invalidation is in use
|
// This actually brings the collection into the cache since invalidation is in use
|
||||||
getCustomer( ids.customerId, remoteFactory, remoteTM );
|
getCustomer( ids.customerId, remoteFactory );
|
||||||
|
|
||||||
// Now the collection is in the cache so, the 2nd "get"
|
// Now the collection is in the cache so, the 2nd "get"
|
||||||
// should read everything from the cache
|
// should read everything from the cache
|
||||||
log.debug( "Find(2) node 1" );
|
log.debug( "Find(2) node 1" );
|
||||||
remoteListener.clear();
|
remoteListener.clear();
|
||||||
getCustomer( ids.customerId, remoteFactory, remoteTM );
|
getCustomer( ids.customerId, remoteFactory );
|
||||||
|
|
||||||
// Check the read came from the cache
|
// Check the read came from the cache
|
||||||
log.debug( "Check cache 1" );
|
log.debug( "Check cache 1" );
|
||||||
|
@ -156,7 +155,7 @@ public class EntityCollectionInvalidationTestCase extends DualNodeTestCase {
|
||||||
|
|
||||||
// Modify customer in remote
|
// Modify customer in remote
|
||||||
remoteListener.clear();
|
remoteListener.clear();
|
||||||
ids = modifyCustomer( ids.customerId, remoteFactory, remoteTM );
|
ids = modifyCustomer( ids.customerId, remoteFactory );
|
||||||
sleep( 250 );
|
sleep( 250 );
|
||||||
assertLoadedFromCache( remoteListener, ids.customerId, ids.contactIds );
|
assertLoadedFromCache( remoteListener, ids.customerId, ids.contactIds );
|
||||||
|
|
||||||
|
@ -178,28 +177,18 @@ public class EntityCollectionInvalidationTestCase extends DualNodeTestCase {
|
||||||
remotePPCache.getAdvancedCache().addInterceptor(hookInterceptor, 0);
|
remotePPCache.getAdvancedCache().addInterceptor(hookInterceptor, 0);
|
||||||
|
|
||||||
IdContainer idContainer = new IdContainer();
|
IdContainer idContainer = new IdContainer();
|
||||||
withTx(localTM, () -> {
|
withTxSession(localFactory, s -> {
|
||||||
Session s = localFactory.getCurrentSession();
|
|
||||||
s.getTransaction().begin();
|
|
||||||
Customer customer = new Customer();
|
Customer customer = new Customer();
|
||||||
customer.setName( "JBoss" );
|
customer.setName( "JBoss" );
|
||||||
s.persist(customer);
|
s.persist(customer);
|
||||||
s.getTransaction().commit();
|
|
||||||
s.close();
|
|
||||||
idContainer.customerId = customer.getId();
|
idContainer.customerId = customer.getId();
|
||||||
return null;
|
|
||||||
});
|
});
|
||||||
// start loading
|
// start loading
|
||||||
|
|
||||||
Thread getThread = new Thread(() -> {
|
Thread getThread = new Thread(() -> {
|
||||||
try {
|
try {
|
||||||
withTx(remoteTM, () -> {
|
withTxSession(remoteFactory, s -> {
|
||||||
Session s = remoteFactory.getCurrentSession();
|
|
||||||
s.getTransaction().begin();
|
|
||||||
s.get(Customer.class, idContainer.customerId);
|
s.get(Customer.class, idContainer.customerId);
|
||||||
s.getTransaction().commit();
|
|
||||||
s.close();
|
|
||||||
return null;
|
|
||||||
});
|
});
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
log.error("Failure to get customer", e);
|
log.error("Failure to get customer", e);
|
||||||
|
@ -208,13 +197,9 @@ public class EntityCollectionInvalidationTestCase extends DualNodeTestCase {
|
||||||
}, "get-thread");
|
}, "get-thread");
|
||||||
Thread deleteThread = new Thread(() -> {
|
Thread deleteThread = new Thread(() -> {
|
||||||
try {
|
try {
|
||||||
withTx(localTM, () -> {
|
withTxSession(localFactory, s -> {
|
||||||
Session s = localFactory.getCurrentSession();
|
|
||||||
s.getTransaction().begin();
|
|
||||||
Customer customer = s.get(Customer.class, idContainer.customerId);
|
Customer customer = s.get(Customer.class, idContainer.customerId);
|
||||||
s.delete(customer);
|
s.delete(customer);
|
||||||
s.getTransaction().commit();
|
|
||||||
return null;
|
|
||||||
});
|
});
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
log.error("Failure to delete customer", e);
|
log.error("Failure to delete customer", e);
|
||||||
|
@ -239,9 +224,9 @@ public class EntityCollectionInvalidationTestCase extends DualNodeTestCase {
|
||||||
throw new IllegalStateException("delete-thread failed", deleteException.get());
|
throw new IllegalStateException("delete-thread failed", deleteException.get());
|
||||||
}
|
}
|
||||||
|
|
||||||
Customer localCustomer = getCustomer(idContainer.customerId, localFactory, localTM);
|
Customer localCustomer = getCustomer(idContainer.customerId, localFactory);
|
||||||
assertNull(localCustomer);
|
assertNull(localCustomer);
|
||||||
Customer remoteCustomer = getCustomer(idContainer.customerId, remoteFactory, remoteTM);
|
Customer remoteCustomer = getCustomer(idContainer.customerId, remoteFactory);
|
||||||
assertNull(remoteCustomer);
|
assertNull(remoteCustomer);
|
||||||
assertTrue(remoteCustomerCache.isEmpty());
|
assertTrue(remoteCustomerCache.isEmpty());
|
||||||
}
|
}
|
||||||
|
@ -255,83 +240,47 @@ public class EntityCollectionInvalidationTestCase extends DualNodeTestCase {
|
||||||
assertTrue( remoteCollectionCache.isEmpty() );
|
assertTrue( remoteCollectionCache.isEmpty() );
|
||||||
}
|
}
|
||||||
|
|
||||||
private IdContainer createCustomer(SessionFactory sessionFactory, TransactionManager tm)
|
private IdContainer createCustomer(SessionFactory sessionFactory)
|
||||||
throws Exception {
|
throws Exception {
|
||||||
log.debug( "CREATE CUSTOMER" );
|
log.debug( "CREATE CUSTOMER" );
|
||||||
|
|
||||||
tm.begin();
|
Customer customer = new Customer();
|
||||||
|
customer.setName("JBoss");
|
||||||
|
Set<Contact> contacts = new HashSet<Contact>();
|
||||||
|
|
||||||
try {
|
Contact kabir = new Contact();
|
||||||
Session session = sessionFactory.getCurrentSession();
|
kabir.setCustomer(customer);
|
||||||
Customer customer = new Customer();
|
kabir.setName("Kabir");
|
||||||
customer.setName( "JBoss" );
|
kabir.setTlf("1111");
|
||||||
Set<Contact> contacts = new HashSet<Contact>();
|
contacts.add(kabir);
|
||||||
|
|
||||||
Contact kabir = new Contact();
|
Contact bill = new Contact();
|
||||||
kabir.setCustomer( customer );
|
bill.setCustomer(customer);
|
||||||
kabir.setName( "Kabir" );
|
bill.setName("Bill");
|
||||||
kabir.setTlf( "1111" );
|
bill.setTlf("2222");
|
||||||
contacts.add( kabir );
|
contacts.add(bill);
|
||||||
|
|
||||||
Contact bill = new Contact();
|
customer.setContacts(contacts);
|
||||||
bill.setCustomer( customer );
|
|
||||||
bill.setName( "Bill" );
|
|
||||||
bill.setTlf( "2222" );
|
|
||||||
contacts.add( bill );
|
|
||||||
|
|
||||||
customer.setContacts( contacts );
|
withTxSession(sessionFactory, session -> session.save(customer));
|
||||||
|
|
||||||
session.save( customer );
|
IdContainer ids = new IdContainer();
|
||||||
tm.commit();
|
ids.customerId = customer.getId();
|
||||||
|
Set contactIds = new HashSet();
|
||||||
|
contactIds.add( kabir.getId() );
|
||||||
|
contactIds.add( bill.getId() );
|
||||||
|
ids.contactIds = contactIds;
|
||||||
|
|
||||||
IdContainer ids = new IdContainer();
|
log.debug( "CREATE CUSTOMER - END" );
|
||||||
ids.customerId = customer.getId();
|
return ids;
|
||||||
Set contactIds = new HashSet();
|
|
||||||
contactIds.add( kabir.getId() );
|
|
||||||
contactIds.add( bill.getId() );
|
|
||||||
ids.contactIds = contactIds;
|
|
||||||
|
|
||||||
return ids;
|
|
||||||
}
|
|
||||||
catch (Exception e) {
|
|
||||||
log.error( "Caught exception creating customer", e );
|
|
||||||
try {
|
|
||||||
tm.rollback();
|
|
||||||
}
|
|
||||||
catch (Exception e1) {
|
|
||||||
log.error( "Exception rolling back txn", e1 );
|
|
||||||
}
|
|
||||||
throw e;
|
|
||||||
}
|
|
||||||
finally {
|
|
||||||
log.debug( "CREATE CUSTOMER - END" );
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private Customer getCustomer(Integer id, SessionFactory sessionFactory, TransactionManager tm) throws Exception {
|
private Customer getCustomer(Integer id, SessionFactory sessionFactory) throws Exception {
|
||||||
log.debug( "Find customer with id=" + id );
|
log.debug( "Find customer with id=" + id );
|
||||||
tm.begin();
|
return withTxSessionApply(sessionFactory, session -> doGetCustomer(id, session));
|
||||||
try {
|
|
||||||
Session session = sessionFactory.getCurrentSession();
|
|
||||||
Customer customer = doGetCustomer( id, session, tm );
|
|
||||||
tm.commit();
|
|
||||||
return customer;
|
|
||||||
}
|
|
||||||
catch (Exception e) {
|
|
||||||
try {
|
|
||||||
tm.rollback();
|
|
||||||
}
|
|
||||||
catch (Exception e1) {
|
|
||||||
log.error( "Exception rolling back txn", e1 );
|
|
||||||
}
|
|
||||||
throw e;
|
|
||||||
}
|
|
||||||
finally {
|
|
||||||
log.debug( "Find customer ended." );
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private Customer doGetCustomer(Integer id, Session session, TransactionManager tm) throws Exception {
|
private Customer doGetCustomer(Integer id, Session session) throws Exception {
|
||||||
Customer customer = session.get( Customer.class, id );
|
Customer customer = session.get( Customer.class, id );
|
||||||
if (customer == null) {
|
if (customer == null) {
|
||||||
return null;
|
return null;
|
||||||
|
@ -346,15 +295,13 @@ public class EntityCollectionInvalidationTestCase extends DualNodeTestCase {
|
||||||
return customer;
|
return customer;
|
||||||
}
|
}
|
||||||
|
|
||||||
private IdContainer modifyCustomer(Integer id, SessionFactory sessionFactory, TransactionManager tm)
|
private IdContainer modifyCustomer(Integer id, SessionFactory sessionFactory)
|
||||||
throws Exception {
|
throws Exception {
|
||||||
log.debug( "Modify customer with id=" + id );
|
log.debug( "Modify customer with id=" + id );
|
||||||
tm.begin();
|
return withTxSessionApply(sessionFactory, session -> {
|
||||||
try {
|
|
||||||
Session session = sessionFactory.getCurrentSession();
|
|
||||||
IdContainer ids = new IdContainer();
|
IdContainer ids = new IdContainer();
|
||||||
Set contactIds = new HashSet();
|
Set contactIds = new HashSet();
|
||||||
Customer customer = doGetCustomer( id, session, tm );
|
Customer customer = doGetCustomer( id, session );
|
||||||
customer.setName( "NewJBoss" );
|
customer.setName( "NewJBoss" );
|
||||||
ids.customerId = customer.getId();
|
ids.customerId = customer.getId();
|
||||||
Set<Contact> contacts = customer.getContacts();
|
Set<Contact> contacts = customer.getContacts();
|
||||||
|
@ -368,51 +315,26 @@ public class EntityCollectionInvalidationTestCase extends DualNodeTestCase {
|
||||||
contact.setCustomer( null );
|
contact.setCustomer( null );
|
||||||
|
|
||||||
session.save( customer );
|
session.save( customer );
|
||||||
tm.commit();
|
|
||||||
return ids;
|
return ids;
|
||||||
}
|
});
|
||||||
catch (Exception e) {
|
|
||||||
try {
|
|
||||||
tm.rollback();
|
|
||||||
}
|
|
||||||
catch (Exception e1) {
|
|
||||||
log.error( "Exception rolling back txn", e1 );
|
|
||||||
}
|
|
||||||
throw e;
|
|
||||||
}
|
|
||||||
finally {
|
|
||||||
log.debug( "Find customer ended." );
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void cleanup(SessionFactory sessionFactory, TransactionManager tm) throws Exception {
|
private void cleanup(SessionFactory sessionFactory) throws Exception {
|
||||||
tm.begin();
|
withTxSession(sessionFactory, session -> {
|
||||||
try {
|
Customer c = (Customer) session.get(Customer.class, CUSTOMER_ID);
|
||||||
Session session = sessionFactory.getCurrentSession();
|
if (c != null) {
|
||||||
Customer c = (Customer) session.get( Customer.class, CUSTOMER_ID );
|
|
||||||
if ( c != null ) {
|
|
||||||
Set contacts = c.getContacts();
|
Set contacts = c.getContacts();
|
||||||
for ( Iterator it = contacts.iterator(); it.hasNext(); ) {
|
for (Iterator it = contacts.iterator(); it.hasNext(); ) {
|
||||||
session.delete( it.next() );
|
session.delete(it.next());
|
||||||
}
|
}
|
||||||
c.setContacts( null );
|
c.setContacts(null);
|
||||||
session.delete( c );
|
session.delete(c);
|
||||||
}
|
}
|
||||||
// since we don't use orphan removal, some contacts may persist
|
// since we don't use orphan removal, some contacts may persist
|
||||||
for (Object contact : session.createCriteria(Contact.class).list()) {
|
for (Object contact : session.createCriteria(Contact.class).list()) {
|
||||||
session.delete(contact);
|
session.delete(contact);
|
||||||
}
|
}
|
||||||
tm.commit();
|
});
|
||||||
}
|
|
||||||
catch (Exception e) {
|
|
||||||
try {
|
|
||||||
tm.rollback();
|
|
||||||
}
|
|
||||||
catch (Exception e1) {
|
|
||||||
log.error( "Exception rolling back txn", e1 );
|
|
||||||
}
|
|
||||||
log.error( "Caught exception in cleanup", e );
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void assertLoadedFromCache(MyListener listener, Integer custId, Set contactIds) {
|
private void assertLoadedFromCache(MyListener listener, Integer custId, Set contactIds) {
|
||||||
|
@ -477,7 +399,7 @@ public class EntityCollectionInvalidationTestCase extends DualNodeTestCase {
|
||||||
String key = event.getCache().getName() + "#" + event.getKey();
|
String key = event.getCache().getName() + "#" + event.getKey();
|
||||||
log.debug( "MyListener[" + name + "] - Visiting key " + key );
|
log.debug( "MyListener[" + name + "] - Visiting key " + key );
|
||||||
// String name = fqn.toString();
|
// String name = fqn.toString();
|
||||||
String token = ".functional.";
|
String token = ".entities.";
|
||||||
int index = key.indexOf( token );
|
int index = key.indexOf( token );
|
||||||
if ( index > -1 ) {
|
if ( index > -1 ) {
|
||||||
index += token.length();
|
index += token.length();
|
|
@ -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
|
* @author Galder Zamarreño
|
||||||
* @since 3.5
|
* @since 3.5
|
||||||
*/
|
*/
|
||||||
public class RepeatableSessionRefreshTest extends SessionRefreshTestCase {
|
public class RepeatableSessionRefreshTest extends SessionRefreshTest {
|
||||||
private static final String CACHE_CONFIG = "entity-repeatable";
|
private static final String CACHE_CONFIG = "entity-repeatable";
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected String getEntityCacheConfigName() {
|
protected String getEntityCacheConfigName() {
|
||||||
return CACHE_CONFIG;
|
return CACHE_CONFIG;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,16 +6,16 @@
|
||||||
*/
|
*/
|
||||||
package org.hibernate.test.cache.infinispan.functional.cluster;
|
package org.hibernate.test.cache.infinispan.functional.cluster;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import javax.transaction.TransactionManager;
|
|
||||||
|
|
||||||
import org.hibernate.SessionFactory;
|
import org.hibernate.SessionFactory;
|
||||||
import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
|
import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
|
||||||
import org.hibernate.cache.infinispan.InfinispanRegionFactory;
|
import org.hibernate.cache.infinispan.InfinispanRegionFactory;
|
||||||
import org.hibernate.cfg.Environment;
|
import org.hibernate.cfg.Environment;
|
||||||
|
|
||||||
import org.hibernate.test.cache.infinispan.functional.classloader.Account;
|
import org.hibernate.test.cache.infinispan.functional.entities.Account;
|
||||||
import org.hibernate.test.cache.infinispan.functional.classloader.ClassLoaderTestDAO;
|
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
import org.infinispan.Cache;
|
import org.infinispan.Cache;
|
||||||
|
@ -33,12 +33,16 @@ import static org.junit.Assert.assertNotNull;
|
||||||
* @author Galder Zamarreño
|
* @author Galder Zamarreño
|
||||||
* @since 3.5
|
* @since 3.5
|
||||||
*/
|
*/
|
||||||
public class SessionRefreshTestCase extends DualNodeTestCase {
|
public class SessionRefreshTest extends DualNodeTest {
|
||||||
private static final Logger log = Logger.getLogger( SessionRefreshTestCase.class );
|
private static final Logger log = Logger.getLogger( SessionRefreshTest.class );
|
||||||
|
|
||||||
static int test = 0;
|
|
||||||
private Cache localCache;
|
private Cache localCache;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<Object[]> getParameters() {
|
||||||
|
return Arrays.asList(TRANSACTIONAL, READ_WRITE);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void configureSecondNode(StandardServiceRegistryBuilder ssrb) {
|
protected void configureSecondNode(StandardServiceRegistryBuilder ssrb) {
|
||||||
super.configureSecondNode( ssrb );
|
super.configureSecondNode( ssrb );
|
||||||
|
@ -57,7 +61,7 @@ public class SessionRefreshTestCase extends DualNodeTestCase {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String[] getMappings() {
|
public String[] getMappings() {
|
||||||
return new String[] {"cache/infinispan/functional/classloader/Account.hbm.xml"};
|
return new String[] {"cache/infinispan/functional/entities/Account.hbm.xml"};
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -70,49 +74,47 @@ public class SessionRefreshTestCase extends DualNodeTestCase {
|
||||||
@Test
|
@Test
|
||||||
public void testRefreshAfterExternalChange() throws Exception {
|
public void testRefreshAfterExternalChange() throws Exception {
|
||||||
// First session factory uses a cache
|
// First session factory uses a cache
|
||||||
CacheContainer localManager = ClusterAwareRegionFactory.getCacheManager( DualNodeTestCase.LOCAL );
|
CacheContainer localManager = ClusterAwareRegionFactory.getCacheManager( DualNodeTest.LOCAL );
|
||||||
localCache = localManager.getCache( Account.class.getName() );
|
localCache = localManager.getCache( Account.class.getName() );
|
||||||
TransactionManager localTM = DualNodeJtaTransactionManagerImpl.getInstance( DualNodeTestCase.LOCAL );
|
|
||||||
SessionFactory localFactory = sessionFactory();
|
SessionFactory localFactory = sessionFactory();
|
||||||
|
|
||||||
// Second session factory doesn't; just needs a transaction manager
|
// Second session factory doesn't; just needs a transaction manager
|
||||||
TransactionManager remoteTM = DualNodeJtaTransactionManagerImpl.getInstance( DualNodeTestCase.REMOTE );
|
|
||||||
SessionFactory remoteFactory = secondNodeEnvironment().getSessionFactory();
|
SessionFactory remoteFactory = secondNodeEnvironment().getSessionFactory();
|
||||||
|
|
||||||
ClassLoaderTestDAO dao0 = new ClassLoaderTestDAO( localFactory, localTM );
|
AccountDAO dao0 = new AccountDAO(useJta, localFactory );
|
||||||
ClassLoaderTestDAO dao1 = new ClassLoaderTestDAO( remoteFactory, remoteTM );
|
AccountDAO dao1 = new AccountDAO(useJta, remoteFactory );
|
||||||
|
|
||||||
Integer id = new Integer( 1 );
|
Integer id = new Integer( 1 );
|
||||||
dao0.createAccount( dao0.getSmith(), id, new Integer( 5 ), DualNodeTestCase.LOCAL );
|
dao0.createAccount( dao0.getSmith(), id, new Integer( 5 ), DualNodeTest.LOCAL );
|
||||||
|
|
||||||
// Basic sanity check
|
// Basic sanity check
|
||||||
Account acct1 = dao1.getAccount( id );
|
Account acct1 = dao1.getAccount( id );
|
||||||
assertNotNull( acct1 );
|
assertNotNull( acct1 );
|
||||||
assertEquals( DualNodeTestCase.LOCAL, acct1.getBranch() );
|
assertEquals( DualNodeTest.LOCAL, acct1.getBranch() );
|
||||||
|
|
||||||
// This dao's session factory isn't caching, so cache won't see this change
|
// This dao's session factory isn't caching, so cache won't see this change
|
||||||
dao1.updateAccountBranch( id, DualNodeTestCase.REMOTE );
|
dao1.updateAccountBranch( id, DualNodeTest.REMOTE );
|
||||||
|
|
||||||
// dao1's session doesn't touch the cache,
|
// dao1's session doesn't touch the cache,
|
||||||
// so reading from dao0 should show a stale value from the cache
|
// so reading from dao0 should show a stale value from the cache
|
||||||
// (we check to confirm the cache is used)
|
// (we check to confirm the cache is used)
|
||||||
Account acct0 = dao0.getAccount( id );
|
Account acct0 = dao0.getAccount( id );
|
||||||
assertNotNull( acct0 );
|
assertNotNull( acct0 );
|
||||||
assertEquals( DualNodeTestCase.LOCAL, acct0.getBranch() );
|
assertEquals( DualNodeTest.LOCAL, acct0.getBranch() );
|
||||||
log.debug( "Contents when re-reading from local: " + TestingUtil.printCache( localCache ) );
|
log.debug( "Contents when re-reading from local: " + TestingUtil.printCache( localCache ) );
|
||||||
|
|
||||||
// Now call session.refresh and confirm we get the correct value
|
// Now call session.refresh and confirm we get the correct value
|
||||||
acct0 = dao0.getAccountWithRefresh( id );
|
acct0 = dao0.getAccountWithRefresh( id );
|
||||||
assertNotNull( acct0 );
|
assertNotNull( acct0 );
|
||||||
assertEquals( DualNodeTestCase.REMOTE, acct0.getBranch() );
|
assertEquals( DualNodeTest.REMOTE, acct0.getBranch() );
|
||||||
log.debug( "Contents after refreshing in remote: " + TestingUtil.printCache( localCache ) );
|
log.debug( "Contents after refreshing in remote: " + TestingUtil.printCache( localCache ) );
|
||||||
|
|
||||||
// Double check with a brand new session, in case the other session
|
// Double check with a brand new session, in case the other session
|
||||||
// for some reason bypassed the 2nd level cache
|
// for some reason bypassed the 2nd level cache
|
||||||
ClassLoaderTestDAO dao0A = new ClassLoaderTestDAO( localFactory, localTM );
|
AccountDAO dao0A = new AccountDAO(useJta, localFactory );
|
||||||
Account acct0A = dao0A.getAccount( id );
|
Account acct0A = dao0A.getAccount( id );
|
||||||
assertNotNull( acct0A );
|
assertNotNull( acct0A );
|
||||||
assertEquals( DualNodeTestCase.REMOTE, acct0A.getBranch() );
|
assertEquals( DualNodeTest.REMOTE, acct0A.getBranch() );
|
||||||
log.debug( "Contents after creating a new session: " + TestingUtil.printCache( localCache ) );
|
log.debug( "Contents after creating a new session: " + TestingUtil.printCache( localCache ) );
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -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>.
|
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package org.hibernate.test.cache.infinispan.functional;
|
package org.hibernate.test.cache.infinispan.functional.entities;
|
||||||
import javax.persistence.Entity;
|
import javax.persistence.Entity;
|
||||||
import javax.persistence.GeneratedValue;
|
import javax.persistence.GeneratedValue;
|
||||||
import javax.persistence.Id;
|
import javax.persistence.Id;
|
|
@ -6,7 +6,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
//$Id$
|
//$Id$
|
||||||
package org.hibernate.test.cache.infinispan.functional;
|
package org.hibernate.test.cache.infinispan.functional.entities;
|
||||||
|
|
||||||
import javax.persistence.Entity;
|
import javax.persistence.Entity;
|
||||||
import javax.persistence.GeneratedValue;
|
import javax.persistence.GeneratedValue;
|
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.
|
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
|
||||||
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
|
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
|
||||||
*/
|
*/
|
||||||
package org.hibernate.test.cache.infinispan.functional;
|
package org.hibernate.test.cache.infinispan.functional.entities;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
|
@ -1,4 +1,4 @@
|
||||||
package org.hibernate.test.cache.infinispan.functional;
|
package org.hibernate.test.cache.infinispan.functional.entities;
|
||||||
|
|
||||||
import javax.persistence.Embeddable;
|
import javax.persistence.Embeddable;
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
|
@ -4,7 +4,7 @@
|
||||||
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
|
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
|
||||||
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
|
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
|
||||||
*/
|
*/
|
||||||
package org.hibernate.test.cache.infinispan.functional;
|
package org.hibernate.test.cache.infinispan.functional.entities;
|
||||||
|
|
||||||
import org.hibernate.annotations.NaturalId;
|
import org.hibernate.annotations.NaturalId;
|
||||||
import org.hibernate.annotations.NaturalIdCache;
|
import org.hibernate.annotations.NaturalIdCache;
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue