HHH-8159 - Apply fixups indicated by analysis tools

This commit is contained in:
Steve Ebersole 2013-05-01 18:53:55 -05:00
parent 1b637030bd
commit bfbc0b88a8
41 changed files with 2528 additions and 1929 deletions

View File

@ -23,7 +23,6 @@
*/
package org.hibernate.cache.ehcache;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.Properties;
@ -47,13 +46,11 @@ import org.hibernate.cfg.Settings;
* @author Alex Snaps
*/
public class EhCacheRegionFactory extends AbstractEhcacheRegionFactory {
private static final EhCacheMessageLogger LOG = Logger.getMessageLogger(
EhCacheMessageLogger.class,
EhCacheRegionFactory.class.getName()
);
/**
* Creates a non-singleton EhCacheRegionFactory
*/
@ -89,7 +86,7 @@ public class EhCacheRegionFactory extends AbstractEhcacheRegionFactory {
manager = new CacheManager( configuration );
}
else {
URL url = loadResource( configurationResourceName );
final URL url = loadResource( configurationResourceName );
final Configuration configuration = HibernateEhcacheUtils.loadAndCorrectConfiguration( url );
manager = new CacheManager( configuration );
}

View File

@ -49,23 +49,17 @@ public class ReadOnlyEhcacheEntityRegionAccessStrategy extends AbstractEhcacheAc
super( region, settings );
}
/**
* {@inheritDoc}
*/
@Override
public EntityRegion getRegion() {
return region();
}
/**
* {@inheritDoc}
*/
@Override
public Object get(Object key, long txTimestamp) throws CacheException {
return region().get( key );
}
/**
* {@inheritDoc}
*/
@Override
public boolean putFromLoad(Object key, Object value, long txTimestamp, Object version, boolean minimalPutOverride)
throws CacheException {
if ( minimalPutOverride && region().contains( key ) ) {
@ -77,47 +71,58 @@ public class ReadOnlyEhcacheEntityRegionAccessStrategy extends AbstractEhcacheAc
}
}
@Override
public SoftLock lockItem(Object key, Object version) throws UnsupportedOperationException {
return null;
}
/**
* {@inheritDoc}
* <p/>
* A no-op since this cache is read-only
*/
@Override
public void unlockItem(Object key, SoftLock lock) throws CacheException {
evict( key );
}
/**
* {@inheritDoc}
* <p/>
* This cache is asynchronous hence a no-op
*/
@Override
public boolean insert(Object key, Object value, Object version) throws CacheException {
return false;
}
/**
* {@inheritDoc}
*/
@Override
public boolean afterInsert(Object key, Object value, Object version) throws CacheException {
region().put( key, value );
return true;
}
/**
* {@inheritDoc}
* <p/>
* Throws UnsupportedOperationException since this cache is read-only
*
* @throws UnsupportedOperationException always
*/
@Override
public boolean update(Object key, Object value, Object currentVersion, Object previousVersion)
throws UnsupportedOperationException {
throw new UnsupportedOperationException( "Can't write to a readonly object" );
}
/**
* {@inheritDoc}
* <p/>
* Throws UnsupportedOperationException since this cache is read-only
*
* @throws UnsupportedOperationException always
*/
@Override
public boolean afterUpdate(Object key, Object value, Object currentVersion, Object previousVersion, SoftLock lock)
throws UnsupportedOperationException {
throw new UnsupportedOperationException( "Can't write to a readonly object" );

View File

@ -115,7 +115,7 @@ public class ReadWriteEhcacheEntityRegionAccessStrategy
final Lockable item = (Lockable) region().get( key );
if ( item != null && item.isUnlockable( lock ) ) {
Lock lockItem = (Lock) item;
final Lock lockItem = (Lock) item;
if ( lockItem.wasLockedConcurrently() ) {
decrementLock( key, lockItem );
return false;

View File

@ -21,74 +21,88 @@
*/
package org.hibernate.cache.infinispan;
import java.util.Properties;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import java.util.Properties;
import org.hibernate.cache.CacheException;
import org.hibernate.internal.util.config.ConfigurationHelper;
import org.hibernate.internal.util.jndi.JndiHelper;
import org.infinispan.manager.EmbeddedCacheManager;
import org.infinispan.util.logging.Log;
import org.infinispan.util.logging.LogFactory;
import org.hibernate.cache.CacheException;
import org.hibernate.internal.util.config.ConfigurationHelper;
import org.hibernate.internal.util.jndi.JndiHelper;
/**
* A {@link org.hibernate.cache.spi.RegionFactory} for <a href="http://www.jboss.org/infinispan">Infinispan</a>-backed cache
* regions that finds its cache manager in JNDI rather than creating one itself.
*
*
* @author Galder Zamarreño
* @since 3.5
*/
public class JndiInfinispanRegionFactory extends InfinispanRegionFactory {
private static final Log log = LogFactory.getLog(JndiInfinispanRegionFactory.class);
private static final Log log = LogFactory.getLog( JndiInfinispanRegionFactory.class );
/**
* Specifies the JNDI name under which the {@link EmbeddedCacheManager} to use is bound.
* There is no default value -- the user must specify the property.
*/
public static final String CACHE_MANAGER_RESOURCE_PROP = "hibernate.cache.infinispan.cachemanager";
/**
* Specifies the JNDI name under which the {@link EmbeddedCacheManager} to use is bound.
* There is no default value -- the user must specify the property.
*/
public static final String CACHE_MANAGER_RESOURCE_PROP = "hibernate.cache.infinispan.cachemanager";
public JndiInfinispanRegionFactory() {
super();
}
/**
* Constructs a JndiInfinispanRegionFactory
*/
@SuppressWarnings("UnusedDeclaration")
public JndiInfinispanRegionFactory() {
super();
}
public JndiInfinispanRegionFactory(Properties props) {
super(props);
}
/**
* Constructs a JndiInfinispanRegionFactory
*
* @param props Any properties to apply (not used).
*/
@SuppressWarnings("UnusedDeclaration")
public JndiInfinispanRegionFactory(Properties props) {
super( props );
}
@Override
protected EmbeddedCacheManager createCacheManager(Properties properties) throws CacheException {
String name = ConfigurationHelper.getString(CACHE_MANAGER_RESOURCE_PROP, properties, null);
if (name == null)
throw new CacheException("Configuration property " + CACHE_MANAGER_RESOURCE_PROP + " not set");
return locateCacheManager(name, JndiHelper.extractJndiProperties(properties));
}
@Override
protected EmbeddedCacheManager createCacheManager(Properties properties) throws CacheException {
final String name = ConfigurationHelper.getString( CACHE_MANAGER_RESOURCE_PROP, properties, null );
if ( name == null ) {
throw new CacheException( "Configuration property " + CACHE_MANAGER_RESOURCE_PROP + " not set" );
}
return locateCacheManager( name, JndiHelper.extractJndiProperties( properties ) );
}
private EmbeddedCacheManager locateCacheManager(String jndiNamespace, Properties jndiProperties) {
Context ctx = null;
try {
ctx = new InitialContext(jndiProperties);
return (EmbeddedCacheManager) ctx.lookup(jndiNamespace);
} catch (NamingException ne) {
String msg = "Unable to retrieve CacheManager from JNDI [" + jndiNamespace + "]";
log.info(msg, ne);
throw new CacheException( msg );
} finally {
if (ctx != null) {
try {
ctx.close();
} catch( NamingException ne ) {
log.info("Unable to release initial context", ne);
}
}
}
}
private EmbeddedCacheManager locateCacheManager(String jndiNamespace, Properties jndiProperties) {
Context ctx = null;
try {
ctx = new InitialContext( jndiProperties );
return (EmbeddedCacheManager) ctx.lookup( jndiNamespace );
}
catch (NamingException ne) {
final String msg = "Unable to retrieve CacheManager from JNDI [" + jndiNamespace + "]";
log.info( msg, ne );
throw new CacheException( msg );
}
finally {
if ( ctx != null ) {
try {
ctx.close();
}
catch (NamingException ne) {
log.info( "Unable to release initial context", ne );
}
}
}
}
@Override
public void stop() {
// Do not attempt to stop a cache manager because it wasn't created by this region factory.
}
@Override
public void stop() {
// Do not attempt to stop a cache manager because it wasn't created by this region factory.
}
}

View File

@ -26,9 +26,9 @@ package org.hibernate.cache.infinispan;
import java.util.ArrayList;
import java.util.List;
import org.hibernate.boot.registry.selector.SimpleStrategyRegistrationImpl;
import org.hibernate.boot.registry.selector.StrategyRegistration;
import org.hibernate.boot.registry.selector.StrategyRegistrationProvider;
import org.hibernate.boot.registry.selector.SimpleStrategyRegistrationImpl;
import org.hibernate.cache.spi.RegionFactory;
/**
@ -43,7 +43,7 @@ public class StrategyRegistrationProviderImpl implements StrategyRegistrationPro
final List<StrategyRegistration> strategyRegistrations = new ArrayList<StrategyRegistration>();
strategyRegistrations.add(
new SimpleStrategyRegistrationImpl(
new SimpleStrategyRegistrationImpl<RegionFactory>(
RegionFactory.class,
InfinispanRegionFactory.class,
"infinispan",
@ -53,7 +53,7 @@ public class StrategyRegistrationProviderImpl implements StrategyRegistrationPro
);
strategyRegistrations.add(
new SimpleStrategyRegistrationImpl(
new SimpleStrategyRegistrationImpl<RegionFactory>(
RegionFactory.class,
JndiInfinispanRegionFactory.class,
"infinispan-jndi",

View File

@ -32,129 +32,135 @@ import org.infinispan.eviction.EvictionStrategy;
import org.hibernate.cache.CacheException;
/**
* This class represents Infinispan cache parameters that can be configured via hibernate configuration properties
* for either general entity/collection/query/timestamp data type caches and overrides for individual entity or
* This class represents Infinispan cache parameters that can be configured via hibernate configuration properties
* for either general entity/collection/query/timestamp data type caches and overrides for individual entity or
* collection caches. Configuration these properties override previously defined properties in XML file.
*
*
* @author Galder Zamarreño
* @since 3.5
*/
public class TypeOverrides {
private final Set<String> overridden = new HashSet<String>();
private final Set<String> overridden = new HashSet<String>();
private String cacheName;
private String cacheName;
private EvictionStrategy evictionStrategy;
private EvictionStrategy evictionStrategy;
private long evictionWakeUpInterval;
private long evictionWakeUpInterval;
private int evictionMaxEntries;
private int evictionMaxEntries;
private long expirationLifespan;
private long expirationLifespan;
private long expirationMaxIdle;
private long expirationMaxIdle;
private boolean isExposeStatistics;
private boolean isExposeStatistics;
public String getCacheName() {
return cacheName;
}
public String getCacheName() {
return cacheName;
}
public void setCacheName(String cacheName) {
this.cacheName = cacheName;
}
public void setCacheName(String cacheName) {
this.cacheName = cacheName;
}
public EvictionStrategy getEvictionStrategy() {
return evictionStrategy;
}
public EvictionStrategy getEvictionStrategy() {
return evictionStrategy;
}
public void setEvictionStrategy(String evictionStrategy) {
markAsOverriden("evictionStrategy");
this.evictionStrategy = EvictionStrategy.valueOf(uc(evictionStrategy));
}
public void setEvictionStrategy(String evictionStrategy) {
markAsOverriden( "evictionStrategy" );
this.evictionStrategy = EvictionStrategy.valueOf( uc( evictionStrategy ) );
}
public long getEvictionWakeUpInterval() {
return evictionWakeUpInterval;
}
public long getEvictionWakeUpInterval() {
return evictionWakeUpInterval;
}
public void setEvictionWakeUpInterval(long evictionWakeUpInterval) {
markAsOverriden("evictionWakeUpInterval");
this.evictionWakeUpInterval = evictionWakeUpInterval;
}
public void setEvictionWakeUpInterval(long evictionWakeUpInterval) {
markAsOverriden( "evictionWakeUpInterval" );
this.evictionWakeUpInterval = evictionWakeUpInterval;
}
public int getEvictionMaxEntries() {
return evictionMaxEntries;
}
public int getEvictionMaxEntries() {
return evictionMaxEntries;
}
public void setEvictionMaxEntries(int evictionMaxEntries) {
markAsOverriden("evictionMaxEntries");
this.evictionMaxEntries = evictionMaxEntries;
}
public void setEvictionMaxEntries(int evictionMaxEntries) {
markAsOverriden( "evictionMaxEntries" );
this.evictionMaxEntries = evictionMaxEntries;
}
public long getExpirationLifespan() {
return expirationLifespan;
}
public long getExpirationLifespan() {
return expirationLifespan;
}
public void setExpirationLifespan(long expirationLifespan) {
markAsOverriden("expirationLifespan");
this.expirationLifespan = expirationLifespan;
}
public void setExpirationLifespan(long expirationLifespan) {
markAsOverriden( "expirationLifespan" );
this.expirationLifespan = expirationLifespan;
}
public long getExpirationMaxIdle() {
return expirationMaxIdle;
}
public long getExpirationMaxIdle() {
return expirationMaxIdle;
}
public void setExpirationMaxIdle(long expirationMaxIdle) {
markAsOverriden("expirationMaxIdle");
this.expirationMaxIdle = expirationMaxIdle;
}
public void setExpirationMaxIdle(long expirationMaxIdle) {
markAsOverriden( "expirationMaxIdle" );
this.expirationMaxIdle = expirationMaxIdle;
}
public boolean isExposeStatistics() {
return isExposeStatistics;
}
public boolean isExposeStatistics() {
return isExposeStatistics;
}
public void setExposeStatistics(boolean isExposeStatistics) {
markAsOverriden("isExposeStatistics");
this.isExposeStatistics = isExposeStatistics;
}
public void setExposeStatistics(boolean isExposeStatistics) {
markAsOverriden( "isExposeStatistics" );
this.isExposeStatistics = isExposeStatistics;
}
public void applyTo(ConfigurationBuilder builder) {
if (overridden.contains("evictionStrategy"))
builder.eviction().strategy(evictionStrategy);
if (overridden.contains("evictionWakeUpInterval"))
builder.expiration().wakeUpInterval(evictionWakeUpInterval);
if (overridden.contains("evictionMaxEntries"))
builder.eviction().maxEntries(evictionMaxEntries);
if (overridden.contains("expirationLifespan"))
builder.expiration().lifespan(expirationLifespan);
if (overridden.contains("expirationMaxIdle"))
builder.expiration().maxIdle(expirationMaxIdle);
if (overridden.contains("isExposeStatistics") && isExposeStatistics)
builder.jmxStatistics().enable();
}
public void applyTo(ConfigurationBuilder builder) {
if ( overridden.contains( "evictionStrategy" ) ) {
builder.eviction().strategy( evictionStrategy );
}
if ( overridden.contains( "evictionWakeUpInterval" ) ) {
builder.expiration().wakeUpInterval( evictionWakeUpInterval );
}
if ( overridden.contains( "evictionMaxEntries" ) ) {
builder.eviction().maxEntries( evictionMaxEntries );
}
if ( overridden.contains( "expirationLifespan" ) ) {
builder.expiration().lifespan( expirationLifespan );
}
if ( overridden.contains( "expirationMaxIdle" ) ) {
builder.expiration().maxIdle( expirationMaxIdle );
}
if ( overridden.contains( "isExposeStatistics" ) && isExposeStatistics ) {
builder.jmxStatistics().enable();
}
}
public void validateInfinispanConfiguration(Configuration cfg) throws CacheException {
// no-op, method overriden
}
public void validateInfinispanConfiguration(Configuration cfg) throws CacheException {
// no-op, method overriden
}
@Override
public String toString() {
return new StringBuilder().append(getClass().getSimpleName()).append('{')
.append("cache=").append(cacheName)
.append(", strategy=").append(evictionStrategy)
.append(", wakeUpInterval=").append(evictionWakeUpInterval)
.append(", maxEntries=").append(evictionMaxEntries)
.append(", lifespan=").append(expirationLifespan)
.append(", maxIdle=").append(expirationMaxIdle)
.append('}').toString();
}
@Override
public String toString() {
return getClass().getSimpleName()
+ '{' + "cache=" + cacheName
+ ", strategy=" + evictionStrategy
+ ", wakeUpInterval=" + evictionWakeUpInterval
+ ", maxEntries=" + evictionMaxEntries
+ ", lifespan=" + expirationLifespan
+ ", maxIdle=" + expirationMaxIdle
+ '}';
}
private String uc(String s) {
return s == null ? null : s.toUpperCase(Locale.ENGLISH);
}
private String uc(String s) {
return s == null ? null : s.toUpperCase( Locale.ENGLISH );
}
private void markAsOverriden(String fieldName) {
overridden.add(fieldName);
}
private void markAsOverriden(String fieldName) {
overridden.add( fieldName );
}
}

View File

@ -23,6 +23,9 @@
*/
package org.hibernate.cache.infinispan.access;
import javax.transaction.SystemException;
import javax.transaction.Transaction;
import javax.transaction.TransactionManager;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
@ -32,47 +35,45 @@ import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import javax.transaction.SystemException;
import javax.transaction.Transaction;
import javax.transaction.TransactionManager;
import org.infinispan.AdvancedCache;
import org.infinispan.manager.EmbeddedCacheManager;
import org.hibernate.cache.CacheException;
import org.hibernate.cache.infinispan.InfinispanRegionFactory;
import org.infinispan.AdvancedCache;
import org.infinispan.manager.EmbeddedCacheManager;
/**
* Encapsulates logic to allow a {@link TransactionalAccessDelegate} to determine
* whether a {@link TransactionalAccessDelegate#putFromLoad(Object, Object, long, Object, boolean)}
* call should be allowed to update the cache. A <code>putFromLoad</code> has
* the potential to store stale data, since the data may have been removed from the
* database and the cache between the time when the data was read from the database
* database and the cache between the time when the data was read from the database
* and the actual call to <code>putFromLoad</code>.
* <p>
* The expected usage of this class by a thread that read the cache and did
* not find data is:
*
* <p/>
* <ol>
* <li> Call {@link #registerPendingPut(Object)}</li>
* <li> Read the database</li>
* <li> Call {@link #acquirePutFromLoadLock(Object)}
* <li> if above returns <code>false</code>, the thread should not cache the data;
* only if above returns <code>true</code>, put data in the cache and...</li>
* only if above returns <code>true</code>, put data in the cache and...</li>
* <li> then call {@link #releasePutFromLoadLock(Object)}</li>
* </ol>
* </p>
*
* <p/>
* <p>
* The expected usage by a thread that is taking an action such that any pending
* <code>putFromLoad</code> may have stale data and should not cache it is to either
* call
*
* <p/>
* <ul>
* <li> {@link #invalidateKey(Object)} (for a single key invalidation)</li>
* <li>or {@link #invalidateRegion()} (for a general invalidation all pending puts)</li>
* </ul>
* </p>
*
* <p/>
* <p>
* This class also supports the concept of "naked puts", which are calls to
* {@link #acquirePutFromLoadLock(Object)} without a preceding {@link #registerPendingPut(Object)}
@ -80,454 +81,477 @@ import org.infinispan.manager.EmbeddedCacheManager;
* </p>
*
* @author Brian Stansberry
*
* @version $Revision: $
*/
public class PutFromLoadValidator {
/**
* Period (in ms) after a removal during which a call to
* {@link #acquirePutFromLoadLock(Object)} that hasn't been
* {@link #registerPendingPut(Object) pre-registered} (aka a "naked put")
* will return false.
* will return false.
*/
public static final long NAKED_PUT_INVALIDATION_PERIOD = TimeUnit.SECONDS.toMillis(20);
/**
* Period (in ms) after a removal during which a call to
* {@link #acquirePutFromLoadLock(Object)} that hasn't been
* {@link #registerPendingPut(Object) pre-registered} (aka a "naked put")
* will return false.
* will return false.
*/
public static final long NAKED_PUT_INVALIDATION_PERIOD = TimeUnit.SECONDS.toMillis( 20 );
/**
* Used to determine whether the owner of a pending put is a thread or a transaction
*/
private final TransactionManager transactionManager;
/**
* Used to determine whether the owner of a pending put is a thread or a transaction
*/
private final TransactionManager transactionManager;
private final long nakedPutInvalidationPeriod;
private final long nakedPutInvalidationPeriod;
/**
* Registry of expected, future, isPutValid calls. If a key+owner is registered in this map, it
* is not a "naked put" and is allowed to proceed.
*/
private final ConcurrentMap<Object, PendingPutMap> pendingPuts;
/**
* Registry of expected, future, isPutValid calls. If a key+owner is registered in this map, it
* is not a "naked put" and is allowed to proceed.
*/
private final ConcurrentMap<Object, PendingPutMap> pendingPuts;
private final ConcurrentMap<Object, Long> recentRemovals = new ConcurrentHashMap<Object, Long>();
/**
* List of recent removals. Used to ensure we don't leak memory via the recentRemovals map
*/
private final List<RecentRemoval> removalsQueue = new LinkedList<RecentRemoval>();
/**
* The time when the first element in removalsQueue will expire. No reason to do housekeeping on
* the queue before this time.
*/
private volatile long earliestRemovalTimestamp;
/** Lock controlling access to removalsQueue */
private final Lock removalsLock = new ReentrantLock();
private final ConcurrentMap<Object, Long> recentRemovals = new ConcurrentHashMap<Object, Long>();
/**
* List of recent removals. Used to ensure we don't leak memory via the recentRemovals map
*/
private final List<RecentRemoval> removalsQueue = new LinkedList<RecentRemoval>();
/**
* The time when the first element in removalsQueue will expire. No reason to do housekeeping on
* the queue before this time.
*/
private volatile long earliestRemovalTimestamp;
/**
* Lock controlling access to removalsQueue
*/
private final Lock removalsLock = new ReentrantLock();
/**
* The time of the last call to regionRemoved(), plus NAKED_PUT_INVALIDATION_PERIOD. All naked
* puts will be rejected until the current time is greater than this value.
*/
private volatile long invalidationTimestamp;
/**
* The time of the last call to regionRemoved(), plus NAKED_PUT_INVALIDATION_PERIOD. All naked
* puts will be rejected until the current time is greater than this value.
*/
private volatile long invalidationTimestamp;
/**
* Creates a new PutFromLoadValidator.
*/
public PutFromLoadValidator(AdvancedCache cache) {
this(cache, NAKED_PUT_INVALIDATION_PERIOD);
}
/**
* Constructor variant for use by unit tests; allows control of various timeouts by the test.
*/
public PutFromLoadValidator(AdvancedCache cache,
long nakedPutInvalidationPeriod) {
this(cache.getCacheManager(), cache.getTransactionManager(),
nakedPutInvalidationPeriod);
}
public PutFromLoadValidator(EmbeddedCacheManager cacheManager,
TransactionManager tm, long nakedPutInvalidationPeriod) {
this.pendingPuts = cacheManager
.getCache(InfinispanRegionFactory.PENDING_PUTS_CACHE_NAME);
this.transactionManager = tm;
this.nakedPutInvalidationPeriod = nakedPutInvalidationPeriod;
}
// ----------------------------------------------------------------- Public
/**
* Acquire a lock giving the calling thread the right to put data in the
* cache for the given key.
* <p>
* <strong>NOTE:</strong> A call to this method that returns <code>true</code>
* should always be matched with a call to {@link #releasePutFromLoadLock(Object)}.
* </p>
*
* @param key the key
*
* @return <code>true</code> if the lock is acquired and the cache put
* can proceed; <code>false</code> if the data should not be cached
*/
public boolean acquirePutFromLoadLock(Object key) {
boolean valid = false;
boolean locked = false;
long now = System.currentTimeMillis();
try {
PendingPutMap pending = pendingPuts.get(key);
if (pending != null) {
locked = pending.acquireLock(100, TimeUnit.MILLISECONDS);
if (locked) {
try {
PendingPut toCancel = pending.remove(getOwnerForPut());
if (toCancel != null) {
valid = !toCancel.completed;
toCancel.completed = true;
}
}
finally {
if (!valid) {
pending.releaseLock();
locked = false;
}
}
}
}
else {
// Key wasn't in pendingPuts, so either this is a "naked put"
// or regionRemoved has been called. Check if we can proceed
if (now > invalidationTimestamp) {
Long removedTime = recentRemovals.get(key);
if (removedTime == null || now > removedTime ) {
// It's legal to proceed. But we have to record this key
// in pendingPuts so releasePutFromLoadLock can find it.
// To do this we basically simulate a normal "register
// then acquire lock" pattern
registerPendingPut(key);
locked = acquirePutFromLoadLock(key);
valid = locked;
}
}
}
}
catch (Throwable t) {
if (locked) {
PendingPutMap toRelease = pendingPuts.get(key);
if (toRelease != null) {
toRelease.releaseLock();
}
}
if (t instanceof RuntimeException) {
throw (RuntimeException) t;
} else if (t instanceof Error) {
throw (Error) t;
} else {
throw new RuntimeException(t);
}
}
return valid;
}
/**
* Releases the lock previously obtained by a call to
* {@link #acquirePutFromLoadLock(Object)} that returned <code>true</code>.
*
* @param key the key
*/
public void releasePutFromLoadLock(Object key) {
PendingPutMap pending = pendingPuts.get(key);
if (pending != null) {
if (pending.size() == 0) {
pendingPuts.remove(key, pending);
}
pending.releaseLock();
}
}
/**
* Invalidates any {@link #registerPendingPut(Object) previously registered pending puts} ensuring a subsequent call to
* {@link #acquirePutFromLoadLock(Object)} will return <code>false</code>. <p> This method will block until any
* concurrent thread that has {@link #acquirePutFromLoadLock(Object) acquired the putFromLoad lock} for the given key
* has released the lock. This allows the caller to be certain the putFromLoad will not execute after this method
* returns, possibly caching stale data. </p>
*
* @param key key identifying data whose pending puts should be invalidated
* @return <code>true</code> if the invalidation was successful; <code>false</code> if a problem occured (which the
* caller should treat as an exception condition)
*/
public boolean invalidateKey(Object key) {
boolean success = true;
// Invalidate any pending puts
PendingPutMap pending = pendingPuts.get(key);
if (pending != null) {
// This lock should be available very quickly, but we'll be
// very patient waiting for it as callers should treat not
// acquiring it as an exception condition
if (pending.acquireLock(60, TimeUnit.SECONDS)) {
try {
pending.invalidate();
}
finally {
pending.releaseLock();
}
} else {
success = false;
}
}
// Record when this occurred to invalidate later naked puts
RecentRemoval removal = new RecentRemoval(key, this.nakedPutInvalidationPeriod);
recentRemovals.put(key, removal.timestamp);
// Don't let recentRemovals map become a memory leak
RecentRemoval toClean = null;
boolean attemptClean = removal.timestamp > earliestRemovalTimestamp;
removalsLock.lock();
try {
removalsQueue.add(removal);
if (attemptClean) {
if (removalsQueue.size() > 1) { // we have at least one as we
// just added it
toClean = removalsQueue.remove(0);
}
earliestRemovalTimestamp = removalsQueue.get( 0 ).timestamp;
}
} finally {
removalsLock.unlock();
}
if (toClean != null) {
Long cleaned = recentRemovals.get(toClean.key);
if (cleaned != null && cleaned.equals(toClean.timestamp)) {
cleaned = recentRemovals.remove(toClean.key);
if (cleaned != null && !cleaned.equals(toClean.timestamp)) {
// Oops; removed the wrong timestamp; restore it
recentRemovals.putIfAbsent(toClean.key, cleaned);
}
}
}
return success;
}
/**
* Invalidates all {@link #registerPendingPut(Object) previously registered pending puts} ensuring a subsequent call to
* {@link #acquirePutFromLoadLock(Object)} will return <code>false</code>. <p> This method will block until any
* concurrent thread that has {@link #acquirePutFromLoadLock(Object) acquired the putFromLoad lock} for the any key has
* released the lock. This allows the caller to be certain the putFromLoad will not execute after this method returns,
* possibly caching stale data. </p>
*
* @return <code>true</code> if the invalidation was successful; <code>false</code> if a problem occured (which the
* caller should treat as an exception condition)
*/
public boolean invalidateRegion() {
boolean ok = false;
invalidationTimestamp = System.currentTimeMillis() + this.nakedPutInvalidationPeriod;
try {
// Acquire the lock for each entry to ensure any ongoing
// work associated with it is completed before we return
for (PendingPutMap entry : pendingPuts.values()) {
if (entry.acquireLock(60, TimeUnit.SECONDS)) {
try {
entry.invalidate();
}
finally {
entry.releaseLock();
}
} else {
ok = false;
}
}
removalsLock.lock();
try {
recentRemovals.clear();
removalsQueue.clear();
ok = true;
} finally {
removalsLock.unlock();
}
}
catch (Exception e) {
ok = false;
}
finally {
earliestRemovalTimestamp = invalidationTimestamp;
}
return ok;
/**
* Creates a new PutFromLoadValidator.
*/
public PutFromLoadValidator(AdvancedCache cache) {
this( cache, NAKED_PUT_INVALIDATION_PERIOD );
}
/**
* Notifies this validator that it is expected that a database read followed by a subsequent {@link
* #acquirePutFromLoadLock(Object)} call will occur. The intent is this method would be called following a cache miss
* wherein it is expected that a database read plus cache put will occur. Calling this method allows the validator to
* treat the subsequent <code>acquirePutFromLoadLock</code> as if the database read occurred when this method was
* invoked. This allows the validator to compare the timestamp of this call against the timestamp of subsequent removal
* notifications. A put that occurs without this call preceding it is "naked"; i.e the validator must assume the put is
* not valid if any relevant removal has occurred within {@link #NAKED_PUT_INVALIDATION_PERIOD} milliseconds.
*
* @param key key that will be used for subsequent cache put
*/
public void registerPendingPut(Object key) {
PendingPut pendingPut = new PendingPut(getOwnerForPut());
PendingPutMap pendingForKey = new PendingPutMap(pendingPut);
/**
* Constructor variant for use by unit tests; allows control of various timeouts by the test.
*/
public PutFromLoadValidator(
AdvancedCache cache,
long nakedPutInvalidationPeriod) {
this(
cache.getCacheManager(), cache.getTransactionManager(),
nakedPutInvalidationPeriod
);
}
for (;;) {
PendingPutMap existing = pendingPuts.putIfAbsent(key, pendingForKey);
if (existing != null) {
if (existing.acquireLock(10, TimeUnit.SECONDS)) {
public PutFromLoadValidator(
EmbeddedCacheManager cacheManager,
TransactionManager tm, long nakedPutInvalidationPeriod) {
this.pendingPuts = cacheManager
.getCache( InfinispanRegionFactory.PENDING_PUTS_CACHE_NAME );
this.transactionManager = tm;
this.nakedPutInvalidationPeriod = nakedPutInvalidationPeriod;
}
try {
existing.put(pendingPut);
PendingPutMap doublecheck = pendingPuts.putIfAbsent(key, existing);
if (doublecheck == null || doublecheck == existing) {
break;
}
// else we hit a race and need to loop to try again
}
finally {
existing.releaseLock();
}
} else {
// Can't get the lock; when we come back we'll be a "naked put"
break;
}
} else {
// normal case
break;
}
}
}
// ----------------------------------------------------------------- Public
// -------------------------------------------------------------- Protected
/**
* Acquire a lock giving the calling thread the right to put data in the
* cache for the given key.
* <p>
* <strong>NOTE:</strong> A call to this method that returns <code>true</code>
* should always be matched with a call to {@link #releasePutFromLoadLock(Object)}.
* </p>
*
* @param key the key
*
* @return <code>true</code> if the lock is acquired and the cache put
* can proceed; <code>false</code> if the data should not be cached
*/
public boolean acquirePutFromLoadLock(Object key) {
boolean valid = false;
boolean locked = false;
final long now = System.currentTimeMillis();
/** Only for use by unit tests; may be removed at any time */
protected int getRemovalQueueLength() {
removalsLock.lock();
try {
return removalsQueue.size();
} finally {
removalsLock.unlock();
}
}
try {
final PendingPutMap pending = pendingPuts.get( key );
if ( pending != null ) {
locked = pending.acquireLock( 100, TimeUnit.MILLISECONDS );
if ( locked ) {
try {
final PendingPut toCancel = pending.remove( getOwnerForPut() );
if ( toCancel != null ) {
valid = !toCancel.completed;
toCancel.completed = true;
}
}
finally {
if ( !valid ) {
pending.releaseLock();
locked = false;
}
}
}
}
else {
// Key wasn't in pendingPuts, so either this is a "naked put"
// or regionRemoved has been called. Check if we can proceed
if ( now > invalidationTimestamp ) {
final Long removedTime = recentRemovals.get( key );
if ( removedTime == null || now > removedTime ) {
// It's legal to proceed. But we have to record this key
// in pendingPuts so releasePutFromLoadLock can find it.
// To do this we basically simulate a normal "register
// then acquire lock" pattern
registerPendingPut( key );
locked = acquirePutFromLoadLock( key );
valid = locked;
}
}
}
}
catch (Throwable t) {
if ( locked ) {
final PendingPutMap toRelease = pendingPuts.get( key );
if ( toRelease != null ) {
toRelease.releaseLock();
}
}
// ---------------------------------------------------------------- Private
if ( t instanceof RuntimeException ) {
throw (RuntimeException) t;
}
else if ( t instanceof Error ) {
throw (Error) t;
}
else {
throw new RuntimeException( t );
}
}
private Object getOwnerForPut() {
Transaction tx = null;
try {
if (transactionManager != null) {
tx = transactionManager.getTransaction();
}
} catch (SystemException se) {
throw new CacheException("Could not obtain transaction", se);
}
return tx == null ? Thread.currentThread() : tx;
return valid;
}
}
/**
* Releases the lock previously obtained by a call to
* {@link #acquirePutFromLoadLock(Object)} that returned <code>true</code>.
*
* @param key the key
*/
public void releasePutFromLoadLock(Object key) {
final PendingPutMap pending = pendingPuts.get( key );
if ( pending != null ) {
if ( pending.size() == 0 ) {
pendingPuts.remove( key, pending );
}
pending.releaseLock();
}
}
/**
* Lazy-initialization map for PendingPut. Optimized for the expected usual case where only a
* single put is pending for a given key.
*
* This class is NOT THREAD SAFE. All operations on it must be performed with the lock held.
*/
private static class PendingPutMap {
private PendingPut singlePendingPut;
private Map<Object, PendingPut> fullMap;
private final Lock lock = new ReentrantLock();
/**
* Invalidates any {@link #registerPendingPut(Object) previously registered pending puts} ensuring a subsequent call to
* {@link #acquirePutFromLoadLock(Object)} will return <code>false</code>. <p> This method will block until any
* concurrent thread that has {@link #acquirePutFromLoadLock(Object) acquired the putFromLoad lock} for the given key
* has released the lock. This allows the caller to be certain the putFromLoad will not execute after this method
* returns, possibly caching stale data. </p>
*
* @param key key identifying data whose pending puts should be invalidated
*
* @return <code>true</code> if the invalidation was successful; <code>false</code> if a problem occured (which the
* caller should treat as an exception condition)
*/
public boolean invalidateKey(Object key) {
boolean success = true;
PendingPutMap(PendingPut singleItem) {
this.singlePendingPut = singleItem;
}
// Invalidate any pending puts
final PendingPutMap pending = pendingPuts.get( key );
if ( pending != null ) {
// This lock should be available very quickly, but we'll be
// very patient waiting for it as callers should treat not
// acquiring it as an exception condition
if ( pending.acquireLock( 60, TimeUnit.SECONDS ) ) {
try {
pending.invalidate();
}
finally {
pending.releaseLock();
}
}
else {
success = false;
}
}
public void put(PendingPut pendingPut) {
if (singlePendingPut == null) {
if (fullMap == null) {
// initial put
singlePendingPut = pendingPut;
} else {
fullMap.put(pendingPut.owner, pendingPut);
}
} else {
// 2nd put; need a map
fullMap = new HashMap<Object, PendingPut>(4);
fullMap.put(singlePendingPut.owner, singlePendingPut);
singlePendingPut = null;
fullMap.put(pendingPut.owner, pendingPut);
}
}
// Record when this occurred to invalidate later naked puts
final RecentRemoval removal = new RecentRemoval( key, this.nakedPutInvalidationPeriod );
recentRemovals.put( key, removal.timestamp );
public PendingPut remove(Object ownerForPut) {
PendingPut removed = null;
if (fullMap == null) {
if (singlePendingPut != null
&& singlePendingPut.owner.equals(ownerForPut)) {
removed = singlePendingPut;
singlePendingPut = null;
}
} else {
removed = fullMap.remove(ownerForPut);
}
return removed;
}
// Don't let recentRemovals map become a memory leak
RecentRemoval toClean = null;
final boolean attemptClean = removal.timestamp > earliestRemovalTimestamp;
removalsLock.lock();
try {
removalsQueue.add( removal );
public int size() {
return fullMap == null ? (singlePendingPut == null ? 0 : 1)
: fullMap.size();
}
if ( attemptClean ) {
if ( removalsQueue.size() > 1 ) {
// we have at least one as we just added it
toClean = removalsQueue.remove( 0 );
}
earliestRemovalTimestamp = removalsQueue.get( 0 ).timestamp;
}
}
finally {
removalsLock.unlock();
}
public boolean acquireLock(long time, TimeUnit unit) {
try {
return lock.tryLock(time, unit);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
return false;
}
}
if ( toClean != null ) {
Long cleaned = recentRemovals.get( toClean.key );
if ( cleaned != null && cleaned.equals( toClean.timestamp ) ) {
cleaned = recentRemovals.remove( toClean.key );
if ( cleaned != null && !cleaned.equals( toClean.timestamp ) ) {
// Oops; removed the wrong timestamp; restore it
recentRemovals.putIfAbsent( toClean.key, cleaned );
}
}
}
public void releaseLock() {
lock.unlock();
}
return success;
}
public void invalidate() {
if (singlePendingPut != null) {
singlePendingPut.completed = true;
// Nullify to avoid leaking completed pending puts
singlePendingPut = null;
} else if (fullMap != null) {
for (PendingPut pp : fullMap.values()) {
pp.completed = true;
}
// Nullify to avoid leaking completed pending puts
fullMap = null;
}
}
}
/**
* Invalidates all {@link #registerPendingPut(Object) previously registered pending puts} ensuring a subsequent call to
* {@link #acquirePutFromLoadLock(Object)} will return <code>false</code>. <p> This method will block until any
* concurrent thread that has {@link #acquirePutFromLoadLock(Object) acquired the putFromLoad lock} for the any key has
* released the lock. This allows the caller to be certain the putFromLoad will not execute after this method returns,
* possibly caching stale data. </p>
*
* @return <code>true</code> if the invalidation was successful; <code>false</code> if a problem occured (which the
* caller should treat as an exception condition)
*/
public boolean invalidateRegion() {
private static class PendingPut {
private final Object owner;
private volatile boolean completed;
boolean ok = false;
invalidationTimestamp = System.currentTimeMillis() + this.nakedPutInvalidationPeriod;
private PendingPut(Object owner) {
this.owner = owner;
}
}
try {
private static class RecentRemoval {
private final Object key;
private final Long timestamp;
// Acquire the lock for each entry to ensure any ongoing
// work associated with it is completed before we return
for ( PendingPutMap entry : pendingPuts.values() ) {
if ( entry.acquireLock( 60, TimeUnit.SECONDS ) ) {
try {
entry.invalidate();
}
finally {
entry.releaseLock();
}
}
else {
ok = false;
}
}
private RecentRemoval(Object key, long nakedPutInvalidationPeriod) {
this.key = key;
timestamp = System.currentTimeMillis() + nakedPutInvalidationPeriod;
}
}
removalsLock.lock();
try {
recentRemovals.clear();
removalsQueue.clear();
ok = true;
}
finally {
removalsLock.unlock();
}
}
catch (Exception e) {
ok = false;
}
finally {
earliestRemovalTimestamp = invalidationTimestamp;
}
return ok;
}
/**
* Notifies this validator that it is expected that a database read followed by a subsequent {@link
* #acquirePutFromLoadLock(Object)} call will occur. The intent is this method would be called following a cache miss
* wherein it is expected that a database read plus cache put will occur. Calling this method allows the validator to
* treat the subsequent <code>acquirePutFromLoadLock</code> as if the database read occurred when this method was
* invoked. This allows the validator to compare the timestamp of this call against the timestamp of subsequent removal
* notifications. A put that occurs without this call preceding it is "naked"; i.e the validator must assume the put is
* not valid if any relevant removal has occurred within {@link #NAKED_PUT_INVALIDATION_PERIOD} milliseconds.
*
* @param key key that will be used for subsequent cache put
*/
public void registerPendingPut(Object key) {
final PendingPut pendingPut = new PendingPut( getOwnerForPut() );
final PendingPutMap pendingForKey = new PendingPutMap( pendingPut );
for (; ; ) {
final PendingPutMap existing = pendingPuts.putIfAbsent( key, pendingForKey );
if ( existing != null ) {
if ( existing.acquireLock( 10, TimeUnit.SECONDS ) ) {
try {
existing.put( pendingPut );
final PendingPutMap doublecheck = pendingPuts.putIfAbsent( key, existing );
if ( doublecheck == null || doublecheck == existing ) {
break;
}
// else we hit a race and need to loop to try again
}
finally {
existing.releaseLock();
}
}
else {
// Can't get the lock; when we come back we'll be a "naked put"
break;
}
}
else {
// normal case
break;
}
}
}
// -------------------------------------------------------------- Protected
/**
* Only for use by unit tests; may be removed at any time
*/
protected int getRemovalQueueLength() {
removalsLock.lock();
try {
return removalsQueue.size();
}
finally {
removalsLock.unlock();
}
}
// ---------------------------------------------------------------- Private
private Object getOwnerForPut() {
Transaction tx = null;
try {
if ( transactionManager != null ) {
tx = transactionManager.getTransaction();
}
}
catch (SystemException se) {
throw new CacheException( "Could not obtain transaction", se );
}
return tx == null ? Thread.currentThread() : tx;
}
/**
* Lazy-initialization map for PendingPut. Optimized for the expected usual case where only a
* single put is pending for a given key.
* <p/>
* This class is NOT THREAD SAFE. All operations on it must be performed with the lock held.
*/
private static class PendingPutMap {
private PendingPut singlePendingPut;
private Map<Object, PendingPut> fullMap;
private final Lock lock = new ReentrantLock();
PendingPutMap(PendingPut singleItem) {
this.singlePendingPut = singleItem;
}
public void put(PendingPut pendingPut) {
if ( singlePendingPut == null ) {
if ( fullMap == null ) {
// initial put
singlePendingPut = pendingPut;
}
else {
fullMap.put( pendingPut.owner, pendingPut );
}
}
else {
// 2nd put; need a map
fullMap = new HashMap<Object, PendingPut>( 4 );
fullMap.put( singlePendingPut.owner, singlePendingPut );
singlePendingPut = null;
fullMap.put( pendingPut.owner, pendingPut );
}
}
public PendingPut remove(Object ownerForPut) {
PendingPut removed = null;
if ( fullMap == null ) {
if ( singlePendingPut != null
&& singlePendingPut.owner.equals( ownerForPut ) ) {
removed = singlePendingPut;
singlePendingPut = null;
}
}
else {
removed = fullMap.remove( ownerForPut );
}
return removed;
}
public int size() {
return fullMap == null ? (singlePendingPut == null ? 0 : 1)
: fullMap.size();
}
public boolean acquireLock(long time, TimeUnit unit) {
try {
return lock.tryLock( time, unit );
}
catch (InterruptedException e) {
Thread.currentThread().interrupt();
return false;
}
}
public void releaseLock() {
lock.unlock();
}
public void invalidate() {
if ( singlePendingPut != null ) {
singlePendingPut.completed = true;
// Nullify to avoid leaking completed pending puts
singlePendingPut = null;
}
else if ( fullMap != null ) {
for ( PendingPut pp : fullMap.values() ) {
pp.completed = true;
}
// Nullify to avoid leaking completed pending puts
fullMap = null;
}
}
}
private static class PendingPut {
private final Object owner;
private volatile boolean completed;
private PendingPut(Object owner) {
this.owner = owner;
}
}
private static class RecentRemoval {
private final Object key;
private final Long timestamp;
private RecentRemoval(Object key, long nakedPutInvalidationPeriod) {
this.key = key;
timestamp = System.currentTimeMillis() + nakedPutInvalidationPeriod;
}
}
}

View File

@ -25,158 +25,172 @@ package org.hibernate.cache.infinispan.access;
import javax.transaction.Transaction;
import org.hibernate.cache.infinispan.util.Caches;
import org.infinispan.AdvancedCache;
import org.infinispan.util.logging.Log;
import org.infinispan.util.logging.LogFactory;
import org.hibernate.cache.CacheException;
import org.hibernate.cache.infinispan.impl.BaseRegion;
import org.hibernate.cache.spi.access.CollectionRegionAccessStrategy;
import org.hibernate.cache.spi.access.EntityRegionAccessStrategy;
import org.hibernate.cache.infinispan.util.Caches;
import org.hibernate.cache.spi.access.SoftLock;
/**
* Defines the strategy for transactional access to entity or collection data in a Infinispan instance.
* <p>
* <p/>
* The intent of this class is to encapsulate common code and serve as a delegate for
* {@link EntityRegionAccessStrategy} and {@link CollectionRegionAccessStrategy} implementations.
*
* {@link org.hibernate.cache.spi.access.EntityRegionAccessStrategy}
* and {@link org.hibernate.cache.spi.access.CollectionRegionAccessStrategy} implementations.
*
* @author Brian Stansberry
* @author Galder Zamarreño
* @since 3.5
*/
public class TransactionalAccessDelegate {
private static final Log log = LogFactory.getLog(TransactionalAccessDelegate.class);
private static final boolean isTrace = log.isTraceEnabled();
private final AdvancedCache cache;
private final BaseRegion region;
private final PutFromLoadValidator putValidator;
private final AdvancedCache<Object, Object> writeCache;
private static final Log log = LogFactory.getLog( TransactionalAccessDelegate.class );
private static final boolean TRACE_ENABLED = log.isTraceEnabled();
private final AdvancedCache cache;
private final BaseRegion region;
private final PutFromLoadValidator putValidator;
private final AdvancedCache<Object, Object> writeCache;
public TransactionalAccessDelegate(BaseRegion region, PutFromLoadValidator validator) {
this.region = region;
this.cache = region.getCache();
this.putValidator = validator;
this.writeCache = Caches.ignoreReturnValuesCache(cache);
}
public TransactionalAccessDelegate(BaseRegion region, PutFromLoadValidator validator) {
this.region = region;
this.cache = region.getCache();
this.putValidator = validator;
this.writeCache = Caches.ignoreReturnValuesCache( cache );
}
public Object get(Object key, long txTimestamp) throws CacheException {
if (!region.checkValid())
return null;
Object val = cache.get(key);
if (val == null)
putValidator.registerPendingPut(key);
return val;
}
public Object get(Object key, long txTimestamp) throws CacheException {
if ( !region.checkValid() ) {
return null;
}
Object val = cache.get( key );
if ( val == null ) {
putValidator.registerPendingPut( key );
}
return val;
}
public boolean putFromLoad(Object key, Object value, long txTimestamp, Object version) {
return putFromLoad(key, value, txTimestamp, version, false);
}
public boolean putFromLoad(Object key, Object value, long txTimestamp, Object version) {
return putFromLoad( key, value, txTimestamp, version, false );
}
public boolean putFromLoad(Object key, Object value, long txTimestamp, Object version, boolean minimalPutOverride)
throws CacheException {
if (!region.checkValid()) {
if (isTrace) log.tracef("Region %s not valid", region.getName());
return false;
}
public boolean putFromLoad(Object key, Object value, long txTimestamp, Object version, boolean minimalPutOverride)
throws CacheException {
if ( !region.checkValid() ) {
if ( TRACE_ENABLED ) {
log.tracef( "Region %s not valid", region.getName() );
}
return false;
}
// In theory, since putForExternalRead is already as minimal as it can
// get, we shouldn't be need this check. However, without the check and
// without https://issues.jboss.org/browse/ISPN-1986, it's impossible to
// know whether the put actually occurred. Knowing this is crucial so
// that Hibernate can expose accurate statistics.
if (minimalPutOverride && cache.containsKey(key))
return false;
// In theory, since putForExternalRead is already as minimal as it can
// get, we shouldn't be need this check. However, without the check and
// without https://issues.jboss.org/browse/ISPN-1986, it's impossible to
// know whether the put actually occurred. Knowing this is crucial so
// that Hibernate can expose accurate statistics.
if ( minimalPutOverride && cache.containsKey( key ) ) {
return false;
}
if (!putValidator.acquirePutFromLoadLock(key)) {
if (isTrace) log.tracef("Put from load lock not acquired for key %s", key);
return false;
}
if ( !putValidator.acquirePutFromLoadLock( key ) ) {
if ( TRACE_ENABLED ) {
log.tracef( "Put from load lock not acquired for key %s", key );
}
return false;
}
try {
writeCache.putForExternalRead(key, value);
} finally {
putValidator.releasePutFromLoadLock(key);
}
try {
writeCache.putForExternalRead( key, value );
}
finally {
putValidator.releasePutFromLoadLock( key );
}
return true;
}
return true;
}
public SoftLock lockItem(Object key, Object version) throws CacheException {
return null;
}
public SoftLock lockItem(Object key, Object version) throws CacheException {
return null;
}
public SoftLock lockRegion() throws CacheException {
return null;
}
public SoftLock lockRegion() throws CacheException {
return null;
}
public void unlockItem(Object key, SoftLock lock) throws CacheException {
}
public void unlockItem(Object key, SoftLock lock) throws CacheException {
}
public void unlockRegion(SoftLock lock) throws CacheException {
}
public void unlockRegion(SoftLock lock) throws CacheException {
}
public boolean insert(Object key, Object value, Object version) throws CacheException {
if (!region.checkValid())
return false;
public boolean insert(Object key, Object value, Object version) throws CacheException {
if ( !region.checkValid() ) {
return false;
}
writeCache.put(key, value);
return true;
}
writeCache.put( key, value );
return true;
}
public boolean afterInsert(Object key, Object value, Object version) throws CacheException {
return false;
}
public boolean afterInsert(Object key, Object value, Object version) throws CacheException {
return false;
}
public boolean update(Object key, Object value, Object currentVersion, Object previousVersion) throws CacheException {
// 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.
writeCache.put(key, value);
return true;
}
public boolean update(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.
writeCache.put( key, value );
return true;
}
public boolean afterUpdate(Object key, Object value, Object currentVersion, Object previousVersion, SoftLock lock)
throws CacheException {
return false;
}
public boolean afterUpdate(Object key, Object value, Object currentVersion, Object previousVersion, SoftLock lock)
throws CacheException {
return false;
}
public void remove(Object key) throws CacheException {
if (!putValidator.invalidateKey(key)) {
throw new CacheException("Failed to invalidate pending putFromLoad calls for key " + key + " from region " + region.getName());
}
// 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.
writeCache.remove(key);
}
public void remove(Object key) throws CacheException {
if ( !putValidator.invalidateKey( key ) ) {
throw new CacheException(
"Failed to invalidate pending putFromLoad calls for key " + key + " from region " + region.getName()
);
}
// 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.
writeCache.remove( key );
}
public void removeAll() throws CacheException {
if (!putValidator.invalidateRegion()) {
throw new CacheException("Failed to invalidate pending putFromLoad calls for region " + region.getName());
}
cache.clear();
}
public void removeAll() throws CacheException {
if ( !putValidator.invalidateRegion() ) {
throw new CacheException( "Failed to invalidate pending putFromLoad calls for region " + region.getName() );
}
cache.clear();
}
public void evict(Object key) throws CacheException {
if (!putValidator.invalidateKey(key)) {
throw new CacheException("Failed to invalidate pending putFromLoad calls for key " + key + " from region " + region.getName());
}
writeCache.remove(key);
}
public void evict(Object key) throws CacheException {
if ( !putValidator.invalidateKey( key ) ) {
throw new CacheException(
"Failed to invalidate pending putFromLoad calls for key " + key + " from region " + region.getName()
);
}
writeCache.remove( key );
}
public void evictAll() throws CacheException {
if (!putValidator.invalidateRegion()) {
throw new CacheException("Failed to invalidate pending putFromLoad calls for region " + region.getName());
}
Transaction tx = region.suspend();
try {
region.invalidateRegion(); // Invalidate the local region and then go remote
Caches.broadcastEvictAll(cache);
} finally {
region.resume(tx);
}
}
public void evictAll() throws CacheException {
if ( !putValidator.invalidateRegion() ) {
throw new CacheException( "Failed to invalidate pending putFromLoad calls for region " + region.getName() );
}
final Transaction tx = region.suspend();
try {
region.invalidateRegion(); // Invalidate the local region and then go remote
Caches.broadcastEvictAll( cache );
}
finally {
region.resume( tx );
}
}
}

View File

@ -0,0 +1,4 @@
/**
* Internal Infinispan-based implementation of the cache region access strategies
*/
package org.hibernate.cache.infinispan.access;

View File

@ -1,5 +1,30 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2013, Red Hat Inc. or third-party contributors as
* indicated by the @author tags or express copyright attribution
* statements applied by the authors. All third-party contributions are
* distributed under license by Red Hat Inc.
*
* This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU
* Lesser General Public License, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this distribution; if not, write to:
* Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*/
package org.hibernate.cache.infinispan.collection;
import org.infinispan.AdvancedCache;
import org.hibernate.cache.CacheException;
import org.hibernate.cache.infinispan.access.PutFromLoadValidator;
import org.hibernate.cache.infinispan.impl.BaseTransactionalDataRegion;
@ -8,7 +33,6 @@ import org.hibernate.cache.spi.CollectionRegion;
import org.hibernate.cache.spi.RegionFactory;
import org.hibernate.cache.spi.access.AccessType;
import org.hibernate.cache.spi.access.CollectionRegionAccessStrategy;
import org.infinispan.AdvancedCache;
/**
* @author Chris Bredesen
@ -17,21 +41,23 @@ import org.infinispan.AdvancedCache;
*/
public class CollectionRegionImpl extends BaseTransactionalDataRegion implements CollectionRegion {
public CollectionRegionImpl(AdvancedCache cache, String name,
CacheDataDescription metadata, RegionFactory factory) {
super(cache, name, metadata, factory);
}
public CollectionRegionImpl(
AdvancedCache cache, String name,
CacheDataDescription metadata, RegionFactory factory) {
super( cache, name, metadata, factory );
}
public CollectionRegionAccessStrategy buildAccessStrategy(AccessType accessType) throws CacheException {
if (AccessType.READ_ONLY.equals(accessType)
|| AccessType.TRANSACTIONAL.equals(accessType))
return new TransactionalAccess(this);
public CollectionRegionAccessStrategy buildAccessStrategy(AccessType accessType) throws CacheException {
if ( AccessType.READ_ONLY.equals( accessType )
|| AccessType.TRANSACTIONAL.equals( accessType ) ) {
return new TransactionalAccess( this );
}
throw new CacheException("Unsupported access type [" + accessType.getExternalName() + "]");
}
throw new CacheException( "Unsupported access type [" + accessType.getExternalName() + "]" );
}
public PutFromLoadValidator getPutFromLoadValidator() {
return new PutFromLoadValidator(cache);
}
public PutFromLoadValidator getPutFromLoadValidator() {
return new PutFromLoadValidator( cache );
}
}

View File

@ -1,3 +1,26 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2013, Red Hat Inc. or third-party contributors as
* indicated by the @author tags or express copyright attribution
* statements applied by the authors. All third-party contributions are
* distributed under license by Red Hat Inc.
*
* This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU
* Lesser General Public License, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this distribution; if not, write to:
* Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*/
package org.hibernate.cache.infinispan.collection;
import org.hibernate.cache.CacheException;
@ -8,66 +31,67 @@ import org.hibernate.cache.spi.access.SoftLock;
/**
* Transactional collection region access for Infinispan.
*
*
* @author Chris Bredesen
* @author Galder Zamarreño
* @since 3.5
*/
class TransactionalAccess implements CollectionRegionAccessStrategy {
private final CollectionRegionImpl region;
private final TransactionalAccessDelegate delegate;
private final CollectionRegionImpl region;
TransactionalAccess(CollectionRegionImpl region) {
this.region = region;
this.delegate = new TransactionalAccessDelegate(region, region.getPutFromLoadValidator());
}
private final TransactionalAccessDelegate delegate;
public void evict(Object key) throws CacheException {
delegate.evict(key);
}
TransactionalAccess(CollectionRegionImpl region) {
this.region = region;
this.delegate = new TransactionalAccessDelegate( region, region.getPutFromLoadValidator() );
}
public void evictAll() throws CacheException {
delegate.evictAll();
}
public void evict(Object key) throws CacheException {
delegate.evict( key );
}
public Object get(Object key, long txTimestamp) throws CacheException {
return delegate.get(key, txTimestamp);
}
public void evictAll() throws CacheException {
delegate.evictAll();
}
public boolean putFromLoad(Object key, Object value, long txTimestamp, Object version) throws CacheException {
return delegate.putFromLoad(key, value, txTimestamp, version);
}
public Object get(Object key, long txTimestamp) throws CacheException {
return delegate.get( key, txTimestamp );
}
public boolean putFromLoad(Object key, Object value, long txTimestamp, Object version, boolean minimalPutOverride) throws CacheException {
return delegate.putFromLoad(key, value, txTimestamp, version, minimalPutOverride);
}
public boolean putFromLoad(Object key, Object value, long txTimestamp, Object version) throws CacheException {
return delegate.putFromLoad( key, value, txTimestamp, version );
}
public void remove(Object key) throws CacheException {
delegate.remove(key);
}
public boolean putFromLoad(Object key, Object value, long txTimestamp, Object version, boolean minimalPutOverride)
throws CacheException {
return delegate.putFromLoad( key, value, txTimestamp, version, minimalPutOverride );
}
public void removeAll() throws CacheException {
delegate.removeAll();
}
public void remove(Object key) throws CacheException {
delegate.remove( key );
}
public CollectionRegion getRegion() {
return region;
}
public void removeAll() throws CacheException {
delegate.removeAll();
}
public SoftLock lockItem(Object key, Object version) throws CacheException {
return null;
}
public CollectionRegion getRegion() {
return region;
}
public SoftLock lockRegion() throws CacheException {
return null;
}
public SoftLock lockItem(Object key, Object version) throws CacheException {
return null;
}
public void unlockItem(Object key, SoftLock lock) throws CacheException {
}
public SoftLock lockRegion() throws CacheException {
return null;
}
public void unlockRegion(SoftLock lock) throws CacheException {
}
public void unlockItem(Object key, SoftLock lock) throws CacheException {
}
public void unlockRegion(SoftLock lock) throws CacheException {
}
}

View File

@ -0,0 +1,4 @@
/**
* Internal Infinispan-based implementation of the collection cache region
*/
package org.hibernate.cache.infinispan.collection;

View File

@ -1,5 +1,30 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2013, Red Hat Inc. or third-party contributors as
* indicated by the @author tags or express copyright attribution
* statements applied by the authors. All third-party contributions are
* distributed under license by Red Hat Inc.
*
* This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU
* Lesser General Public License, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this distribution; if not, write to:
* Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*/
package org.hibernate.cache.infinispan.entity;
import org.infinispan.AdvancedCache;
import org.hibernate.cache.CacheException;
import org.hibernate.cache.infinispan.access.PutFromLoadValidator;
import org.hibernate.cache.infinispan.impl.BaseTransactionalDataRegion;
@ -9,8 +34,6 @@ import org.hibernate.cache.spi.RegionFactory;
import org.hibernate.cache.spi.access.AccessType;
import org.hibernate.cache.spi.access.EntityRegionAccessStrategy;
import org.infinispan.AdvancedCache;
/**
* @author Chris Bredesen
* @author Galder Zamarreño
@ -18,8 +41,9 @@ import org.infinispan.AdvancedCache;
*/
public class EntityRegionImpl extends BaseTransactionalDataRegion implements EntityRegion {
public EntityRegionImpl(AdvancedCache cache, String name,
CacheDataDescription metadata, RegionFactory factory) {
public EntityRegionImpl(
AdvancedCache cache, String name,
CacheDataDescription metadata, RegionFactory factory) {
super( cache, name, metadata, factory );
}

View File

@ -1,3 +1,26 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2013, Red Hat Inc. or third-party contributors as
* indicated by the @author tags or express copyright attribution
* statements applied by the authors. All third-party contributions are
* distributed under license by Red Hat Inc.
*
* This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU
* Lesser General Public License, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this distribution; if not, write to:
* Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*/
package org.hibernate.cache.infinispan.entity;
import org.hibernate.cache.CacheException;
@ -14,19 +37,21 @@ import org.hibernate.cache.spi.access.SoftLock;
class ReadOnlyAccess extends TransactionalAccess {
ReadOnlyAccess(EntityRegionImpl region) {
super(region);
super( region );
}
@Override
public boolean update(Object key, Object value, Object currentVersion,
Object previousVersion) throws CacheException {
throw new UnsupportedOperationException("Illegal attempt to edit read only item");
public boolean update(
Object key, Object value, Object currentVersion,
Object previousVersion) throws CacheException {
throw new UnsupportedOperationException( "Illegal attempt to edit read only item" );
}
@Override
public boolean afterUpdate(Object key, Object value, Object currentVersion,
Object previousVersion, SoftLock lock) throws CacheException {
throw new UnsupportedOperationException("Illegal attempt to edit read only item");
public boolean afterUpdate(
Object key, Object value, Object currentVersion,
Object previousVersion, SoftLock lock) throws CacheException {
throw new UnsupportedOperationException( "Illegal attempt to edit read only item" );
}
}

View File

@ -1,3 +1,26 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2013, Red Hat Inc. or third-party contributors as
* indicated by the @author tags or express copyright attribution
* statements applied by the authors. All third-party contributions are
* distributed under license by Red Hat Inc.
*
* This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU
* Lesser General Public License, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this distribution; if not, write to:
* Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*/
package org.hibernate.cache.infinispan.entity;
import org.hibernate.cache.CacheException;
@ -8,81 +31,84 @@ import org.hibernate.cache.spi.access.SoftLock;
/**
* Transactional entity region access for Infinispan.
*
*
* @author Chris Bredesen
* @author Galder Zamarreño
* @since 3.5
*/
class TransactionalAccess implements EntityRegionAccessStrategy {
private final EntityRegionImpl region;
private final TransactionalAccessDelegate delegate;
TransactionalAccess(EntityRegionImpl region) {
this.region = region;
this.delegate = new TransactionalAccessDelegate(region, region.getPutFromLoadValidator());
}
private final EntityRegionImpl region;
public void evict(Object key) throws CacheException {
delegate.evict(key);
}
private final TransactionalAccessDelegate delegate;
public void evictAll() throws CacheException {
delegate.evictAll();
}
TransactionalAccess(EntityRegionImpl region) {
this.region = region;
this.delegate = new TransactionalAccessDelegate( region, region.getPutFromLoadValidator() );
}
public Object get(Object key, long txTimestamp) throws CacheException {
return delegate.get(key, txTimestamp);
}
public void evict(Object key) throws CacheException {
delegate.evict( key );
}
public EntityRegion getRegion() {
return this.region;
}
public void evictAll() throws CacheException {
delegate.evictAll();
}
public boolean insert(Object key, Object value, Object version) throws CacheException {
return delegate.insert(key, value, version);
}
public Object get(Object key, long txTimestamp) throws CacheException {
return delegate.get( key, txTimestamp );
}
public boolean putFromLoad(Object key, Object value, long txTimestamp, Object version) throws CacheException {
return delegate.putFromLoad(key, value, txTimestamp, version);
}
public EntityRegion getRegion() {
return this.region;
}
public boolean putFromLoad(Object key, Object value, long txTimestamp, Object version, boolean minimalPutOverride) throws CacheException {
return delegate.putFromLoad(key, value, txTimestamp, version, minimalPutOverride);
}
public boolean insert(Object key, Object value, Object version) throws CacheException {
return delegate.insert( key, value, version );
}
public void remove(Object key) throws CacheException {
delegate.remove(key);
}
public boolean putFromLoad(Object key, Object value, long txTimestamp, Object version) throws CacheException {
return delegate.putFromLoad( key, value, txTimestamp, version );
}
public void removeAll() throws CacheException {
delegate.removeAll();
}
public boolean putFromLoad(Object key, Object value, long txTimestamp, Object version, boolean minimalPutOverride)
throws CacheException {
return delegate.putFromLoad( key, value, txTimestamp, version, minimalPutOverride );
}
public boolean update(Object key, Object value, Object currentVersion, Object previousVersion) throws CacheException {
return delegate.update(key, value, currentVersion, previousVersion);
}
public void remove(Object key) throws CacheException {
delegate.remove( key );
}
public SoftLock lockItem(Object key, Object version) throws CacheException {
return null;
}
public void removeAll() throws CacheException {
delegate.removeAll();
}
public SoftLock lockRegion() throws CacheException {
return null;
}
public boolean update(Object key, Object value, Object currentVersion, Object previousVersion)
throws CacheException {
return delegate.update( key, value, currentVersion, previousVersion );
}
public void unlockItem(Object key, SoftLock lock) throws CacheException {
}
public SoftLock lockItem(Object key, Object version) throws CacheException {
return null;
}
public void unlockRegion(SoftLock lock) throws CacheException {
}
public SoftLock lockRegion() throws CacheException {
return null;
}
public boolean afterInsert(Object key, Object value, Object version) throws CacheException {
return false;
}
public void unlockItem(Object key, SoftLock lock) throws CacheException {
}
public boolean afterUpdate(Object key, Object value, Object currentVersion, Object previousVersion, SoftLock lock) throws CacheException {
return false;
}
public void unlockRegion(SoftLock lock) throws CacheException {
}
public boolean afterInsert(Object key, Object value, Object version) throws CacheException {
return false;
}
public boolean afterUpdate(Object key, Object value, Object currentVersion, Object previousVersion, SoftLock lock)
throws CacheException {
return false;
}
}

View File

@ -0,0 +1,4 @@
/**
* Internal Infinispan-based implementation of the entity cache region
*/
package org.hibernate.cache.infinispan.entity;

View File

@ -1,44 +1,49 @@
package org.hibernate.cache.infinispan.impl;
import javax.transaction.TransactionManager;
import org.infinispan.AdvancedCache;
import org.hibernate.cache.CacheException;
import org.hibernate.cache.infinispan.util.Caches;
import org.hibernate.cache.spi.GeneralDataRegion;
import org.hibernate.cache.spi.RegionFactory;
import org.infinispan.AdvancedCache;
/**
* Support for Infinispan {@link GeneralDataRegion} implementors.
*
*
* @author Chris Bredesen
* @author Galder Zamarreño
* @since 3.5
*/
public abstract class BaseGeneralDataRegion extends BaseRegion implements GeneralDataRegion {
private final AdvancedCache putCache;
private final AdvancedCache putCache;
public BaseGeneralDataRegion(
AdvancedCache cache, String name,
RegionFactory factory) {
super( cache, name, factory );
this.putCache = Caches.ignoreReturnValuesCache( cache );
}
public BaseGeneralDataRegion(AdvancedCache cache, String name,
RegionFactory factory) {
super(cache, name, factory);
this.putCache = Caches.ignoreReturnValuesCache(cache);
}
@Override
@SuppressWarnings("unchecked")
public void evict(Object key) throws CacheException {
cache.evict( key );
}
public void evict(Object key) throws CacheException {
cache.evict(key);
}
@Override
public void evictAll() throws CacheException {
cache.clear();
}
public void evictAll() throws CacheException {
cache.clear();
}
@Override
public Object get(Object key) throws CacheException {
return cache.get( key );
}
public Object get(Object key) throws CacheException {
return cache.get(key);
}
public void put(Object key, Object value) throws CacheException {
putCache.put(key, value);
}
@Override
@SuppressWarnings("unchecked")
public void put(Object key, Object value) throws CacheException {
putCache.put( key, value );
}
}

View File

@ -1,20 +1,43 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2013, Red Hat Inc. or third-party contributors as
* indicated by the @author tags or express copyright attribution
* statements applied by the authors. All third-party contributions are
* distributed under license by Red Hat Inc.
*
* This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU
* Lesser General Public License, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this distribution; if not, write to:
* Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*/
package org.hibernate.cache.infinispan.impl;
import javax.transaction.SystemException;
import javax.transaction.Transaction;
import javax.transaction.TransactionManager;
import java.util.Collections;
import java.util.Map;
import java.util.concurrent.Callable;
import java.util.concurrent.atomic.AtomicReference;
import javax.transaction.SystemException;
import javax.transaction.Transaction;
import javax.transaction.TransactionManager;
import org.hibernate.cache.infinispan.util.Caches;
import org.infinispan.AdvancedCache;
import org.infinispan.context.Flag;
import org.infinispan.util.logging.Log;
import org.infinispan.util.logging.LogFactory;
import org.hibernate.cache.CacheException;
import org.hibernate.cache.infinispan.util.Caches;
import org.hibernate.cache.spi.Region;
import org.hibernate.cache.spi.RegionFactory;
@ -22,184 +45,208 @@ import org.hibernate.cache.spi.RegionFactory;
* Support for Infinispan {@link Region}s. Handles common "utility" methods for an underlying named
* Cache. In other words, this implementation doesn't actually read or write data. Subclasses are
* expected to provide core cache interaction appropriate to the semantics needed.
*
*
* @author Chris Bredesen
* @author Galder Zamarreño
* @since 3.5
*/
public abstract class BaseRegion implements Region {
private static final Log log = LogFactory.getLog(BaseRegion.class);
private static final Log log = LogFactory.getLog( BaseRegion.class );
private enum InvalidateState {
INVALID, CLEARING, VALID
}
private enum InvalidateState {
INVALID, CLEARING, VALID
}
private final String name;
private final AdvancedCache regionClearCache;
private final TransactionManager tm;
private final Object invalidationMutex = new Object();
private final AtomicReference<InvalidateState> invalidateState =
new AtomicReference<InvalidateState>(InvalidateState.VALID);
private final RegionFactory factory;
private final String name;
private final AdvancedCache regionClearCache;
private final TransactionManager tm;
private final Object invalidationMutex = new Object();
private final AtomicReference<InvalidateState> invalidateState =
new AtomicReference<InvalidateState>( InvalidateState.VALID );
private final RegionFactory factory;
protected final AdvancedCache cache;
protected final AdvancedCache cache;
public BaseRegion(AdvancedCache cache, String name, RegionFactory factory) {
this.cache = cache;
this.name = name;
this.tm = cache.getTransactionManager();
this.factory = factory;
this.regionClearCache = cache.withFlags(
Flag.CACHE_MODE_LOCAL, Flag.ZERO_LOCK_ACQUISITION_TIMEOUT);
}
public BaseRegion(AdvancedCache cache, String name, RegionFactory factory) {
this.cache = cache;
this.name = name;
this.tm = cache.getTransactionManager();
this.factory = factory;
this.regionClearCache = cache.withFlags(
Flag.CACHE_MODE_LOCAL, Flag.ZERO_LOCK_ACQUISITION_TIMEOUT
);
}
public String getName() {
return name;
}
@Override
public String getName() {
return name;
}
public long getElementCountInMemory() {
if (checkValid())
return cache.size();
@Override
public long getElementCountInMemory() {
if ( checkValid() ) {
return cache.size();
}
return 0;
}
return 0;
}
/**
* Not supported.
*
* @return -1
*/
public long getElementCountOnDisk() {
return -1;
}
/**
* {@inheritDoc}
* <p/>
* Not supported; returns -1
*/
@Override
public long getElementCountOnDisk() {
return -1;
}
/**
* Not supported.
*
* @return -1
*/
public long getSizeInMemory() {
return -1;
}
/**
* {@inheritDoc}
* <p/>
* Not supported; returns -1
*/
@Override
public long getSizeInMemory() {
return -1;
}
public int getTimeout() {
return 600; // 60 seconds
}
@Override
public int getTimeout() {
// 60 seconds
return 600;
}
public long nextTimestamp() {
return factory.nextTimestamp();
}
@Override
public long nextTimestamp() {
return factory.nextTimestamp();
}
public Map toMap() {
if (checkValid())
return cache;
@Override
public Map toMap() {
if ( checkValid() ) {
return cache;
}
return Collections.EMPTY_MAP;
}
return Collections.EMPTY_MAP;
}
public void destroy() throws CacheException {
try {
cache.stop();
} finally {
cache.removeListener(this);
}
}
@Override
public void destroy() throws CacheException {
try {
cache.stop();
}
finally {
cache.removeListener( this );
}
}
public boolean contains(Object key) {
return checkValid() && cache.containsKey(key);
}
@Override
public boolean contains(Object key) {
return checkValid() && cache.containsKey( key );
}
public boolean checkValid() {
boolean valid = isValid();
if (!valid) {
synchronized (invalidationMutex) {
if (invalidateState.compareAndSet(
InvalidateState.INVALID, InvalidateState.CLEARING)) {
Transaction tx = suspend();
try {
// Clear region in a separate transaction
Caches.withinTx(cache, new Callable<Void>() {
@Override
public Void call() throws Exception {
regionClearCache.clear();
return null;
}
});
invalidateState.compareAndSet(
InvalidateState.CLEARING, InvalidateState.VALID);
}
catch (Exception e) {
if (log.isTraceEnabled()) {
log.trace("Could not invalidate region: "
+ e.getLocalizedMessage());
}
}
finally {
resume(tx);
}
}
}
valid = isValid();
}
return valid;
}
public boolean checkValid() {
boolean valid = isValid();
if ( !valid ) {
synchronized (invalidationMutex) {
if ( invalidateState.compareAndSet(
InvalidateState.INVALID, InvalidateState.CLEARING
) ) {
Transaction tx = suspend();
try {
// Clear region in a separate transaction
Caches.withinTx(
cache, new Callable<Void>() {
@Override
public Void call() throws Exception {
regionClearCache.clear();
return null;
}
}
);
invalidateState.compareAndSet(
InvalidateState.CLEARING, InvalidateState.VALID
);
}
catch (Exception e) {
if ( log.isTraceEnabled() ) {
log.trace(
"Could not invalidate region: "
+ e.getLocalizedMessage()
);
}
}
finally {
resume( tx );
}
}
}
valid = isValid();
}
protected boolean isValid() {
return invalidateState.get() == InvalidateState.VALID;
}
return valid;
}
/**
* Tell the TransactionManager to suspend any ongoing transaction.
*
* @return the transaction that was suspended, or <code>null</code> if
* there wasn't one
*/
public Transaction suspend() {
Transaction tx = null;
try {
if (tm != null) {
tx = tm.suspend();
}
} catch (SystemException se) {
throw new CacheException("Could not suspend transaction", se);
}
return tx;
}
protected boolean isValid() {
return invalidateState.get() == InvalidateState.VALID;
}
/**
* Tell the TransactionManager to resume the given transaction
*
* @param tx
* the transaction to suspend. May be <code>null</code>.
*/
public void resume(Transaction tx) {
try {
if (tx != null)
tm.resume(tx);
} catch (Exception e) {
throw new CacheException("Could not resume transaction", e);
}
}
/**
* Tell the TransactionManager to suspend any ongoing transaction.
*
* @return the transaction that was suspended, or <code>null</code> if
* there wasn't one
*/
public Transaction suspend() {
Transaction tx = null;
try {
if ( tm != null ) {
tx = tm.suspend();
}
}
catch (SystemException se) {
throw new CacheException( "Could not suspend transaction", se );
}
return tx;
}
public void invalidateRegion() {
if (log.isTraceEnabled()) log.trace("Invalidate region: " + name);
invalidateState.set(InvalidateState.INVALID);
}
/**
* Tell the TransactionManager to resume the given transaction
*
* @param tx the transaction to suspend. May be <code>null</code>.
*/
public void resume(Transaction tx) {
try {
if ( tx != null ) {
tm.resume( tx );
}
}
catch (Exception e) {
throw new CacheException( "Could not resume transaction", e );
}
}
public TransactionManager getTransactionManager() {
return tm;
}
public void invalidateRegion() {
if ( log.isTraceEnabled() ) {
log.trace( "Invalidate region: " + name );
}
invalidateState.set( InvalidateState.INVALID );
}
// Used to satisfy TransactionalDataRegion.isTransactionAware in subclasses
@SuppressWarnings("unused")
public boolean isTransactionAware() {
return tm != null;
}
public TransactionManager getTransactionManager() {
return tm;
}
public AdvancedCache getCache() {
return cache;
}
// Used to satisfy TransactionalDataRegion.isTransactionAware in subclasses
@SuppressWarnings("unused")
public boolean isTransactionAware() {
return tm != null;
}
public AdvancedCache getCache() {
return cache;
}
}

View File

@ -1,31 +1,56 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2013, Red Hat Inc. or third-party contributors as
* indicated by the @author tags or express copyright attribution
* statements applied by the authors. All third-party contributions are
* distributed under license by Red Hat Inc.
*
* This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU
* Lesser General Public License, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this distribution; if not, write to:
* Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*/
package org.hibernate.cache.infinispan.impl;
import org.infinispan.AdvancedCache;
import org.hibernate.cache.spi.CacheDataDescription;
import org.hibernate.cache.spi.RegionFactory;
import org.hibernate.cache.spi.TransactionalDataRegion;
import org.infinispan.AdvancedCache;
/**
* Support for Inifinispan {@link org.hibernate.cache.spi.TransactionalDataRegion} implementors.
*
*
* @author Chris Bredesen
* @author Galder Zamarreño
* @since 3.5
*/
public abstract class BaseTransactionalDataRegion
extends BaseRegion implements TransactionalDataRegion {
extends BaseRegion implements TransactionalDataRegion {
private final CacheDataDescription metadata;
private final CacheDataDescription metadata;
public BaseTransactionalDataRegion(AdvancedCache cache, String name,
CacheDataDescription metadata, RegionFactory factory) {
super(cache, name, factory);
this.metadata = metadata;
}
public BaseTransactionalDataRegion(
AdvancedCache cache, String name,
CacheDataDescription metadata, RegionFactory factory) {
super( cache, name, factory );
this.metadata = metadata;
}
@Override
public CacheDataDescription getCacheDataDescription() {
return metadata;
}
public CacheDataDescription getCacheDataDescription() {
return metadata;
}
}

View File

@ -0,0 +1,4 @@
/**
* Internal Infinispan-specific base cache region implementations
*/
package org.hibernate.cache.infinispan.impl;

View File

@ -1,5 +1,30 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2013, Red Hat Inc. or third-party contributors as
* indicated by the @author tags or express copyright attribution
* statements applied by the authors. All third-party contributions are
* distributed under license by Red Hat Inc.
*
* This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU
* Lesser General Public License, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this distribution; if not, write to:
* Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*/
package org.hibernate.cache.infinispan.naturalid;
import org.infinispan.AdvancedCache;
import org.hibernate.cache.CacheException;
import org.hibernate.cache.infinispan.access.PutFromLoadValidator;
import org.hibernate.cache.infinispan.impl.BaseTransactionalDataRegion;
@ -8,7 +33,6 @@ import org.hibernate.cache.spi.NaturalIdRegion;
import org.hibernate.cache.spi.RegionFactory;
import org.hibernate.cache.spi.access.AccessType;
import org.hibernate.cache.spi.access.NaturalIdRegionAccessStrategy;
import org.infinispan.AdvancedCache;
/**
* Natural ID cache region
@ -17,16 +41,17 @@ import org.infinispan.AdvancedCache;
* @author Galder Zamarreño
*/
public class NaturalIdRegionImpl extends BaseTransactionalDataRegion
implements NaturalIdRegion {
implements NaturalIdRegion {
public NaturalIdRegionImpl(AdvancedCache cache, String name,
CacheDataDescription metadata, RegionFactory factory) {
super(cache, name, metadata, factory);
public NaturalIdRegionImpl(
AdvancedCache cache, String name,
CacheDataDescription metadata, RegionFactory factory) {
super( cache, name, metadata, factory );
}
@Override
public NaturalIdRegionAccessStrategy buildAccessStrategy(AccessType accessType) throws CacheException {
switch ( accessType ){
switch ( accessType ) {
case READ_ONLY:
return new ReadOnlyAccess( this );
case TRANSACTIONAL:
@ -37,7 +62,7 @@ public class NaturalIdRegionImpl extends BaseTransactionalDataRegion
}
public PutFromLoadValidator getPutFromLoadValidator() {
return new PutFromLoadValidator(cache);
return new PutFromLoadValidator( cache );
}
}

View File

@ -1,8 +1,28 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2013, Red Hat Inc. or third-party contributors as
* indicated by the @author tags or express copyright attribution
* statements applied by the authors. All third-party contributions are
* distributed under license by Red Hat Inc.
*
* This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU
* Lesser General Public License, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this distribution; if not, write to:
* Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*/
package org.hibernate.cache.infinispan.naturalid;
import org.infinispan.util.logging.Log;
import org.infinispan.util.logging.LogFactory;
import org.hibernate.cache.CacheException;
import org.hibernate.cache.spi.access.SoftLock;

View File

@ -1,8 +1,30 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2013, Red Hat Inc. or third-party contributors as
* indicated by the @author tags or express copyright attribution
* statements applied by the authors. All third-party contributions are
* distributed under license by Red Hat Inc.
*
* This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU
* Lesser General Public License, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this distribution; if not, write to:
* Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*/
package org.hibernate.cache.infinispan.naturalid;
import org.hibernate.cache.CacheException;
import org.hibernate.cache.infinispan.access.TransactionalAccessDelegate;
import org.hibernate.cache.spi.EntityRegion;
import org.hibernate.cache.spi.NaturalIdRegion;
import org.hibernate.cache.spi.access.NaturalIdRegionAccessStrategy;
import org.hibernate.cache.spi.access.SoftLock;
@ -21,12 +43,12 @@ class TransactionalAccess implements NaturalIdRegionAccessStrategy {
@Override
public boolean insert(Object key, Object value) throws CacheException {
return delegate.insert(key, value, null);
return delegate.insert( key, value, null );
}
@Override
public boolean update(Object key, Object value) throws CacheException {
return delegate.update(key, value, null, null);
return delegate.update( key, value, null, null );
}
@Override
@ -88,14 +110,14 @@ class TransactionalAccess implements NaturalIdRegionAccessStrategy {
public void unlockRegion(SoftLock lock) throws CacheException {
}
@Override
public boolean afterInsert(Object key, Object value) throws CacheException {
return false;
}
@Override
public boolean afterInsert(Object key, Object value) throws CacheException {
return false;
}
@Override
public boolean afterUpdate(Object key, Object value, SoftLock lock) throws CacheException {
return false;
}
@Override
public boolean afterUpdate(Object key, Object value, SoftLock lock) throws CacheException {
return false;
}
}

View File

@ -0,0 +1,4 @@
/**
* Internal Infinispan-based implementation of the natural-id cache region
*/
package org.hibernate.cache.infinispan.naturalid;

View File

@ -0,0 +1,4 @@
/**
* Defines the integration with Infinispan as a second-level cache service.
*/
package org.hibernate.cache.infinispan;

View File

@ -1,14 +1,38 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2013, Red Hat Inc. or third-party contributors as
* indicated by the @author tags or express copyright attribution
* statements applied by the authors. All third-party contributions are
* distributed under license by Red Hat Inc.
*
* This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU
* Lesser General Public License, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this distribution; if not, write to:
* Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*/
package org.hibernate.cache.infinispan.query;
import javax.transaction.Transaction;
import org.infinispan.AdvancedCache;
import org.infinispan.context.Flag;
import org.hibernate.cache.CacheException;
import org.hibernate.cache.infinispan.impl.BaseTransactionalDataRegion;
import org.hibernate.cache.infinispan.util.Caches;
import org.hibernate.cache.spi.QueryResultsRegion;
import org.hibernate.cache.spi.RegionFactory;
import org.infinispan.AdvancedCache;
import org.infinispan.context.Flag;
/**
* @author Chris Bredesen
@ -17,80 +41,91 @@ import org.infinispan.context.Flag;
*/
public class QueryResultsRegionImpl extends BaseTransactionalDataRegion implements QueryResultsRegion {
private final AdvancedCache evictCache;
private final AdvancedCache putCache;
private final AdvancedCache getCache;
private final AdvancedCache evictCache;
private final AdvancedCache putCache;
private final AdvancedCache getCache;
public QueryResultsRegionImpl(AdvancedCache cache, String name, RegionFactory factory) {
super(cache, name, null, factory);
// If Infinispan is using INVALIDATION for query cache, we don't want to propagate changes.
// We use the Timestamps cache to manage invalidation
boolean localOnly = Caches.isInvalidationCache(cache);
public QueryResultsRegionImpl(AdvancedCache cache, String name, RegionFactory factory) {
super( cache, name, null, factory );
// If Infinispan is using INVALIDATION for query cache, we don't want to propagate changes.
// We use the Timestamps cache to manage invalidation
final boolean localOnly = Caches.isInvalidationCache( cache );
this.evictCache = localOnly ? Caches.localCache(cache) : cache;
this.evictCache = localOnly ? Caches.localCache( cache ) : cache;
this.putCache = localOnly ?
Caches.failSilentWriteCache(cache, Flag.CACHE_MODE_LOCAL) :
Caches.failSilentWriteCache(cache);
this.putCache = localOnly ?
Caches.failSilentWriteCache( cache, Flag.CACHE_MODE_LOCAL ) :
Caches.failSilentWriteCache( cache );
this.getCache = Caches.failSilentReadCache(cache);
}
this.getCache = Caches.failSilentReadCache( cache );
}
public void evict(Object key) throws CacheException {
evictCache.remove(key);
}
@Override
public void evict(Object key) throws CacheException {
evictCache.remove( key );
}
public void evictAll() throws CacheException {
Transaction tx = suspend();
try {
invalidateRegion(); // Invalidate the local region and then go remote
Caches.broadcastEvictAll(cache);
} finally {
resume(tx);
}
}
@Override
public void evictAll() throws CacheException {
final Transaction tx = suspend();
try {
// Invalidate the local region and then go remote
invalidateRegion();
Caches.broadcastEvictAll( cache );
}
finally {
resume( tx );
}
}
public Object get(Object key) throws CacheException {
// If the region is not valid, skip cache store to avoid going remote to retrieve the query.
// The aim of this is to maintain same logic/semantics as when state transfer was configured.
// TODO: Once https://issues.jboss.org/browse/ISPN-835 has been resolved, revert to state transfer and remove workaround
boolean skipCacheStore = false;
if (!isValid())
skipCacheStore = true;
@Override
public Object get(Object key) throws CacheException {
// If the region is not valid, skip cache store to avoid going remote to retrieve the query.
// The aim of this is to maintain same logic/semantics as when state transfer was configured.
// TODO: Once https://issues.jboss.org/browse/ISPN-835 has been resolved, revert to state transfer and remove workaround
boolean skipCacheStore = false;
if ( !isValid() ) {
skipCacheStore = true;
}
if (!checkValid())
return null;
if ( !checkValid() ) {
return null;
}
// In Infinispan get doesn't acquire any locks, so no need to suspend the tx.
// In the past, when get operations acquired locks, suspending the tx was a way
// to avoid holding locks that would prevent updates.
// Add a zero (or low) timeout option so we don't block
// waiting for tx's that did a put to commit
if (skipCacheStore)
return getCache.withFlags(Flag.SKIP_CACHE_STORE).get(key);
else
return getCache.get(key);
}
// In Infinispan get doesn't acquire any locks, so no need to suspend the tx.
// In the past, when get operations acquired locks, suspending the tx was a way
// to avoid holding locks that would prevent updates.
// Add a zero (or low) timeout option so we don't block
// waiting for tx's that did a put to commit
if ( skipCacheStore ) {
return getCache.withFlags( Flag.SKIP_CACHE_STORE ).get( key );
}
else {
return getCache.get( key );
}
}
public void put(Object key, Object value) throws CacheException {
if (checkValid()) {
// Here we don't want to suspend the tx. If we do:
// 1) We might be caching query results that reflect uncommitted
// changes. No tx == no WL on cache node, so other threads
// can prematurely see those query results
// 2) No tx == immediate replication. More overhead, plus we
// spread issue #1 above around the cluster
@Override
@SuppressWarnings("unchecked")
public void put(Object key, Object value) throws CacheException {
if ( checkValid() ) {
// Here we don't want to suspend the tx. If we do:
// 1) We might be caching query results that reflect uncommitted
// changes. No tx == no WL on cache node, so other threads
// can prematurely see those query results
// 2) No tx == immediate replication. More overhead, plus we
// spread issue #1 above around the cluster
// Add a zero (or quite low) timeout option so we don't block.
// Ignore any TimeoutException. Basically we forego caching the
// query result in order to avoid blocking.
// Reads are done with suspended tx, so they should not hold the
// lock for long. Not caching the query result is OK, since
// any subsequent read will just see the old result with its
// out-of-date timestamp; that result will be discarded and the
// db query performed again.
putCache.put(key, value);
}
}
// Add a zero (or quite low) timeout option so we don't block.
// Ignore any TimeoutException. Basically we forego caching the
// query result in order to avoid blocking.
// Reads are done with suspended tx, so they should not hold the
// lock for long. Not caching the query result is OK, since
// any subsequent read will just see the old result with its
// out-of-date timestamp; that result will be discarded and the
// db query performed again.
putCache.put( key, value );
}
}
}

View File

@ -0,0 +1,4 @@
/**
* Internal Infinispan-based implementation of the "query results" cache region
*/
package org.hibernate.cache.infinispan.query;

View File

@ -20,12 +20,13 @@
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/
package org.hibernate.cache.infinispan.timestamp;
import org.hibernate.cache.CacheException;
import org.hibernate.cache.infinispan.util.Caches;
import org.hibernate.cache.spi.RegionFactory;
import javax.transaction.Transaction;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import org.infinispan.AdvancedCache;
import org.infinispan.context.Flag;
import org.infinispan.notifications.Listener;
@ -34,10 +35,9 @@ import org.infinispan.notifications.cachelistener.annotation.CacheEntryRemoved;
import org.infinispan.notifications.cachelistener.event.CacheEntryModifiedEvent;
import org.infinispan.notifications.cachelistener.event.CacheEntryRemovedEvent;
import javax.transaction.Transaction;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import org.hibernate.cache.CacheException;
import org.hibernate.cache.infinispan.util.Caches;
import org.hibernate.cache.spi.RegionFactory;
/**
* Timestamp cache region for clustered environments.
@ -48,105 +48,118 @@ import java.util.concurrent.ConcurrentHashMap;
@Listener
public class ClusteredTimestampsRegionImpl extends TimestampsRegionImpl {
/**
* Maintains a local (authoritative) cache of timestamps along with the
* replicated cache held in Infinispan. It listens for changes in the
* cache and updates the local cache accordingly. This approach allows
* timestamp changes to be replicated asynchronously.
*/
private final Map localCache = new ConcurrentHashMap();
/**
* Maintains a local (authoritative) cache of timestamps along with the
* replicated cache held in Infinispan. It listens for changes in the
* cache and updates the local cache accordingly. This approach allows
* timestamp changes to be replicated asynchronously.
*/
private final Map localCache = new ConcurrentHashMap();
public ClusteredTimestampsRegionImpl(AdvancedCache cache,
String name, RegionFactory factory) {
super(cache, name, factory);
cache.addListener(this);
populateLocalCache();
}
public ClusteredTimestampsRegionImpl(
AdvancedCache cache,
String name, RegionFactory factory) {
super( cache, name, factory );
cache.addListener( this );
populateLocalCache();
}
@Override
protected AdvancedCache getTimestampsPutCache(AdvancedCache cache) {
return Caches.asyncWriteCache(cache, Flag.SKIP_LOCKING);
}
@Override
protected AdvancedCache getTimestampsPutCache(AdvancedCache cache) {
return Caches.asyncWriteCache( cache, Flag.SKIP_LOCKING );
}
@Override
public Object get(Object key) throws CacheException {
Object value = localCache.get(key);
@Override
@SuppressWarnings("unchecked")
public Object get(Object key) throws CacheException {
Object value = localCache.get( key );
// If the region is not valid, skip cache store to avoid going remote to retrieve the query.
// The aim of this is to maintain same logic/semantics as when state transfer was configured.
// TODO: Once https://issues.jboss.org/browse/ISPN-835 has been resolved, revert to state transfer and remove workaround
boolean skipCacheStore = false;
if (!isValid())
skipCacheStore = true;
// If the region is not valid, skip cache store to avoid going remote to retrieve the query.
// The aim of this is to maintain same logic/semantics as when state transfer was configured.
// TODO: Once https://issues.jboss.org/browse/ISPN-835 has been resolved, revert to state transfer and remove workaround
boolean skipCacheStore = false;
if ( !isValid() ) {
skipCacheStore = true;
}
if (value == null && checkValid()) {
if (skipCacheStore)
value = cache.withFlags(Flag.SKIP_CACHE_STORE).get(key);
else
value = cache.get(key);
if ( value == null && checkValid() ) {
if ( skipCacheStore ) {
value = cache.withFlags( Flag.SKIP_CACHE_STORE ).get( key );
}
else {
value = cache.get( key );
}
if (value != null)
localCache.put(key, value);
}
return value;
}
if ( value != null ) {
localCache.put( key, value );
}
}
return value;
}
@Override
public void evictAll() throws CacheException {
// TODO Is this a valid operation on a timestamps cache?
Transaction tx = suspend();
try {
invalidateRegion(); // Invalidate the local region and then go remote
Caches.broadcastEvictAll(cache);
} finally {
resume(tx);
}
}
@Override
public void evictAll() throws CacheException {
// TODO Is this a valid operation on a timestamps cache?
final Transaction tx = suspend();
try {
// Invalidate the local region and then go remote
invalidateRegion();
Caches.broadcastEvictAll( cache );
}
finally {
resume( tx );
}
}
@Override
public void invalidateRegion() {
super.invalidateRegion(); // Invalidate first
localCache.clear();
}
@Override
public void invalidateRegion() {
// Invalidate first
super.invalidateRegion();
localCache.clear();
}
@Override
public void destroy() throws CacheException {
localCache.clear();
cache.removeListener(this);
super.destroy();
}
@Override
public void destroy() throws CacheException {
localCache.clear();
cache.removeListener( this );
super.destroy();
}
/**
* Brings all data from the distributed cache into our local cache.
*/
private void populateLocalCache() {
Set children = cache.keySet();
for (Object key : children)
get(key);
}
/**
* Brings all data from the distributed cache into our local cache.
*/
private void populateLocalCache() {
final Set children = cache.keySet();
for ( Object key : children ) {
get( key );
}
}
/**
* Monitors cache events and updates the local cache
*
* @param event
*/
@CacheEntryModified
@SuppressWarnings("unused")
public void nodeModified(CacheEntryModifiedEvent event) {
if (!event.isPre())
localCache.put(event.getKey(), event.getValue());
}
/**
* Monitors cache events and updates the local cache
*
* @param event The event
*/
@CacheEntryModified
@SuppressWarnings({"unused", "unchecked"})
public void nodeModified(CacheEntryModifiedEvent event) {
if ( !event.isPre() ) {
localCache.put( event.getKey(), event.getValue() );
}
}
/**
* Monitors cache events and updates the local cache
*
* @param event
*/
@CacheEntryRemoved
@SuppressWarnings("unused")
public void nodeRemoved(CacheEntryRemovedEvent event) {
if (event.isPre()) return;
localCache.remove(event.getKey());
}
/**
* Monitors cache events and updates the local cache
*
* @param event The event
*/
@CacheEntryRemoved
@SuppressWarnings("unused")
public void nodeRemoved(CacheEntryRemovedEvent event) {
if ( event.isPre() ) {
return;
}
localCache.remove( event.getKey() );
}
}

View File

@ -29,21 +29,21 @@ import org.hibernate.cache.infinispan.TypeOverrides;
/**
* TimestampTypeOverrides.
*
*
* @author Galder Zamarreño
* @since 3.5
*/
public class TimestampTypeOverrides extends TypeOverrides {
@Override
public void validateInfinispanConfiguration(Configuration cfg) throws CacheException {
if (cfg.clustering().cacheMode().isInvalidation()) {
throw new CacheException("Timestamp cache cannot be configured with invalidation");
}
EvictionStrategy strategy = cfg.eviction().strategy();
if (!strategy.equals(EvictionStrategy.NONE)) {
throw new CacheException("Timestamp cache cannot be configured with eviction");
}
}
@Override
public void validateInfinispanConfiguration(Configuration cfg) throws CacheException {
if ( cfg.clustering().cacheMode().isInvalidation() ) {
throw new CacheException( "Timestamp cache cannot be configured with invalidation" );
}
final EvictionStrategy strategy = cfg.eviction().strategy();
if ( !strategy.equals( EvictionStrategy.NONE ) ) {
throw new CacheException( "Timestamp cache cannot be configured with eviction" );
}
}
}

View File

@ -1,89 +1,114 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2013, Red Hat Inc. or third-party contributors as
* indicated by the @author tags or express copyright attribution
* statements applied by the authors. All third-party contributions are
* distributed under license by Red Hat Inc.
*
* This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU
* Lesser General Public License, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this distribution; if not, write to:
* Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*/
package org.hibernate.cache.infinispan.timestamp;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import javax.transaction.Transaction;
import org.hibernate.cache.infinispan.util.Caches;
import org.infinispan.AdvancedCache;
import org.infinispan.context.Flag;
import org.infinispan.notifications.Listener;
import org.infinispan.notifications.cachelistener.annotation.CacheEntryModified;
import org.infinispan.notifications.cachelistener.annotation.CacheEntryRemoved;
import org.infinispan.notifications.cachelistener.event.CacheEntryModifiedEvent;
import org.infinispan.notifications.cachelistener.event.CacheEntryRemovedEvent;
import org.hibernate.cache.CacheException;
import org.hibernate.cache.infinispan.impl.BaseGeneralDataRegion;
import org.hibernate.cache.infinispan.util.Caches;
import org.hibernate.cache.spi.RegionFactory;
import org.hibernate.cache.spi.TimestampsRegion;
/**
* Defines the behavior of the timestamps cache region for Infinispan.
*
*
* @author Chris Bredesen
* @author Galder Zamarreño
* @since 3.5
*/
public class TimestampsRegionImpl extends BaseGeneralDataRegion implements TimestampsRegion {
private final AdvancedCache removeCache;
private final AdvancedCache timestampsPutCache;
private final AdvancedCache removeCache;
private final AdvancedCache timestampsPutCache;
public TimestampsRegionImpl(AdvancedCache cache, String name,
RegionFactory factory) {
super(cache, name, factory);
this.removeCache = Caches.ignoreReturnValuesCache(cache);
public TimestampsRegionImpl(
AdvancedCache cache, String name,
RegionFactory factory) {
super( cache, name, factory );
this.removeCache = Caches.ignoreReturnValuesCache( cache );
// Skip locking when updating timestamps to provide better performance
// under highly concurrent insert scenarios, where update timestamps
// for an entity/collection type are constantly updated, creating
// contention.
//
// The worst it can happen is that an earlier an earlier timestamp
// (i.e. ts=1) will override a later on (i.e. ts=2), so it means that
// in highly concurrent environments, queries might be considered stale
// earlier in time. The upside is that inserts/updates are way faster
// in local set ups.
this.timestampsPutCache = getTimestampsPutCache(cache);
}
// Skip locking when updating timestamps to provide better performance
// under highly concurrent insert scenarios, where update timestamps
// for an entity/collection type are constantly updated, creating
// contention.
//
// The worst it can happen is that an earlier an earlier timestamp
// (i.e. ts=1) will override a later on (i.e. ts=2), so it means that
// in highly concurrent environments, queries might be considered stale
// earlier in time. The upside is that inserts/updates are way faster
// in local set ups.
this.timestampsPutCache = getTimestampsPutCache( cache );
}
protected AdvancedCache getTimestampsPutCache(AdvancedCache cache) {
return Caches.ignoreReturnValuesCache(cache, Flag.SKIP_LOCKING);
}
protected AdvancedCache getTimestampsPutCache(AdvancedCache cache) {
return Caches.ignoreReturnValuesCache( cache, Flag.SKIP_LOCKING );
}
@Override
public void evict(Object key) throws CacheException {
// TODO Is this a valid operation on a timestamps cache?
removeCache.remove(key);
}
@Override
public void evict(Object key) throws CacheException {
// TODO Is this a valid operation on a timestamps cache?
removeCache.remove( key );
}
public void evictAll() throws CacheException {
// TODO Is this a valid operation on a timestamps cache?
Transaction tx = suspend();
try {
invalidateRegion(); // Invalidate the local region
} finally {
resume(tx);
}
}
@Override
public void evictAll() throws CacheException {
// TODO Is this a valid operation on a timestamps cache?
final Transaction tx = suspend();
try {
// Invalidate the local region
invalidateRegion();
}
finally {
resume( tx );
}
}
public Object get(Object key) throws CacheException {
if (checkValid())
return cache.get(key);
return null;
}
@Override
public Object get(Object key) throws CacheException {
if ( checkValid() ) {
return cache.get( key );
}
public void put(final Object key, final Object value) throws CacheException {
try {
// We ensure ASYNC semantics (JBCACHE-1175) and make sure previous
// value is not loaded from cache store cos it's not needed.
timestampsPutCache.put(key, value);
} catch (Exception e) {
throw new CacheException(e);
}
}
return null;
}
@Override
@SuppressWarnings("unchecked")
public void put(final Object key, final Object value) throws CacheException {
try {
// We ensure ASYNC semantics (JBCACHE-1175) and make sure previous
// value is not loaded from cache store cos it's not needed.
timestampsPutCache.put( key, value );
}
catch (Exception e) {
throw new CacheException( e );
}
}
}

View File

@ -0,0 +1,4 @@
/**
* Internal Infinispan-based implementation of the "update timestamps" cache region
*/
package org.hibernate.cache.infinispan.timestamp;

View File

@ -21,15 +21,15 @@
*/
package org.hibernate.cache.infinispan.tm;
import java.util.Properties;
import javax.transaction.TransactionManager;
import java.util.Properties;
import org.hibernate.cfg.Settings;
import org.hibernate.engine.transaction.jta.platform.spi.JtaPlatform;
/**
* HibernateTransactionManagerLookup.
*
*
* @author Galder Zamarreño
* @since 3.5
*/
@ -39,9 +39,10 @@ public class HibernateTransactionManagerLookup implements org.infinispan.transac
public HibernateTransactionManagerLookup(Settings settings, Properties properties) {
this.jtaPlatform = settings != null ? settings.getJtaPlatform() : null;
}
@Override
public TransactionManager getTransactionManager() throws Exception {
return jtaPlatform == null ? null : jtaPlatform.retrieveTransactionManager();
}
}

View File

@ -0,0 +1,4 @@
/**
* Internal bridging between Infinispan and Hibernate notions of talking to JTA
*/
package org.hibernate.cache.infinispan.tm;

View File

@ -1,3 +1,26 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2013, Red Hat Inc. or third-party contributors as
* indicated by the @author tags or express copyright attribution
* statements applied by the authors. All third-party contributions are
* distributed under license by Red Hat Inc.
*
* This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU
* Lesser General Public License, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this distribution; if not, write to:
* Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*/
package org.hibernate.cache.infinispan.util;
import org.infinispan.commands.module.ExtendedModuleCommandFactory;
@ -11,18 +34,17 @@ import org.infinispan.commands.module.ModuleCommandInitializer;
* @since 4.0
*/
public class CacheCommandExtensions implements ModuleCommandExtensions {
final CacheCommandFactory cacheCommandFactory = new CacheCommandFactory();
final CacheCommandInitializer cacheCommandInitializer = new CacheCommandInitializer();
final CacheCommandFactory cacheCommandFactory = new CacheCommandFactory();
final CacheCommandInitializer cacheCommandInitializer = new CacheCommandInitializer();
@Override
public ExtendedModuleCommandFactory getModuleCommandFactory() {
return cacheCommandFactory;
}
@Override
public ExtendedModuleCommandFactory getModuleCommandFactory() {
return cacheCommandFactory;
}
@Override
public ModuleCommandInitializer getModuleCommandInitializer() {
return cacheCommandInitializer;
}
@Override
public ModuleCommandInitializer getModuleCommandInitializer() {
return cacheCommandInitializer;
}
}

View File

@ -1,3 +1,26 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2013, Red Hat Inc. or third-party contributors as
* indicated by the @author tags or express copyright attribution
* statements applied by the authors. All third-party contributions are
* distributed under license by Red Hat Inc.
*
* This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU
* Lesser General Public License, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this distribution; if not, write to:
* Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*/
package org.hibernate.cache.infinispan.util;
import java.util.HashMap;
@ -19,45 +42,45 @@ import org.hibernate.cache.infinispan.impl.BaseRegion;
* @since 4.0
*/
public class CacheCommandFactory implements ExtendedModuleCommandFactory {
private ConcurrentMap<String, BaseRegion> allRegions =
new ConcurrentHashMap<String, BaseRegion>();
private ConcurrentMap<String, BaseRegion> allRegions =
new ConcurrentHashMap<String, BaseRegion>();
public void addRegion(String regionName, BaseRegion region) {
allRegions.put( regionName, region );
}
public void addRegion(String regionName, BaseRegion region) {
allRegions.put(regionName, region);
}
public void clearRegions(List<String> regionNames) {
for ( String regionName : regionNames ) {
allRegions.remove( regionName );
}
}
public void clearRegions(List<String> regionNames) {
for (String regionName : regionNames)
allRegions.remove(regionName);
}
@Override
public Map<Byte, Class<? extends ReplicableCommand>> getModuleCommands() {
final Map<Byte, Class<? extends ReplicableCommand>> map = new HashMap<Byte, Class<? extends ReplicableCommand>>( 3 );
map.put( CacheCommandIds.EVICT_ALL, EvictAllCommand.class );
return map;
}
@Override
public Map<Byte, Class<? extends ReplicableCommand>> getModuleCommands() {
Map<Byte, Class<? extends ReplicableCommand>> map = new HashMap<Byte, Class<? extends ReplicableCommand>>(3);
map.put(CacheCommandIds.EVICT_ALL, EvictAllCommand.class);
return map;
}
@Override
public CacheRpcCommand fromStream(byte commandId, Object[] args, String cacheName) {
CacheRpcCommand c;
switch ( commandId ) {
case CacheCommandIds.EVICT_ALL:
c = new EvictAllCommand( cacheName, allRegions.get( cacheName ) );
break;
default:
throw new IllegalArgumentException( "Not registered to handle command id " + commandId );
}
c.setParameters( commandId, args );
return c;
}
@Override
public CacheRpcCommand fromStream(byte commandId, Object[] args, String cacheName) {
CacheRpcCommand c;
switch (commandId) {
case CacheCommandIds.EVICT_ALL:
c = new EvictAllCommand(cacheName, allRegions.get(cacheName));
break;
default:
throw new IllegalArgumentException("Not registered to handle command id " + commandId);
}
c.setParameters(commandId, args);
return c;
}
@Override
public ReplicableCommand fromStream(byte commandId, Object[] args) {
// Should not be called while this factory only
// provides cache specific replicable commands.
return null;
}
@Override
public ReplicableCommand fromStream(byte commandId, Object[] args) {
// Should not be called while this factory only
// provides cache specific replicable commands.
return null;
}
}

View File

@ -1,3 +1,26 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2013, Red Hat Inc. or third-party contributors as
* indicated by the @author tags or express copyright attribution
* statements applied by the authors. All third-party contributions are
* distributed under license by Red Hat Inc.
*
* This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU
* Lesser General Public License, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this distribution; if not, write to:
* Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*/
package org.hibernate.cache.infinispan.util;
/**
@ -7,7 +30,8 @@ package org.hibernate.cache.infinispan.util;
* @since 4.0
*/
public interface CacheCommandIds {
byte EVICT_ALL = 120;
/**
* The "evict all" command id
*/
public static final byte EVICT_ALL = 120;
}

View File

@ -1,3 +1,26 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2013, Red Hat Inc. or third-party contributors as
* indicated by the @author tags or express copyright attribution
* statements applied by the authors. All third-party contributions are
* distributed under license by Red Hat Inc.
*
* This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU
* Lesser General Public License, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this distribution; if not, write to:
* Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*/
package org.hibernate.cache.infinispan.util;
import org.infinispan.commands.ReplicableCommand;
@ -11,17 +34,17 @@ import org.infinispan.commands.module.ModuleCommandInitializer;
*/
public class CacheCommandInitializer implements ModuleCommandInitializer {
public EvictAllCommand buildEvictAllCommand(String regionName) {
// No need to pass region factory because no information on that object
// is sent around the cluster. However, when the command factory builds
// and evict all command remotely, it does need to initialize it with
// the right region factory so that it can call it back.
return new EvictAllCommand(regionName);
}
public EvictAllCommand buildEvictAllCommand(String regionName) {
// No need to pass region factory because no information on that object
// is sent around the cluster. However, when the command factory builds
// and evict all command remotely, it does need to initialize it with
// the right region factory so that it can call it back.
return new EvictAllCommand( regionName );
}
@Override
public void initializeReplicableCommand(ReplicableCommand c, boolean isRemote) {
// No need to initialize...
}
@Override
public void initializeReplicableCommand(ReplicableCommand c, boolean isRemote) {
// No need to initialize...
}
}

View File

@ -20,17 +20,16 @@
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/
package org.hibernate.cache.infinispan.util;
import org.infinispan.AdvancedCache;
import org.infinispan.context.Flag;
import org.infinispan.remoting.rpc.RpcManager;
import javax.transaction.Status;
import javax.transaction.TransactionManager;
import java.util.concurrent.Callable;
import org.infinispan.AdvancedCache;
import org.infinispan.context.Flag;
import org.infinispan.remoting.rpc.RpcManager;
/**
* Helper for dealing with Infinispan cache instances.
*
@ -39,103 +38,118 @@ import java.util.concurrent.Callable;
*/
public class Caches {
private Caches() {
// Suppresses default constructor, ensuring non-instantiability.
}
private Caches() {
// Suppresses default constructor, ensuring non-instantiability.
}
public static <T> T withinTx(AdvancedCache cache,
Callable<T> c) throws Exception {
// Retrieve transaction manager
return withinTx(cache.getTransactionManager(), c);
}
public static <T> T withinTx(
AdvancedCache cache,
Callable<T> c) throws Exception {
// Retrieve transaction manager
return withinTx( cache.getTransactionManager(), c );
}
public static <T> T withinTx(TransactionManager tm,
Callable<T> c) throws Exception {
tm.begin();
try {
return c.call();
} catch (Exception e) {
tm.setRollbackOnly();
throw e;
} finally {
if (tm.getStatus() == Status.STATUS_ACTIVE) tm.commit();
else tm.rollback();
}
}
public static <T> T withinTx(
TransactionManager tm,
Callable<T> c) throws Exception {
tm.begin();
try {
return c.call();
}
catch (Exception e) {
tm.setRollbackOnly();
throw e;
}
finally {
if ( tm.getStatus() == Status.STATUS_ACTIVE ) {
tm.commit();
}
else {
tm.rollback();
}
}
}
public static AdvancedCache localCache(AdvancedCache cache) {
return cache.withFlags(Flag.CACHE_MODE_LOCAL);
}
public static AdvancedCache localCache(AdvancedCache cache) {
return cache.withFlags( Flag.CACHE_MODE_LOCAL );
}
public static AdvancedCache ignoreReturnValuesCache(AdvancedCache cache) {
return cache.withFlags(Flag.SKIP_CACHE_LOAD, Flag.SKIP_REMOTE_LOOKUP);
}
public static AdvancedCache ignoreReturnValuesCache(AdvancedCache cache) {
return cache.withFlags( Flag.SKIP_CACHE_LOAD, Flag.SKIP_REMOTE_LOOKUP );
}
public static AdvancedCache ignoreReturnValuesCache(
AdvancedCache cache, Flag extraFlag) {
return cache.withFlags(
Flag.SKIP_CACHE_LOAD, Flag.SKIP_REMOTE_LOOKUP, extraFlag);
}
public static AdvancedCache ignoreReturnValuesCache(
AdvancedCache cache, Flag extraFlag) {
return cache.withFlags(
Flag.SKIP_CACHE_LOAD, Flag.SKIP_REMOTE_LOOKUP, extraFlag
);
}
public static AdvancedCache asyncWriteCache(AdvancedCache cache,
Flag extraFlag) {
return cache.withFlags(
Flag.SKIP_CACHE_LOAD,
Flag.SKIP_REMOTE_LOOKUP,
Flag.FORCE_ASYNCHRONOUS,
extraFlag);
}
public static AdvancedCache asyncWriteCache(
AdvancedCache cache,
Flag extraFlag) {
return cache.withFlags(
Flag.SKIP_CACHE_LOAD,
Flag.SKIP_REMOTE_LOOKUP,
Flag.FORCE_ASYNCHRONOUS,
extraFlag
);
}
public static AdvancedCache failSilentWriteCache(AdvancedCache cache) {
return cache.withFlags(
Flag.FAIL_SILENTLY,
Flag.ZERO_LOCK_ACQUISITION_TIMEOUT,
Flag.SKIP_CACHE_LOAD,
Flag.SKIP_REMOTE_LOOKUP);
}
public static AdvancedCache failSilentWriteCache(AdvancedCache cache) {
return cache.withFlags(
Flag.FAIL_SILENTLY,
Flag.ZERO_LOCK_ACQUISITION_TIMEOUT,
Flag.SKIP_CACHE_LOAD,
Flag.SKIP_REMOTE_LOOKUP
);
}
public static AdvancedCache failSilentWriteCache(AdvancedCache cache,
Flag extraFlag) {
return cache.withFlags(
Flag.FAIL_SILENTLY,
Flag.ZERO_LOCK_ACQUISITION_TIMEOUT,
Flag.SKIP_CACHE_LOAD,
Flag.SKIP_REMOTE_LOOKUP,
extraFlag);
}
public static AdvancedCache failSilentWriteCache(
AdvancedCache cache,
Flag extraFlag) {
return cache.withFlags(
Flag.FAIL_SILENTLY,
Flag.ZERO_LOCK_ACQUISITION_TIMEOUT,
Flag.SKIP_CACHE_LOAD,
Flag.SKIP_REMOTE_LOOKUP,
extraFlag
);
}
public static AdvancedCache failSilentReadCache(AdvancedCache cache) {
return cache.withFlags(
Flag.FAIL_SILENTLY,
Flag.ZERO_LOCK_ACQUISITION_TIMEOUT);
}
public static AdvancedCache failSilentReadCache(AdvancedCache cache) {
return cache.withFlags(
Flag.FAIL_SILENTLY,
Flag.ZERO_LOCK_ACQUISITION_TIMEOUT
);
}
public static void broadcastEvictAll(AdvancedCache cache) {
RpcManager rpcManager = cache.getRpcManager();
if (rpcManager != null) {
// Only broadcast evict all if it's clustered
CacheCommandInitializer factory = cache.getComponentRegistry()
.getComponent(CacheCommandInitializer.class);
boolean isSync = isSynchronousCache(cache);
public static void broadcastEvictAll(AdvancedCache cache) {
final RpcManager rpcManager = cache.getRpcManager();
if ( rpcManager != null ) {
// Only broadcast evict all if it's clustered
final CacheCommandInitializer factory = cache.getComponentRegistry()
.getComponent( CacheCommandInitializer.class );
final boolean isSync = isSynchronousCache( cache );
EvictAllCommand cmd = factory.buildEvictAllCommand(cache.getName());
rpcManager.broadcastRpcCommand(cmd, isSync);
}
}
final EvictAllCommand cmd = factory.buildEvictAllCommand( cache.getName() );
rpcManager.broadcastRpcCommand( cmd, isSync );
}
}
public static boolean isInvalidationCache(AdvancedCache cache) {
return cache.getCacheConfiguration()
.clustering().cacheMode().isInvalidation();
}
public static boolean isInvalidationCache(AdvancedCache cache) {
return cache.getCacheConfiguration()
.clustering().cacheMode().isInvalidation();
}
public static boolean isSynchronousCache(AdvancedCache cache) {
return cache.getCacheConfiguration()
.clustering().cacheMode().isSynchronous();
}
public static boolean isSynchronousCache(AdvancedCache cache) {
return cache.getCacheConfiguration()
.clustering().cacheMode().isSynchronous();
}
public static boolean isClustered(AdvancedCache cache) {
return cache.getCacheConfiguration()
.clustering().cacheMode().isClustered();
}
public static boolean isClustered(AdvancedCache cache) {
return cache.getCacheConfiguration()
.clustering().cacheMode().isClustered();
}
}

View File

@ -1,3 +1,26 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2013, Red Hat Inc. or third-party contributors as
* indicated by the @author tags or express copyright attribution
* statements applied by the authors. All third-party contributions are
* distributed under license by Red Hat Inc.
*
* This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU
* Lesser General Public License, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this distribution; if not, write to:
* Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*/
package org.hibernate.cache.infinispan.util;
import org.infinispan.commands.remote.BaseRpcCommand;
@ -13,41 +36,42 @@ import org.hibernate.cache.infinispan.impl.BaseRegion;
*/
public class EvictAllCommand extends BaseRpcCommand {
private final BaseRegion region;
private final BaseRegion region;
public EvictAllCommand(String regionName, BaseRegion region) {
super(regionName); // region name and cache names are the same...
this.region = region;
}
public EvictAllCommand(String regionName, BaseRegion region) {
// region name and cache names are the same...
super( regionName );
this.region = region;
}
public EvictAllCommand(String regionName) {
this(regionName, null);
}
public EvictAllCommand(String regionName) {
this( regionName, null );
}
@Override
public Object perform(InvocationContext ctx) throws Throwable {
region.invalidateRegion();
return null;
}
@Override
public Object perform(InvocationContext ctx) throws Throwable {
region.invalidateRegion();
return null;
}
@Override
public byte getCommandId() {
return CacheCommandIds.EVICT_ALL;
}
@Override
public byte getCommandId() {
return CacheCommandIds.EVICT_ALL;
}
@Override
public Object[] getParameters() {
return new Object[0];
}
@Override
public Object[] getParameters() {
return new Object[0];
}
@Override
public void setParameters(int commandId, Object[] parameters) {
// No-op
}
@Override
public void setParameters(int commandId, Object[] parameters) {
// No-op
}
@Override
public boolean isReturnValueExpected() {
return false;
}
@Override
public boolean isReturnValueExpected() {
return false;
}
}

View File

@ -0,0 +1,4 @@
/**
* Internal utilities for the Infinispan integration
*/
package org.hibernate.cache.infinispan.util;