All Downloads are FREE. Search and download functionalities are using the official Maven repository.

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; } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy