All Downloads are FREE. Search and download functionalities are using the official Maven repository.
Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
org.infinispan.spring.common.provider.SpringCache Maven / Gradle / Ivy
package org.infinispan.spring.common.provider;
import java.util.Map;
import java.util.concurrent.Callable;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.concurrent.locks.ReentrantLock;
import org.infinispan.commons.CacheException;
import org.infinispan.commons.api.BasicCache;
import org.infinispan.commons.util.NullValue;
import org.springframework.cache.Cache;
import org.springframework.cache.support.SimpleValueWrapper;
import org.springframework.util.Assert;
/**
*
* A {@link Cache Cache
} implementation that delegates to a
* {@link org.infinispan.Cache org.infinispan.Cache
} instance supplied at construction
* time.
*
*
* @author Olaf Bergner
* @author Marius Bogoevici
*/
public class SpringCache implements Cache {
public static final SimpleValueWrapper NULL_VALUE_WRAPPER = new SimpleValueWrapper(null);
private final BasicCache nativeCache;
private final long readTimeout;
private final long writeTimeout;
private final Map synchronousGetLocks = new ConcurrentHashMap<>();
/**
* @param nativeCache underlying cache
*/
public SpringCache(BasicCache nativeCache) {
this(nativeCache, 0, 0);
}
public SpringCache(BasicCache nativeCache, long readTimeout, long writeTimeout) {
Assert.notNull(nativeCache, "A non-null Infinispan cache implementation is required");
this.nativeCache = nativeCache;
this.readTimeout = readTimeout;
this.writeTimeout = writeTimeout;
}
/**
* @see Cache#getName()
*/
@Override
public String getName() {
return this.nativeCache.getName();
}
/**
* @see Cache#getNativeCache()
*/
@Override
public BasicCache, ?> getNativeCache() {
return this.nativeCache;
}
/**
* @see Cache#get(Object)
*/
@Override
public ValueWrapper get(final Object key) {
try {
if (readTimeout > 0)
return encodedToValueWrapper(nativeCache.getAsync(key).get(readTimeout, TimeUnit.MILLISECONDS));
else
return encodedToValueWrapper(nativeCache.get(key));
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
throw new CacheException(e);
} catch (ExecutionException | TimeoutException e) {
throw new CacheException(e);
}
}
@Override
public T get(Object key, Class type) {
try {
Object value;
if (readTimeout > 0)
value = nativeCache.getAsync(key).get(readTimeout, TimeUnit.MILLISECONDS);
else
value = nativeCache.get(key);
value = decodeNull(value);
if (value != null && type != null && !type.isInstance(value)) {
throw new IllegalStateException("Cached value is not of required type [" + type.getName() + "]: " + value);
}
return (T) value;
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
throw new CacheException(e);
} catch (ExecutionException | TimeoutException e) {
throw new CacheException(e);
}
}
@Override
public T get(Object key, Callable valueLoader) {
ReentrantLock lock;
T value = (T) nativeCache.get(key);
if (value == null) {
lock = synchronousGetLocks.computeIfAbsent(key, k -> new ReentrantLock());
lock.lock();
try {
if ((value = (T) nativeCache.get(key)) == null) {
try {
T newValue = valueLoader.call();
// we can't use computeIfAbsent here since in distributed embedded scenario we would
// send a lambda to other nodes. This is the behavior we want to avoid.
value = (T) nativeCache.putIfAbsent(key, encodeNull(newValue));
if (value == null) {
value = newValue;
}
} catch (Exception e) {
throw ValueRetrievalExceptionResolver.throwValueRetrievalException(key, valueLoader, e);
}
}
} finally {
lock.unlock();
synchronousGetLocks.remove(key);
}
}
return decodeNull(value);
}
/**
* @see Cache#put(Object, Object)
*/
@Override
public void put(final Object key, final Object value) {
try {
if (writeTimeout > 0)
this.nativeCache.putAsync(key, encodeNull(value)).get(writeTimeout, TimeUnit.MILLISECONDS);
else
this.nativeCache.put(key, encodeNull(value));
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
throw new CacheException(e);
} catch (ExecutionException | TimeoutException e) {
throw new CacheException(e);
}
}
/**
* @see BasicCache#put(Object, Object, long, TimeUnit)
*/
public void put(Object key, Object value, long lifespan, TimeUnit unit) {
try {
if (writeTimeout > 0)
this.nativeCache.putAsync(key, encodeNull(value), lifespan, unit).get(writeTimeout, TimeUnit.MILLISECONDS);
else
this.nativeCache.put(key, encodeNull(value), lifespan, unit);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
throw new CacheException(e);
} catch (ExecutionException | TimeoutException e) {
throw new CacheException(e);
}
}
@Override
public ValueWrapper putIfAbsent(Object key, Object value) {
try {
if (writeTimeout > 0)
return encodedToValueWrapper(this.nativeCache.putIfAbsentAsync(key, encodeNull(value)).get(writeTimeout, TimeUnit.MILLISECONDS));
else
return encodedToValueWrapper(this.nativeCache.putIfAbsent(key, encodeNull(value)));
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
throw new CacheException(e);
} catch (ExecutionException | TimeoutException e) {
throw new CacheException(e);
}
}
/**
* @see Cache#evict(Object)
*/
@Override
public void evict(final Object key) {
try {
if (writeTimeout > 0)
this.nativeCache.removeAsync(key).get(writeTimeout, TimeUnit.MILLISECONDS);
else
this.nativeCache.remove(key);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
throw new CacheException(e);
} catch (ExecutionException | TimeoutException e) {
throw new CacheException(e);
}
}
/**
* @see Cache#clear()
*/
@Override
public void clear() {
try {
if (writeTimeout > 0)
this.nativeCache.clearAsync().get(writeTimeout, TimeUnit.MILLISECONDS);
else
this.nativeCache.clear();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
throw new CacheException(e);
} catch (ExecutionException | TimeoutException e) {
throw new CacheException(e);
}
}
/**
* @see Object#toString()
*/
@Override
public String toString() {
return "InfinispanCache [nativeCache = " + this.nativeCache + "]";
}
private ValueWrapper encodedToValueWrapper(Object value) {
if (value == null) {
return null;
}
if (value == NullValue.NULL) {
return NULL_VALUE_WRAPPER;
}
return new SimpleValueWrapper(value);
}
private Object encodeNull(Object value) {
return value != null ? value : NullValue.NULL;
}
private T decodeNull(Object value) {
return value != NullValue.NULL ? (T) value : null;
}
//Implemented as a static holder class for backwards compatibility.
//Imagine a situation where a client has new integration module and old Spring version. In that case
//this exception does not exist. However we can bypass this by using separate class file (which is loaded
//by the JVM when needed...)
private static class ValueRetrievalExceptionResolver {
static RuntimeException throwValueRetrievalException(Object key, Callable> loader, Throwable ex) {
return new ValueRetrievalException(key, loader, ex);
}
}
public long getWriteTimeout() {
return writeTimeout;
}
}