HHH-10770 JCache 2nd-level cache

* Provider and CacheManager creation
* Caches creation
* Region class hierarchy
* Different access strategies
This commit is contained in:
Alex Snaps 2014-03-19 21:43:42 -04:00 committed by Steve Ebersole
parent 030f442f3c
commit a872885a43
31 changed files with 2045 additions and 0 deletions

View File

@ -0,0 +1,20 @@
dependencies {
compile project( ':hibernate-core' )
compile( libraries.jcache )
testCompile project( ':hibernate-testing' )
testCompile( libraries.mockito )
testRuntime( libraries.ehcache3 )
}
def pomName() {
return 'Hibernate/JCache Integration'
}
def pomDescription() {
return 'Integration for javax.cache into Hibernate as a second-level caching service'
}
def osgiDescription() {
return pomDescription()
}

View File

@ -0,0 +1,45 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
package org.hibernate.cache.jcache;
import javax.cache.Cache;
import org.hibernate.boot.spi.SessionFactoryOptions;
import org.hibernate.cache.CacheException;
import org.hibernate.cache.jcache.access.NonStrictCollectionRegionAccessStrategy;
import org.hibernate.cache.jcache.access.ReadOnlyCollectionRegionAccessStrategy;
import org.hibernate.cache.jcache.access.ReadWriteCollectionRegionAccessStrategy;
import org.hibernate.cache.spi.CacheDataDescription;
import org.hibernate.cache.spi.CollectionRegion;
import org.hibernate.cache.spi.access.AccessType;
import org.hibernate.cache.spi.access.CollectionRegionAccessStrategy;
/**
* @author Alex Snaps
*/
public class JCacheCollectionRegion extends JCacheTransactionalDataRegion implements CollectionRegion {
public JCacheCollectionRegion(Cache<Object, Object> cache, CacheDataDescription metadata, SessionFactoryOptions options) {
super( cache, metadata, options );
}
@Override
public CollectionRegionAccessStrategy buildAccessStrategy(AccessType accessType) throws CacheException {
switch ( accessType ) {
case READ_ONLY:
return new ReadOnlyCollectionRegionAccessStrategy( this );
case NONSTRICT_READ_WRITE:
return new NonStrictCollectionRegionAccessStrategy( this );
case READ_WRITE:
return new ReadWriteCollectionRegionAccessStrategy( this );
case TRANSACTIONAL:
throw new UnsupportedOperationException( "Implement me!" );
default:
throw new UnsupportedOperationException( "Unknown AccessType: " + accessType.name() );
}
}
}

View File

@ -0,0 +1,50 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
package org.hibernate.cache.jcache;
import javax.cache.Cache;
import org.hibernate.boot.spi.SessionFactoryOptions;
import org.hibernate.cache.CacheException;
import org.hibernate.cache.jcache.access.NonStrictEntityRegionAccessStrategy;
import org.hibernate.cache.jcache.access.ReadOnlyEntityRegionAccessStrategy;
import org.hibernate.cache.jcache.access.ReadWriteEntityRegionAccessStrategy;
import org.hibernate.cache.spi.CacheDataDescription;
import org.hibernate.cache.spi.EntityRegion;
import org.hibernate.cache.spi.access.AccessType;
import org.hibernate.cache.spi.access.EntityRegionAccessStrategy;
/**
* @author Alex Snaps
*/
public class JCacheEntityRegion extends JCacheTransactionalDataRegion implements EntityRegion {
public JCacheEntityRegion(Cache<Object, Object> cache, CacheDataDescription metadata, SessionFactoryOptions options) {
super( cache, metadata, options );
}
@Override
public EntityRegionAccessStrategy buildAccessStrategy(AccessType accessType) throws CacheException {
throwIfAccessTypeUnsupported( accessType );
switch ( accessType ) {
case READ_ONLY:
return new ReadOnlyEntityRegionAccessStrategy( this );
case NONSTRICT_READ_WRITE:
return new NonStrictEntityRegionAccessStrategy( this );
case READ_WRITE:
return new ReadWriteEntityRegionAccessStrategy( this );
case TRANSACTIONAL:
return createTransactionalEntityRegionAccessStrategy();
default:
throw new IllegalArgumentException( "Unknown AccessType: " + accessType );
}
}
protected EntityRegionAccessStrategy createTransactionalEntityRegionAccessStrategy() {
throw new UnsupportedOperationException("No org.hibernate.cache.spi.access.AccessType.TRANSACTIONAL support");
}
}

View File

@ -0,0 +1,44 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
package org.hibernate.cache.jcache;
import javax.cache.Cache;
import org.hibernate.cache.CacheException;
import org.hibernate.cache.spi.GeneralDataRegion;
import org.hibernate.engine.spi.SharedSessionContractImplementor;
/**
* @author Alex Snaps
*/
public class JCacheGeneralDataRegion extends JCacheRegion implements GeneralDataRegion {
public JCacheGeneralDataRegion(Cache<Object, Object> cache) {
super( cache );
}
@Override
public Object get(SharedSessionContractImplementor session, Object key) throws CacheException {
return cache.get( key );
}
@Override
public void put(SharedSessionContractImplementor session, Object key, Object value) throws CacheException {
cache.put( key, value );
}
@Override
public void evict(Object key) throws CacheException {
cache.remove( key );
}
@Override
public void evictAll() throws CacheException {
cache.removeAll();
}
}

View File

@ -0,0 +1,39 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
package org.hibernate.cache.jcache;
import org.jboss.logging.annotations.LogMessage;
import org.jboss.logging.annotations.Message;
import org.jboss.logging.annotations.MessageLogger;
import org.hibernate.internal.CoreMessageLogger;
import static org.jboss.logging.Logger.Level.WARN;
/**
* @author Alex Snaps
*/
@MessageLogger(projectCode = "HHH")
public interface JCacheMessageLogger extends CoreMessageLogger {
static final int NAMESPACE = 40000;
@LogMessage(level = WARN)
@Message(
value = "Attempt to restart an already started JCacheRegionFactory. Use sessionFactory.close() between " +
"repeated calls to buildSessionFactory. Using previously created JCacheRegionFactory.",
id = NAMESPACE + 1
)
void attemptToRestartAlreadyStartedJCacheProvider();
@LogMessage(level = WARN)
@Message(
value = "Attempt to restop an already stopped JCacheRegionFactory.",
id = NAMESPACE + 2
)
void attemptToRestopAlreadyStoppedJCacheProvider();
}

View File

@ -0,0 +1,45 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
package org.hibernate.cache.jcache;
import javax.cache.Cache;
import org.hibernate.boot.spi.SessionFactoryOptions;
import org.hibernate.cache.CacheException;
import org.hibernate.cache.jcache.access.NonStrictNaturalIdRegionAccessStrategy;
import org.hibernate.cache.jcache.access.ReadOnlyNaturalIdRegionAccessStrategy;
import org.hibernate.cache.jcache.access.ReadWriteNaturalIdRegionAccessStrategy;
import org.hibernate.cache.spi.CacheDataDescription;
import org.hibernate.cache.spi.NaturalIdRegion;
import org.hibernate.cache.spi.access.AccessType;
import org.hibernate.cache.spi.access.NaturalIdRegionAccessStrategy;
/**
* @author Alex Snaps
*/
public class JCacheNaturalIdRegion extends JCacheTransactionalDataRegion implements NaturalIdRegion {
public JCacheNaturalIdRegion(Cache<Object, Object> cache, CacheDataDescription metadata, SessionFactoryOptions options) {
super( cache, metadata, options );
}
@Override
public NaturalIdRegionAccessStrategy buildAccessStrategy(AccessType accessType) throws CacheException {
switch ( accessType ) {
case READ_ONLY:
return new ReadOnlyNaturalIdRegionAccessStrategy( this );
case NONSTRICT_READ_WRITE:
return new NonStrictNaturalIdRegionAccessStrategy( this );
case READ_WRITE:
return new ReadWriteNaturalIdRegionAccessStrategy( this );
case TRANSACTIONAL:
throw new UnsupportedOperationException( "Implement me!" );
default:
throw new UnsupportedOperationException( "Unknown AccessType: " + accessType.name() );
}
}
}

View File

@ -0,0 +1,22 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
package org.hibernate.cache.jcache;
import javax.cache.Cache;
import org.hibernate.cache.spi.QueryResultsRegion;
/**
* @author Alex Snaps
*/
public class JCacheQueryResultsRegion extends JCacheGeneralDataRegion implements QueryResultsRegion {
public JCacheQueryResultsRegion(Cache<Object, Object> cache) {
super( cache );
}
}

View File

@ -0,0 +1,73 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
package org.hibernate.cache.jcache;
import java.util.HashMap;
import java.util.Map;
import javax.cache.Cache;
import org.hibernate.cache.CacheException;
import org.hibernate.cache.spi.Region;
/**
* @author Alex Snaps
*/
public class JCacheRegion implements Region {
protected final Cache<Object, Object> cache;
public JCacheRegion(Cache<Object, Object> cache) {
if(cache == null) {
throw new NullPointerException("JCacheRegion requires a Cache!");
}
this.cache = cache;
}
public String getName() {
return cache.getName();
}
public void destroy() throws CacheException {
cache.getCacheManager().destroyCache( cache.getName() );
}
public boolean contains(Object key) {
return cache.containsKey( key );
}
public long getSizeInMemory() {
return -1;
}
public long getElementCountInMemory() {
return -1;
}
public long getElementCountOnDisk() {
return -1;
}
public Map toMap() {
final Map<Object, Object> map = new HashMap<Object, Object>();
for ( Cache.Entry<Object, Object> entry : cache ) {
map.put( entry.getKey(), entry.getValue() );
}
return map;
}
public long nextTimestamp() {
return JCacheRegionFactory.nextTS();
}
public int getTimeout() {
return JCacheRegionFactory.timeOut();
}
Cache<Object, Object> getCache() {
return cache;
}
}

View File

@ -0,0 +1,207 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
package org.hibernate.cache.jcache;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.Properties;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import javax.cache.Cache;
import javax.cache.CacheManager;
import javax.cache.Caching;
import javax.cache.configuration.Configuration;
import javax.cache.configuration.MutableConfiguration;
import javax.cache.spi.CachingProvider;
import org.jboss.logging.Logger;
import org.hibernate.boot.spi.SessionFactoryOptions;
import org.hibernate.cache.CacheException;
import org.hibernate.cache.spi.CacheDataDescription;
import org.hibernate.cache.spi.CollectionRegion;
import org.hibernate.cache.spi.EntityRegion;
import org.hibernate.cache.spi.NaturalIdRegion;
import org.hibernate.cache.spi.QueryResultsRegion;
import org.hibernate.cache.spi.RegionFactory;
import org.hibernate.cache.spi.TimestampsRegion;
import org.hibernate.cache.spi.access.AccessType;
/**
* @author Alex Snaps
*/
public class JCacheRegionFactory implements RegionFactory {
private static final String PROP_PREFIX = "hibernate.javax.cache";
public static final String PROVIDER = PROP_PREFIX + ".provider";
public static final String CONFIG_URI = PROP_PREFIX + ".uri";
private static final JCacheMessageLogger LOG = Logger.getMessageLogger(
JCacheMessageLogger.class,
JCacheRegionFactory.class.getName()
);
private final AtomicBoolean started = new AtomicBoolean( false );
private volatile CacheManager cacheManager;
private SessionFactoryOptions options;
@Override
public void start(final SessionFactoryOptions options, final Properties properties) throws CacheException {
if ( started.compareAndSet( false, true ) ) {
synchronized ( this ) {
this.options = options;
try {
final CachingProvider cachingProvider;
final String provider = getProp( properties, PROVIDER );
if ( provider != null ) {
cachingProvider = Caching.getCachingProvider( provider );
}
else {
cachingProvider = Caching.getCachingProvider();
}
final CacheManager cacheManager;
final String cacheManagerUri = getProp( properties, CONFIG_URI );
if ( cacheManagerUri != null ) {
URI uri;
try {
uri = new URI( cacheManagerUri );
}
catch ( URISyntaxException e ) {
throw new CacheException( "Couldn't create URI from " + cacheManagerUri, e );
}
cacheManager = cachingProvider.getCacheManager( uri, cachingProvider.getDefaultClassLoader() );
}
else {
cacheManager = cachingProvider.getCacheManager();
}
this.cacheManager = cacheManager;
}
finally {
if ( this.cacheManager == null ) {
started.set( false );
}
}
}
}
else {
LOG.attemptToRestartAlreadyStartedJCacheProvider();
}
}
@Override
public void stop() {
if ( started.compareAndSet( true, false ) ) {
synchronized ( this ) {
cacheManager.close();
cacheManager = null;
}
}
else {
LOG.attemptToRestopAlreadyStoppedJCacheProvider();
}
}
@Override
public boolean isMinimalPutsEnabledByDefault() {
return true;
}
@Override
public AccessType getDefaultAccessType() {
return AccessType.READ_WRITE;
}
@Override
public long nextTimestamp() {
return nextTS();
}
@Override
public EntityRegion buildEntityRegion(final String regionName, final Properties properties, final CacheDataDescription metadata)
throws CacheException {
final Cache<Object, Object> cache = getOrCreateCache( regionName, properties, metadata );
return new JCacheEntityRegion( cache, metadata, options );
}
@Override
public NaturalIdRegion buildNaturalIdRegion(final String regionName, final Properties properties, final CacheDataDescription metadata)
throws CacheException {
final Cache<Object, Object> cache = getOrCreateCache( regionName, properties, metadata );
return new JCacheNaturalIdRegion( cache, metadata, options );
}
@Override
public CollectionRegion buildCollectionRegion(final String regionName, final Properties properties, final CacheDataDescription metadata)
throws CacheException {
final Cache<Object, Object> cache = getOrCreateCache( regionName, properties, metadata );
return new JCacheCollectionRegion( cache, metadata, options );
}
@Override
public QueryResultsRegion buildQueryResultsRegion(final String regionName, final Properties properties)
throws CacheException {
final Cache<Object, Object> cache = getOrCreateCache( regionName, properties, null );
return new JCacheQueryResultsRegion( cache );
}
@Override
public TimestampsRegion buildTimestampsRegion(final String regionName, final Properties properties)
throws CacheException {
final Cache<Object, Object> cache = getOrCreateCache( regionName, properties, null );
return new JCacheTimestampsRegion( cache );
}
boolean isStarted() {
return started.get() && cacheManager != null;
}
protected Cache<Object, Object> getOrCreateCache(String regionName, Properties properties, CacheDataDescription metadata) {
checkStatus();
final Cache<Object, Object> cache = cacheManager.getCache( regionName );
if ( cache == null ) {
try {
return cacheManager.createCache( regionName, newDefaultConfig( properties, metadata ) );
}
catch ( CacheException e ) {
final Cache<Object, Object> existing = cacheManager.getCache( regionName );
if ( existing != null ) {
return existing;
}
throw e;
}
}
return cache;
}
protected Configuration<Object, Object> newDefaultConfig(Properties properties, CacheDataDescription metadata) {
return new MutableConfiguration<Object, Object>();
}
CacheManager getCacheManager() {
return cacheManager;
}
static long nextTS() {
return System.currentTimeMillis() / 100;
}
static int timeOut() {
return (int) (TimeUnit.SECONDS.toMillis(60) / 100);
}
private String getProp(Properties properties, String prop) {
return properties != null ? properties.getProperty( prop ) : null;
}
private void checkStatus() {
if(!isStarted()) {
throw new IllegalStateException("JCacheRegionFactory not yet started!");
}
}
}

View File

@ -0,0 +1,22 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
package org.hibernate.cache.jcache;
import javax.cache.Cache;
import org.hibernate.cache.spi.TimestampsRegion;
/**
* @author Alex Snaps
*/
public class JCacheTimestampsRegion extends JCacheGeneralDataRegion implements TimestampsRegion {
public JCacheTimestampsRegion(Cache<Object, Object> cache) {
super( cache );
}
}

View File

@ -0,0 +1,78 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
package org.hibernate.cache.jcache;
import java.util.EnumSet;
import java.util.Set;
import javax.cache.Cache;
import javax.cache.processor.EntryProcessor;
import org.hibernate.boot.spi.SessionFactoryOptions;
import org.hibernate.cache.spi.CacheDataDescription;
import org.hibernate.cache.spi.TransactionalDataRegion;
import org.hibernate.cache.spi.access.AccessType;
/**
* @author Alex Snaps
*/
public class JCacheTransactionalDataRegion extends JCacheRegion implements TransactionalDataRegion {
private static final Set<AccessType> SUPPORTED_ACCESS_TYPES
= EnumSet.of( AccessType.READ_ONLY, AccessType.NONSTRICT_READ_WRITE, AccessType.READ_WRITE );
private final CacheDataDescription metadata;
private final SessionFactoryOptions options;
public JCacheTransactionalDataRegion(Cache<Object, Object> cache, CacheDataDescription metadata, SessionFactoryOptions options) {
super( cache );
this.metadata = metadata;
this.options = options;
}
public boolean isTransactionAware() {
return false;
}
public CacheDataDescription getCacheDataDescription() {
return metadata;
}
protected void throwIfAccessTypeUnsupported(AccessType accessType) {
if ( supportedAccessTypes().contains( accessType ) ) {
throw new UnsupportedOperationException( "This doesn't JCacheTransactionalDataRegion doesn't support " + accessType );
}
}
protected Set<AccessType> supportedAccessTypes() {
return SUPPORTED_ACCESS_TYPES;
}
public void clear() {
cache.removeAll();
}
public Object get(Object key) {
return cache.get( key );
}
public void remove(Object key) {
cache.remove( key );
}
public void put(Object key, Object value) {
cache.put( key, value );
}
public SessionFactoryOptions getSessionFactoryOptions() {
return options;
}
public <T> T invoke(Object key, EntryProcessor<Object, Object, T> entryProcessor, Object... args) {
return cache.invoke( key, entryProcessor, args);
}
}

View File

@ -0,0 +1,338 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
package org.hibernate.cache.jcache.access;
import java.io.Serializable;
import java.util.Comparator;
import java.util.UUID;
import java.util.concurrent.atomic.AtomicLong;
import javax.cache.processor.EntryProcessor;
import javax.cache.processor.EntryProcessorException;
import javax.cache.processor.MutableEntry;
import org.hibernate.cache.CacheException;
import org.hibernate.cache.jcache.JCacheTransactionalDataRegion;
import org.hibernate.cache.spi.access.SoftLock;
import org.hibernate.engine.spi.SharedSessionContractImplementor;
/**
* @author Alex Snaps
*/
public class AbstractReadWriteRegionAccessStrategy<R extends JCacheTransactionalDataRegion> {
protected final R region;
protected final Comparator versionComparator;
private final UUID uuid = UUID.randomUUID();
private final AtomicLong nextLockId = new AtomicLong();
public AbstractReadWriteRegionAccessStrategy(R region) {
this.versionComparator = region.getCacheDataDescription().getVersionComparator();
this.region = region;
}
public R getRegion() {
return region;
}
public Object get(SharedSessionContractImplementor session, Object key, long txTimestamp) throws CacheException {
final Lockable item = (Lockable) region.get( key );
final boolean readable = item != null && item.isReadable( txTimestamp );
if ( readable ) {
return item.getValue();
}
else {
return null;
}
}
public boolean putFromLoad(SharedSessionContractImplementor session, Object key, Object value, long txTimestamp, Object version) throws CacheException {
return region.invoke(
key, new EntryProcessor<Object, Object, Boolean>() {
@Override
public Boolean process(MutableEntry<Object, Object> entry, Object... args)
throws EntryProcessorException {
final Lockable item = (Lockable) entry.getValue();
final boolean writeable = item == null || item.isWriteable(
(Long) args[1],
args[2],
versionComparator
);
if ( writeable ) {
entry.setValue( new Item( args[0], args[2], region.nextTimestamp() ) );
return true;
}
else {
return false;
}
}
}, value, txTimestamp, version);
}
public boolean putFromLoad(SharedSessionContractImplementor session, Object key, Object value, long txTimestamp, Object version, boolean minimalPutOverride)
throws CacheException {
return putFromLoad( session, key, value, txTimestamp, version );
}
public SoftLock lockItem(SharedSessionContractImplementor session, Object key, Object version) throws CacheException {
return region.invoke(
key, new EntryProcessor<Object, Object, SoftLock>() {
@Override
public SoftLock process(MutableEntry<Object, Object> entry, Object... args)
throws EntryProcessorException {
final Lockable item = (Lockable) entry.getValue();
final long timeout = region.nextTimestamp() + region.getTimeout();
final Lock lock = ( item == null ) ? new Lock(
timeout,
(UUID) args[0],
(Long) args[1],
args[2]
)
: item.lock( timeout, (UUID) args[0], (Long) args[1] );
entry.setValue( lock );
return lock;
}
}, uuid, nextLockId(), version
);
}
public void unlockItem(SharedSessionContractImplementor session, Object key, SoftLock lock) throws CacheException {
region.invoke(
key, new EntryProcessor<Object, Object, Void>() {
@Override
public Void process(MutableEntry<Object, Object> entry, Object... args)
throws EntryProcessorException {
final Lockable item = (Lockable) entry.getValue();
if ( (item != null) && item.isUnlockable( (SoftLock) args[0] ) ) {
( (Lock) item ).unlock( region.nextTimestamp() );
entry.setValue( item );
}
else {
entry.setValue( null );
}
return null;
}
}, lock);
}
public void remove(SharedSessionContractImplementor session, Object key) throws CacheException {
region.remove( key );
}
public void removeAll() throws CacheException {
region.clear();
}
public void evict(Object key) throws CacheException {
region.remove( key );
}
public void evictAll() throws CacheException {
region.clear();
}
public SoftLock lockRegion() throws CacheException {
return null;
}
public void unlockRegion(SoftLock lock) throws CacheException {
throw new UnsupportedOperationException( "JCache doesn't support region locking" );
}
private long nextLockId() {
return nextLockId.getAndIncrement();
}
/**
* Interface type implemented by all wrapper objects in the cache.
*/
protected static interface Lockable {
/**
* Returns <code>true</code> if the enclosed value can be read by a transaction started at the given time.
*/
public boolean isReadable(long txTimestamp);
/**
* Returns <code>true</code> if the enclosed value can be replaced with one of the given version by a
* transaction started at the given time.
*/
public boolean isWriteable(long txTimestamp, Object version, Comparator versionComparator);
/**
* Returns the enclosed value.
*/
public Object getValue();
/**
* Returns <code>true</code> if the given lock can be unlocked using the given SoftLock instance as a handle.
*/
public boolean isUnlockable(SoftLock lock);
/**
* Locks this entry, stamping it with the UUID and lockId given, with the lock timeout occuring at the specified
* time. The returned Lock object can be used to unlock the entry in the future.
*/
public Lock lock(long timeout, UUID uuid, long lockId);
}
/**
* Wrapper type representing unlocked items.
*/
protected static final class Item implements Serializable, Lockable {
private static final long serialVersionUID = 1L;
private final Object value;
private final Object version;
private final long timestamp;
/**
* Creates an unlocked item wrapping the given value with a version and creation timestamp.
*/
Item(Object value, Object version, long timestamp) {
this.value = value;
this.version = version;
this.timestamp = timestamp;
}
@Override
public boolean isReadable(long txTimestamp) {
return txTimestamp > timestamp;
}
@Override
@SuppressWarnings("unchecked")
public boolean isWriteable(long txTimestamp, Object newVersion, Comparator versionComparator) {
return version != null && versionComparator.compare( version, newVersion ) < 0;
}
@Override
public Object getValue() {
return value;
}
@Override
public boolean isUnlockable(SoftLock lock) {
return false;
}
@Override
public Lock lock(long timeout, UUID uuid, long lockId) {
return new Lock( timeout, uuid, lockId, version );
}
}
/**
* Wrapper type representing locked items.
*/
public static final class Lock implements Serializable, Lockable, SoftLock {
private static final long serialVersionUID = 2L;
private final UUID sourceUuid;
private final long lockId;
private final Object version;
private long timeout;
private boolean concurrent;
private int multiplicity = 1;
private long unlockTimestamp;
/**
* Creates a locked item with the given identifiers and object version.
*/
public Lock(long timeout, UUID sourceUuid, long lockId, Object version) {
this.timeout = timeout;
this.lockId = lockId;
this.version = version;
this.sourceUuid = sourceUuid;
}
@Override
public boolean isReadable(long txTimestamp) {
return false;
}
@Override
@SuppressWarnings({ "SimplifiableIfStatement", "unchecked" })
public boolean isWriteable(long txTimestamp, Object newVersion, Comparator versionComparator) {
if ( txTimestamp > timeout ) {
// if timedout then allow write
return true;
}
if ( multiplicity > 0 ) {
// if still locked then disallow write
return false;
}
return version == null
? txTimestamp > unlockTimestamp
: versionComparator.compare( version, newVersion ) < 0;
}
@Override
public Object getValue() {
return null;
}
@Override
public boolean isUnlockable(SoftLock lock) {
return equals( lock );
}
@Override
@SuppressWarnings("SimplifiableIfStatement")
public boolean equals(Object o) {
if ( o == this ) {
return true;
}
else if ( o instanceof Lock ) {
return ( lockId == ( (Lock) o ).lockId ) && sourceUuid.equals( ( (Lock) o ).sourceUuid );
}
else {
return false;
}
}
@Override
public int hashCode() {
final int hash = ( sourceUuid != null ? sourceUuid.hashCode() : 0 );
int temp = (int) lockId;
for ( int i = 1; i < Long.SIZE / Integer.SIZE; i++ ) {
temp ^= ( lockId >>> ( i * Integer.SIZE ) );
}
return hash + temp;
}
/**
* Returns true if this Lock has been concurrently locked by more than one transaction.
*/
public boolean wasLockedConcurrently() {
return concurrent;
}
@Override
public Lock lock(long timeout, UUID uuid, long lockId) {
concurrent = true;
multiplicity++;
this.timeout = timeout;
return this;
}
/**
* Unlocks this Lock, and timestamps the unlock event.
*/
public void unlock(long timestamp) {
if ( --multiplicity == 0 ) {
unlockTimestamp = timestamp;
}
}
@Override
public String toString() {
return "Lock Source-UUID:" + sourceUuid + " Lock-ID:" + lockId;
}
}
}

View File

@ -0,0 +1,97 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
package org.hibernate.cache.jcache.access;
import org.hibernate.boot.spi.SessionFactoryOptions;
import org.hibernate.cache.CacheException;
import org.hibernate.cache.jcache.JCacheTransactionalDataRegion;
import org.hibernate.cache.spi.access.RegionAccessStrategy;
import org.hibernate.cache.spi.access.SoftLock;
import org.hibernate.engine.spi.SharedSessionContractImplementor;
/**
* @author Alex Snaps
*/
abstract class JCacheRegionAccessStrategy<R extends JCacheTransactionalDataRegion> implements RegionAccessStrategy {
private final R region;
public JCacheRegionAccessStrategy(R region) {
if ( region == null ) {
throw new NullPointerException( "Requires a non-null JCacheTransactionalDataRegion" );
}
this.region = region;
}
@Override
public Object get(SharedSessionContractImplementor session, Object key, long txTimestamp) throws CacheException {
return region.get( key );
}
@Override
public boolean putFromLoad(SharedSessionContractImplementor session, Object key, Object value, long txTimestamp, Object version) throws CacheException {
final SessionFactoryOptions options = region.getSessionFactoryOptions();
final boolean minimalPutOverride = options != null && options.isMinimalPutsEnabled();
return putFromLoad( session, key, value, txTimestamp, version, minimalPutOverride );
}
@Override
public boolean putFromLoad(SharedSessionContractImplementor session, Object key, Object value, long txTimestamp, Object version, boolean minimalPutOverride)
throws CacheException {
if ( minimalPutOverride && region.contains( key ) ) {
return false;
}
else {
region.put( key, value );
return true;
}
}
@Override
public SoftLock lockItem(SharedSessionContractImplementor session, Object key, Object version) throws CacheException {
return null;
}
@Override
public SoftLock lockRegion() throws CacheException {
return null;
}
@Override
public void unlockItem(SharedSessionContractImplementor session, Object key, SoftLock lock) throws CacheException {
evict( key );
}
@Override
public void unlockRegion(SoftLock lock) throws CacheException {
// no op
}
@Override
public void remove(SharedSessionContractImplementor session, Object key) throws CacheException {
region.remove( key );
}
@Override
public void removeAll() throws CacheException {
region.clear();
}
@Override
public void evict(Object key) throws CacheException {
region.remove( key );
}
@Override
public void evictAll() throws CacheException {
region.clear();
}
public R getRegion() {
return region;
}
}

View File

@ -0,0 +1,37 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
package org.hibernate.cache.jcache.access;
import org.hibernate.cache.internal.DefaultCacheKeysFactory;
import org.hibernate.cache.jcache.JCacheCollectionRegion;
import org.hibernate.cache.spi.access.CollectionRegionAccessStrategy;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.persister.collection.CollectionPersister;
/**
* @author Alex Snaps
*/
public class NonStrictCollectionRegionAccessStrategy
extends JCacheRegionAccessStrategy<JCacheCollectionRegion>
implements CollectionRegionAccessStrategy {
public NonStrictCollectionRegionAccessStrategy(JCacheCollectionRegion jCacheCollectionRegion) {
super( jCacheCollectionRegion );
}
@Override
public Object generateCacheKey(Object id, CollectionPersister persister, SessionFactoryImplementor factory, String tenantIdentifier) {
return DefaultCacheKeysFactory.createCollectionKey( id, persister, factory, tenantIdentifier );
}
@Override
public Object getCacheKeyId(Object cacheKey) {
return DefaultCacheKeysFactory.getCollectionId( cacheKey );
}
}

View File

@ -0,0 +1,63 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
package org.hibernate.cache.jcache.access;
import org.hibernate.cache.CacheException;
import org.hibernate.cache.internal.DefaultCacheKeysFactory;
import org.hibernate.cache.jcache.JCacheEntityRegion;
import org.hibernate.cache.spi.access.EntityRegionAccessStrategy;
import org.hibernate.cache.spi.access.SoftLock;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.persister.entity.EntityPersister;
/**
* @author Alex Snaps
*/
public class NonStrictEntityRegionAccessStrategy extends JCacheRegionAccessStrategy<JCacheEntityRegion>
implements EntityRegionAccessStrategy {
public NonStrictEntityRegionAccessStrategy(JCacheEntityRegion jCacheEntityRegion) {
super( jCacheEntityRegion );
}
@Override
public boolean insert(SharedSessionContractImplementor session, Object key, Object value, Object version) throws CacheException {
return false;
}
@Override
public boolean afterInsert(SharedSessionContractImplementor session, Object key, Object value, Object version) throws CacheException {
getRegion().put( key, value );
return true;
}
@Override
public boolean update(SharedSessionContractImplementor session, Object key, Object value, Object currentVersion, Object previousVersion)
throws CacheException {
getRegion().remove( key );
return false;
}
@Override
public boolean afterUpdate(SharedSessionContractImplementor session, Object key, Object value, Object currentVersion, Object previousVersion, SoftLock lock)
throws CacheException {
getRegion().remove( key );
return false;
}
@Override
public Object generateCacheKey(Object id, EntityPersister persister, SessionFactoryImplementor factory, String tenantIdentifier) {
return DefaultCacheKeysFactory.createEntityKey( id, persister, factory, tenantIdentifier );
}
@Override
public Object getCacheKeyId(Object cacheKey) {
return DefaultCacheKeysFactory.getEntityId( cacheKey );
}
}

View File

@ -0,0 +1,60 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
package org.hibernate.cache.jcache.access;
import org.hibernate.cache.CacheException;
import org.hibernate.cache.internal.DefaultCacheKeysFactory;
import org.hibernate.cache.jcache.JCacheNaturalIdRegion;
import org.hibernate.cache.spi.access.NaturalIdRegionAccessStrategy;
import org.hibernate.cache.spi.access.SoftLock;
import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.persister.entity.EntityPersister;
/**
* @author Alex Snaps
*/
public class NonStrictNaturalIdRegionAccessStrategy
extends JCacheRegionAccessStrategy<JCacheNaturalIdRegion>
implements NaturalIdRegionAccessStrategy {
public NonStrictNaturalIdRegionAccessStrategy(JCacheNaturalIdRegion jCacheNaturalIdRegion) {
super( jCacheNaturalIdRegion );
}
@Override
public boolean insert(SharedSessionContractImplementor session, Object key, Object value) throws CacheException {
return false;
}
@Override
public boolean afterInsert(SharedSessionContractImplementor session, Object key, Object value) throws CacheException {
return false;
}
@Override
public boolean update(SharedSessionContractImplementor session, Object key, Object value) throws CacheException {
remove( session, key );
return false;
}
@Override
public boolean afterUpdate(SharedSessionContractImplementor session, Object key, Object value, SoftLock lock) throws CacheException {
unlockItem( session, key, lock );
return false;
}
@Override
public Object generateCacheKey(Object[] naturalIdValues, EntityPersister persister, SharedSessionContractImplementor session) {
return DefaultCacheKeysFactory.createNaturalIdKey( naturalIdValues, persister, session );
}
@Override
public Object[] getNaturalIdValues(Object cacheKey) {
return DefaultCacheKeysFactory.getNaturalIdValues( cacheKey );
}
}

View File

@ -0,0 +1,36 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
package org.hibernate.cache.jcache.access;
import org.hibernate.cache.internal.DefaultCacheKeysFactory;
import org.hibernate.cache.jcache.JCacheCollectionRegion;
import org.hibernate.cache.spi.access.CollectionRegionAccessStrategy;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.persister.collection.CollectionPersister;
/**
* @author Alex Snaps
*/
public class ReadOnlyCollectionRegionAccessStrategy
extends JCacheRegionAccessStrategy<JCacheCollectionRegion>
implements CollectionRegionAccessStrategy {
public ReadOnlyCollectionRegionAccessStrategy(JCacheCollectionRegion jCacheCollectionRegion) {
super( jCacheCollectionRegion );
}
@Override
public Object generateCacheKey(Object id, CollectionPersister persister, SessionFactoryImplementor factory, String tenantIdentifier) {
return DefaultCacheKeysFactory.createCollectionKey( id, persister, factory, tenantIdentifier );
}
@Override
public Object getCacheKeyId(Object cacheKey) {
return DefaultCacheKeysFactory.getCollectionId( cacheKey );
}
}

View File

@ -0,0 +1,60 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
package org.hibernate.cache.jcache.access;
import org.hibernate.cache.CacheException;
import org.hibernate.cache.internal.DefaultCacheKeysFactory;
import org.hibernate.cache.jcache.JCacheEntityRegion;
import org.hibernate.cache.spi.access.EntityRegionAccessStrategy;
import org.hibernate.cache.spi.access.SoftLock;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.persister.entity.EntityPersister;
/**
* @author Alex Snaps
*/
public class ReadOnlyEntityRegionAccessStrategy extends JCacheRegionAccessStrategy<JCacheEntityRegion>
implements EntityRegionAccessStrategy {
public ReadOnlyEntityRegionAccessStrategy(JCacheEntityRegion jCacheEntityRegion) {
super( jCacheEntityRegion );
}
@Override
public boolean insert(SharedSessionContractImplementor session, Object key, Object value, Object version) throws CacheException {
return false;
}
@Override
public boolean afterInsert(SharedSessionContractImplementor session, Object key, Object value, Object version) throws CacheException {
getRegion().put( key, value );
return true;
}
@Override
public boolean update(SharedSessionContractImplementor session, Object key, Object value, Object currentVersion, Object previousVersion)
throws CacheException {
throw new UnsupportedOperationException( "This is a ReadOnly strategy!" );
}
@Override
public boolean afterUpdate(SharedSessionContractImplementor session, Object key, Object value, Object currentVersion, Object previousVersion, SoftLock lock)
throws CacheException {
throw new UnsupportedOperationException( "This is a ReadOnly strategy!" );
}
@Override
public Object generateCacheKey(Object id, EntityPersister persister, SessionFactoryImplementor factory, String tenantIdentifier) {
return DefaultCacheKeysFactory.createEntityKey( id, persister, factory, tenantIdentifier );
}
@Override
public Object getCacheKeyId(Object cacheKey) {
return DefaultCacheKeysFactory.getEntityId( cacheKey );
}
}

View File

@ -0,0 +1,59 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
package org.hibernate.cache.jcache.access;
import org.hibernate.cache.CacheException;
import org.hibernate.cache.internal.DefaultCacheKeysFactory;
import org.hibernate.cache.jcache.JCacheNaturalIdRegion;
import org.hibernate.cache.spi.access.NaturalIdRegionAccessStrategy;
import org.hibernate.cache.spi.access.SoftLock;
import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.persister.entity.EntityPersister;
/**
* @author Alex Snaps
*/
public class ReadOnlyNaturalIdRegionAccessStrategy
extends JCacheRegionAccessStrategy<JCacheNaturalIdRegion>
implements NaturalIdRegionAccessStrategy {
public ReadOnlyNaturalIdRegionAccessStrategy(JCacheNaturalIdRegion jCacheNaturalIdRegion) {
super( jCacheNaturalIdRegion );
}
@Override
public boolean insert(SharedSessionContractImplementor session, Object key, Object value) throws CacheException {
return false;
}
@Override
public boolean afterInsert(SharedSessionContractImplementor session, Object key, Object value) throws CacheException {
getRegion().put( key, value );
return true;
}
@Override
public boolean update(SharedSessionContractImplementor session, Object key, Object value) throws CacheException {
throw new UnsupportedOperationException( "This is a ReadOnly strategy!" );
}
@Override
public boolean afterUpdate(SharedSessionContractImplementor session, Object key, Object value, SoftLock lock) throws CacheException {
throw new UnsupportedOperationException( "This is a ReadOnly strategy!" );
}
@Override
public Object generateCacheKey(Object[] naturalIdValues, EntityPersister persister, SharedSessionContractImplementor session) {
return DefaultCacheKeysFactory.createNaturalIdKey( naturalIdValues, persister, session );
}
@Override
public Object[] getNaturalIdValues(Object cacheKey) {
return DefaultCacheKeysFactory.getNaturalIdValues( cacheKey );
}
}

View File

@ -0,0 +1,37 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
package org.hibernate.cache.jcache.access;
import org.hibernate.cache.internal.DefaultCacheKeysFactory;
import org.hibernate.cache.jcache.JCacheCollectionRegion;
import org.hibernate.cache.spi.access.CollectionRegionAccessStrategy;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.persister.collection.CollectionPersister;
/**
* @author Alex Snaps
*/
public class ReadWriteCollectionRegionAccessStrategy
extends AbstractReadWriteRegionAccessStrategy<JCacheCollectionRegion>
implements CollectionRegionAccessStrategy {
public ReadWriteCollectionRegionAccessStrategy(JCacheCollectionRegion jCacheCollectionRegion) {
super( jCacheCollectionRegion );
}
@Override
public Object generateCacheKey(Object id, CollectionPersister persister, SessionFactoryImplementor factory, String tenantIdentifier) {
return DefaultCacheKeysFactory.createCollectionKey( id, persister, factory, tenantIdentifier );
}
@Override
public Object getCacheKeyId(Object cacheKey) {
return DefaultCacheKeysFactory.getCollectionId( cacheKey );
}
}

View File

@ -0,0 +1,105 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
package org.hibernate.cache.jcache.access;
import javax.cache.processor.EntryProcessor;
import javax.cache.processor.EntryProcessorException;
import javax.cache.processor.MutableEntry;
import org.hibernate.cache.CacheException;
import org.hibernate.cache.internal.DefaultCacheKeysFactory;
import org.hibernate.cache.jcache.JCacheEntityRegion;
import org.hibernate.cache.spi.access.EntityRegionAccessStrategy;
import org.hibernate.cache.spi.access.SoftLock;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.persister.entity.EntityPersister;
/**
* @author Alex Snaps
*/
public class ReadWriteEntityRegionAccessStrategy
extends AbstractReadWriteRegionAccessStrategy<JCacheEntityRegion>
implements EntityRegionAccessStrategy {
public ReadWriteEntityRegionAccessStrategy(JCacheEntityRegion region) {
super( region );
}
@Override
public boolean insert(SharedSessionContractImplementor session, Object key, Object value, Object version) throws CacheException {
return false;
}
@Override
public boolean afterInsert(SharedSessionContractImplementor session, Object key, Object value, Object version) throws CacheException {
return region.invoke(
key, new EntryProcessor<Object, Object, Boolean>() {
@Override
public Boolean process(MutableEntry<Object, Object> entry, Object... args)
throws EntryProcessorException {
if ( !entry.exists() ) {
entry.setValue( new Item( args[0], args[1], (Long) args[2] ) );
return true;
}
else {
return false;
}
}
}, value, version, region.nextTimestamp()
);
}
@Override
public boolean update(SharedSessionContractImplementor session, Object key, Object value, Object currentVersion, Object previousVersion)
throws CacheException {
return false;
}
@Override
public boolean afterUpdate(SharedSessionContractImplementor session, Object key, Object value, Object currentVersion, Object previousVersion, SoftLock lock)
throws CacheException {
return region.invoke(
key, new EntryProcessor<Object, Object, Boolean>() {
@Override
public Boolean process(MutableEntry<Object, Object> entry, Object... args)
throws EntryProcessorException {
final Lockable item = (Lockable) entry.getValue();
if ( item != null && item.isUnlockable( (SoftLock) args[3] ) ) {
final Lock lockItem = (Lock) item;
if ( lockItem.wasLockedConcurrently() ) {
lockItem.unlock( (Long) args[1] );
entry.setValue( lockItem );
return false;
}
else {
entry.setValue( new Item( args[0], args[1], (Long) args[4] ) );
return true;
}
}
else {
entry.setValue( null );
return false;
}
}
}, value, currentVersion, previousVersion, lock, region.nextTimestamp()
);
}
@Override
public Object generateCacheKey(Object id, EntityPersister persister, SessionFactoryImplementor factory, String tenantIdentifier) {
return DefaultCacheKeysFactory.createEntityKey( id, persister, factory, tenantIdentifier );
}
@Override
public Object getCacheKeyId(Object cacheKey) {
return DefaultCacheKeysFactory.getEntityId( cacheKey );
}
}

View File

@ -0,0 +1,104 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
package org.hibernate.cache.jcache.access;
import javax.cache.processor.EntryProcessor;
import javax.cache.processor.EntryProcessorException;
import javax.cache.processor.MutableEntry;
import org.hibernate.cache.CacheException;
import org.hibernate.cache.internal.DefaultCacheKeysFactory;
import org.hibernate.cache.jcache.JCacheNaturalIdRegion;
import org.hibernate.cache.spi.access.NaturalIdRegionAccessStrategy;
import org.hibernate.cache.spi.access.SoftLock;
import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.persister.entity.EntityPersister;
/**
* @author Alex Snaps
*/
public class ReadWriteNaturalIdRegionAccessStrategy
extends AbstractReadWriteRegionAccessStrategy<JCacheNaturalIdRegion>
implements NaturalIdRegionAccessStrategy {
public ReadWriteNaturalIdRegionAccessStrategy(JCacheNaturalIdRegion jCacheNaturalIdRegion) {
super ( jCacheNaturalIdRegion );
}
@Override
public boolean insert(SharedSessionContractImplementor session, Object key, Object value) throws CacheException {
return false;
}
@Override
public boolean afterInsert(SharedSessionContractImplementor session, Object key, Object value) throws CacheException {
return region.invoke(
key, new EntryProcessor<Object, Object, Boolean>() {
@Override
public Boolean process(MutableEntry<Object, Object> entry, Object... args)
throws EntryProcessorException {
if ( !entry.exists() ) {
entry.setValue( new Item( args[0], null, (Long) args[1] ) );
return true;
}
else {
return false;
}
}
}, value, region.nextTimestamp()
);
}
@Override
public boolean update(SharedSessionContractImplementor session, Object key, Object value)
throws CacheException {
return false;
}
@Override
public boolean afterUpdate(SharedSessionContractImplementor session, Object key, Object value, SoftLock lock)
throws CacheException {
return region.invoke(
key, new EntryProcessor<Object, Object, Boolean>() {
@Override
public Boolean process(MutableEntry<Object, Object> entry, Object... args)
throws EntryProcessorException {
final Lockable item = (Lockable) entry.getValue();
if ( item != null && item.isUnlockable( (SoftLock) args[1] ) ) {
final Lock lockItem = (Lock) item;
if ( lockItem.wasLockedConcurrently() ) {
lockItem.unlock( region.nextTimestamp() );
entry.setValue( lockItem );
return false;
}
else {
entry.setValue( new Item( args[0], null, (Long) args[2] ) );
return true;
}
}
else {
entry.setValue( null );
return false;
}
}
}, value, lock, region.nextTimestamp()
);
}
@Override
public Object generateCacheKey(Object[] naturalIdValues, EntityPersister persister, SharedSessionContractImplementor session) {
return DefaultCacheKeysFactory.createNaturalIdKey( naturalIdValues, persister, session );
}
@Override
public Object[] getNaturalIdValues(Object cacheKey) {
return DefaultCacheKeysFactory.getNaturalIdValues( cacheKey );
}
}

View File

@ -0,0 +1,11 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
/**
* Access strategies for the JSR-107 Hibernate caching provider.
*/
package org.hibernate.cache.jcache.access;

View File

@ -0,0 +1,11 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
/**
* Hibernate caching provider for JSR-107 compliant caches.
*/
package org.hibernate.cache.jcache;

View File

@ -0,0 +1,34 @@
package org.hibernate.cache.jcache;
import javax.cache.Cache;
import org.junit.Before;
import org.junit.Test;
import org.mockito.Mockito;
import org.hibernate.cache.spi.access.AccessType;
import org.hibernate.cfg.Settings;
/**
* @author Alex Snaps
*/
public class JCacheEntityRegionTest {
private JCacheEntityRegion region;
@Before
public void createRegion() {
final Cache<Object, Object> cache = Mockito.mock( Cache.class );
region = new JCacheEntityRegion( cache, null, null );
}
@Test(expected = UnsupportedOperationException.class)
public void testThrowsWhenCreatingTxRegionAccess() {
region.buildAccessStrategy( AccessType.TRANSACTIONAL );
}
@Test(expected = UnsupportedOperationException.class)
public void testThrowsWhenCreatingTxRegionAccessExplicitly() {
region.createTransactionalEntityRegionAccessStrategy();
}
}

View File

@ -0,0 +1,54 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
package org.hibernate.cache.jcache;
import javax.cache.Cache;
import org.hibernate.engine.spi.SessionImplementor;
import org.junit.Before;
import org.junit.Test;
import org.mockito.Mockito;
import static org.mockito.Mockito.verify;
/**
* @author Alex Snaps
*/
public class JCacheGeneralDataRegionTest {
JCacheGeneralDataRegion region;
@Before
public void createRegion() {
final Cache<Object, Object> mock = Mockito.mock( Cache.class );
region = new JCacheGeneralDataRegion( mock );
}
@Test
public void testDelegatesGetToCache() {
region.get( Mockito.mock(SessionImplementor.class), "foo" );
verify( region.getCache() ).get( "foo" );
}
@Test
public void testDelegatesPutToCache() {
region.put( Mockito.mock(SessionImplementor.class), "foo", "bar" );
verify( region.getCache() ).put( "foo", "bar" );
}
@Test
public void testDelegatesEvictKeyToCache() {
region.evict( "foo" );
verify( region.getCache() ).remove( "foo" );
}
@Test
public void testDelegatesEvictAllToCache() {
region.evictAll();
verify( region.getCache() ).removeAll();
}
}

View File

@ -0,0 +1,132 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
package org.hibernate.cache.jcache;
import java.util.Properties;
import javax.cache.Cache;
import javax.cache.CacheManager;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.hibernate.cache.CacheException;
import org.hibernate.cache.spi.access.AccessType;
import static java.util.Collections.EMPTY_MAP;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.CoreMatchers.notNullValue;
import static org.hamcrest.CoreMatchers.nullValue;
import static org.hamcrest.CoreMatchers.sameInstance;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.fail;
/**
* @author Alex Snaps
*/
public class JCacheRegionFactoryTest {
private JCacheRegionFactory factory;
@Before
public void createFactory() {
factory = new JCacheRegionFactory();
}
@Test
public void testIsInitiallyNotStarted() {
assertThat( factory.isStarted(), is( false ) );
assertThat( factory.getCacheManager(), nullValue() );
}
@Test
public void testMaintainsStartedFlag() {
factory.start( null, EMPTY_MAP );
assertThat( factory.isStarted(), is( true ) );
assertThat( factory.getCacheManager(), notNullValue() );
factory.stop();
assertThat( factory.isStarted(), is( false ) );
assertThat( factory.getCacheManager(), nullValue() );
}
@Test(expected = javax.cache.CacheException.class)
public void testFailsOnNotFindingProvider() {
final Properties properties = new Properties();
properties.setProperty( JCacheRegionFactory.PROVIDER, "no.such.thing" );
factory.start( null, properties );
}
@Test(expected = CacheException.class)
public void testFailsOnInvalidURI() {
final Properties properties = new Properties();
properties.setProperty( JCacheRegionFactory.CONFIG_URI, "_fil:" );
factory.start( null, properties );
}
@Test
public void testDefaultAccessIsReadWrite() {
assertThat( factory.getDefaultAccessType(), is( AccessType.READ_WRITE ) );
}
@Test
public void testUsesMinimalPutsAsDefault() {
assertThat( factory.isMinimalPutsEnabledByDefault(), is( true ) );
}
@Test
public void testRemainsStoppedOnFailure() {
final Properties properties = new Properties();
properties.setProperty( JCacheRegionFactory.CONFIG_URI, "_fil:" );
try {
factory.start( null, properties );
fail();
}
catch ( CacheException e ) {
assertThat( factory.isStarted(), is( false ) );
}
properties.setProperty( JCacheRegionFactory.PROVIDER, "no.such.thing" );
try {
factory.start( null, properties );
fail();
}
catch ( javax.cache.CacheException e ) {
assertThat( factory.isStarted(), is( false ) );
}
}
@Test
public void testStopsCacheManagerOnShutdown() {
factory.start( null, EMPTY_MAP );
final CacheManager cacheManager = factory.getCacheManager();
assertThat( cacheManager.isClosed(), is( false ) );
factory.stop();
assertThat(cacheManager.isClosed(), is(true));
}
@Test(expected = IllegalStateException.class)
public void testThrowsIllegalStateExceptionWhenNotStarted() {
factory.getOrCreateCache( "foo", null, null );
}
@Test
public void testCreatesNonExistingCacheNamedLikeRegion() {
factory.start( null, EMPTY_MAP );
final Cache<Object, Object> foo = factory.getOrCreateCache( "foo", null, null );
assertThat( foo, notNullValue());
assertThat( factory.getCacheManager().getCache( "foo" ), sameInstance( foo ));
assertThat( factory.getOrCreateCache( "foo", null, null ), sameInstance( foo ));
}
@After
public void stopFactory() {
if ( factory.isStarted() ) {
factory.stop();
}
}
}

View File

@ -0,0 +1,88 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
package org.hibernate.cache.jcache;
import java.util.Iterator;
import java.util.Map;
import javax.cache.Cache;
import javax.cache.CacheManager;
import org.junit.Before;
import org.junit.Test;
import org.mockito.Mockito;
import static org.hamcrest.CoreMatchers.equalTo;
import static org.hamcrest.CoreMatchers.is;
import static org.junit.Assert.assertThat;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
/**
* @author Alex Snaps
*/
public class JCacheRegionTest {
public static final String CACHE_NAME = "foo";
private JCacheRegion region;
@Before
public void createRegion() {
final Cache mock = Mockito.mock( Cache.class );
when( mock.getName() ).thenReturn( CACHE_NAME );
this.region = new JCacheRegion( mock );
}
@Test
public void testDestroyCallsDestroyCacheOnCacheManager() {
final Cache<Object, Object> cache = region.getCache();
CacheManager cacheManager = Mockito.mock( CacheManager.class );
when( cache.getCacheManager() ).thenReturn( cacheManager );
when( cacheManager.getCache( CACHE_NAME ) ).thenReturn( cache );
region.destroy();
verify( cacheManager ).destroyCache( CACHE_NAME );
}
@Test
public void testDelegatesGetNameToCache() {
assertThat( region.getName(), is( CACHE_NAME ) );
}
@Test
public void testDelegatesContainsToCache() {
final Cache<Object, Object> cache = region.getCache();
region.contains( "bar" );
verify( cache ).containsKey( "bar" );
}
@Test
public void testSupportsToMap() {
final Cache<Object, Object> cache = region.getCache();
final Iterator mock = Mockito.mock( Iterator.class );
when( mock.hasNext() ).thenReturn( true ).thenReturn( false );
when( mock.next() ).thenReturn( new Cache.Entry<Object, Object>() {
@Override
public Object getKey() {
return "foo";
}
@Override
public Object getValue() {
return "bar";
}
@Override
public <T> T unwrap(Class<T> clazz) {
throw new UnsupportedOperationException( "Implement me!" );
}
} );
when( cache.iterator() ).thenReturn( mock );
final Map<String, String> map = region.toMap();
assertThat( map.size(), is(1) );
assertThat( map.get( "foo" ), equalTo( "bar" ));
}
}

View File

@ -0,0 +1,71 @@
package org.hibernate.cache.jcache;
import javax.cache.Cache;
import org.junit.Before;
import org.junit.Test;
import org.mockito.Mockito;
import org.hibernate.cache.spi.access.AccessType;
import static org.hamcrest.CoreMatchers.is;
import static org.junit.Assert.assertThat;
import static org.mockito.Mockito.verify;
/**
* @author Alex Snaps
*/
public class JCacheTransactionalDataRegionTest {
private JCacheTransactionalDataRegion region;
@Before
public void createRegion() {
final Cache<Object, Object> cache = Mockito.mock( Cache.class );
region = new JCacheTransactionalDataRegion( cache, null, null );
}
@Test(expected = NullPointerException.class)
public void testThrowsOnNullCache() {
new JCacheTransactionalDataRegion( null, null, null );
}
@Test
public void testIsNotTransactionAware() {
assertThat( region.isTransactionAware(), is( false ) );
}
@Test
public void testDelegatesClearToCache() {
final Cache<Object, Object> cache = region.getCache();
region.clear();
verify( cache ).removeAll();
}
@Test
public void testDelegatesGetToCache() {
final Cache<Object, Object> cache = region.getCache();
region.get( "foo" );
verify( cache ).get( "foo" );
}
@Test
public void testSupportsAllAccessTypesButTx() {
for ( AccessType type : AccessType.values() ) {
if ( type != AccessType.TRANSACTIONAL ) {
assertThat(
"JCacheTransactionalDataRegion should support " + type,
region.supportedAccessTypes().contains( type ),
is( true )
);
}
else {
assertThat(
"JCacheTransactionalDataRegion NOT should support " + type,
region.supportedAccessTypes().contains( type ),
is( false )
);
}
}
}
}

View File

@ -110,6 +110,8 @@ ext {
c3p0: "com.mchange:c3p0:0.9.2.1",
ehcache: "net.sf.ehcache:ehcache:2.10.1",
ehcache3: "org.ehcache:ehcache:3.0.0",
jcache: "javax.cache:cache-api:1.0.0",
proxool: "proxool:proxool:0.8.3",
hikaricp: "com.zaxxer:HikariCP-java6:2.3.9"

View File

@ -19,6 +19,7 @@ include 'hibernate-c3p0'
include 'hibernate-proxool'
include 'hibernate-hikaricp'
include 'hibernate-jcache'
include 'hibernate-ehcache'
include 'hibernate-infinispan'