org.infinispan.jcache.AbstractJCache Maven / Gradle / Ivy
package org.infinispan.jcache;
import static org.infinispan.jcache.RIMBeanServerRegistrationUtility.ObjectNameType.CONFIGURATION;
import static org.infinispan.jcache.RIMBeanServerRegistrationUtility.ObjectNameType.STATISTICS;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import javax.cache.Cache;
import javax.cache.CacheException;
import javax.cache.CacheManager;
import javax.cache.configuration.CacheEntryListenerConfiguration;
import javax.cache.configuration.CompleteConfiguration;
import javax.cache.configuration.Configuration;
import javax.cache.configuration.Factory;
import javax.cache.configuration.MutableConfiguration;
import javax.cache.expiry.Duration;
import javax.cache.expiry.ExpiryPolicy;
import javax.cache.integration.CacheLoader;
import javax.cache.integration.CacheWriter;
import javax.cache.integration.CompletionListener;
import javax.cache.management.CacheMXBean;
import javax.cache.processor.EntryProcessor;
import javax.cache.processor.EntryProcessorResult;
import javax.management.MBeanServer;
import org.infinispan.commons.CacheListenerException;
import org.infinispan.commons.api.BasicCache;
import org.infinispan.commons.logging.LogFactory;
import org.infinispan.commons.util.ReflectionUtil;
import org.infinispan.jcache.logging.Log;
public abstract class AbstractJCache implements Cache {
private static final Log log =
LogFactory.getLog(AbstractJCache.class, Log.class);
private static final boolean trace = log.isTraceEnabled();
protected final MutableConfiguration configuration;
protected final ExpiryPolicy expiryPolicy;
protected final AbstractJCacheNotifier notifier;
private final CacheManager cacheManager;
protected CacheLoader jcacheLoader;
protected CacheWriter super K, ? super V> jcacheWriter;
private final CacheMXBean mxBean;
public AbstractJCache(MutableConfiguration configuration, CacheManager cacheManager, AbstractJCacheNotifier notifier) {
this.configuration = configuration;
this.cacheManager = cacheManager;
this.notifier = notifier;
this.expiryPolicy = configuration.getExpiryPolicyFactory().create();
this.mxBean = new RIDelegatingCacheMXBean(this);
}
protected void addConfigurationListeners() {
for (CacheEntryListenerConfiguration r
: configuration.getCacheEntryListenerConfigurations())
notifier.addListener(r, this, notifier);
}
@Override
public > C getConfiguration(Class clazz) {
if (clazz.isInstance(configuration))
return clazz.cast(configuration);
throw log.configurationClassNotSupported(clazz);
}
public AbstractJCache checkNotNull(Object obj, String name) {
if (obj == null) {
throw log.parameterMustNotBeNull(name);
}
return this;
}
protected void setCacheLoader(CompleteConfiguration c) {
// Plug user-defined cache loader into adaptor
Factory> cacheLoaderFactory = c.getCacheLoaderFactory();
if (cacheLoaderFactory != null) {
jcacheLoader = cacheLoaderFactory.create();
addCacheLoaderAdapter(jcacheLoader);
}
}
protected void setCacheWriter(CompleteConfiguration c) {
// Plug user-defined cache writer into adaptor
Factory> cacheWriterFactory = c.getCacheWriterFactory();
if (cacheWriterFactory != null) {
jcacheWriter = cacheWriterFactory.create();
addCacheWriterAdapter(jcacheWriter);
}
}
protected abstract void addCacheLoaderAdapter(CacheLoader cacheLoader);
protected abstract void addCacheWriterAdapter(CacheWriter super K, ? super V> cacheWriter);
protected void setListenerCompletion(CompletionListener listener) {
if (listener != null)
listener.onCompletion();
}
protected void setListenerException(CompletionListener listener, Throwable t) {
if (listener != null) {
if (t instanceof Exception)
listener.onException((Exception) t);
else
listener.onException(new CacheException(t));
}
}
protected List filterLoadAllKeys(Set extends K> keys, boolean replaceExistingValues, boolean cacheEvict) {
if (trace)
log.tracef("Before filtering, keys to load: %s", keys);
// Filter null keys out and keys to be overridden optionally
final List keysToLoad = new ArrayList();
for (K key : keys) {
if (key == null)
throw log.parameterMustNotBeNull("Key");
// Evict from memory if value needs to be replaced (instead of flag)
if (cacheEvict && replaceExistingValues && containsKey(key))
evict(key);
if (replaceExistingValues || !containsKey(key))
keysToLoad.add(key);
}
if (trace)
log.tracef("After filtering, keys to load: %s", keysToLoad);
return keysToLoad;
}
protected Map loadAllKeys(List keysToLoad) {
try {
return jcacheLoader.loadAll(keysToLoad);
} catch (Exception e) {
throw Exceptions.launderCacheLoaderException(e);
}
}
protected void loadAllFromJCacheLoader(Set extends K> keys, boolean replaceExistingValues, final CompletionListener listener,
BasicCache cache, BasicCache createCheckCache) {
final List keysToLoad = filterLoadAllKeys(keys, replaceExistingValues, false);
if (keysToLoad.isEmpty()) {
setListenerCompletion(listener);
return;
}
try {
Map loaded = loadAllKeys(keysToLoad);
Iterator> it = loaded.entrySet().iterator();
while (it.hasNext()) {
Map.Entry entry = it.next();
if (entry.getValue() == null)
it.remove();
}
for (Map.Entry entry : loaded.entrySet()) {
K loadedKey = entry.getKey();
V loadedValue = entry.getValue();
put(cache, createCheckCache, loadedKey, loadedValue, false);
}
setListenerCompletion(listener);
} catch (Throwable t) {
setListenerException(listener, t);
}
}
protected T processEntryProcessor(MutableJCacheEntry mutable,
EntryProcessor entryProcessor, Object[] arguments) {
try {
return entryProcessor.process(mutable, arguments);
} catch (Exception e) {
throw Exceptions.launderEntryProcessorException(e);
}
}
protected abstract AbstractJCache checkNotClosed();
protected AbstractJCache verifyKeys(Set extends K> keys) {
// spec required
if (keys == null || keys.contains(null))
throw new NullPointerException("keys is null or keys contains a null: " + keys);
return this;
}
@Override
public Map> invokeAll(
Set extends K> keys, EntryProcessor entryProcessor, Object... arguments) {
checkNotClosed().checkNotNull(entryProcessor, "entryProcessor").verifyKeys(keys);
Map> map = new HashMap>(keys.size());
for (K key : keys) {
EntryProcessorResult result;
try {
T t = invoke(key, entryProcessor, arguments);
result = t == null ? null : new SuccessEntryProcessorResult(t);
} catch (Throwable t) {
result = new FailureEntryProcessorResult(t);
}
if (result != null)
map.put(key, result);
}
return map;
}
protected abstract void evict(K key);
protected V put(BasicCache cache, BasicCache createCheckCache,
K key, V value, boolean isPutIfAbsent) {
// Use a separate cache reference to check whether entry is created or
// not. A separate reference allows for listener notifications to be
// skipped selectively.
V prev = createCheckCache.get(key);
boolean isCreated = prev == null;
// If putIfAbsent and entry already present, skip early
if (!isCreated && isPutIfAbsent)
return prev;
V ret;
Duration ttl = isCreated
? Expiration.getExpiry(expiryPolicy, Expiration.Operation.CREATION)
: Expiration.getExpiry(expiryPolicy, Expiration.Operation.UPDATE);
try {
if (ttl == null || ttl.isEternal()) {
ret = isPutIfAbsent
? cache.putIfAbsent(key, value)
: cache.put(key, value);
} else if (ttl.equals(Duration.ZERO)) {
// TODO: Can this be avoided?
// Special case for ZERO because the Infinispan remove()
// implementation returns true if entry was expired in the removal
// (since it was previously stored). JSR-107 TCK expects that if
// ZERO is passed, the entry is not stored and removal returns false.
// So, if entry is created, do not store it in the cache.
// If the entry is modified, explicitly remove it.
if (!isCreated)
ret = cache.remove(key);
else
ret = null;
} else {
long duration = ttl.getDurationAmount();
TimeUnit timeUnit = ttl.getTimeUnit();
ret = isPutIfAbsent
? cache.putIfAbsent(key, value, duration, timeUnit)
: cache.put(key, value, duration, timeUnit);
}
return ret;
} catch (CacheListenerException e) {
throw Exceptions.launderCacheListenerException(e);
}
}
protected void updateTTLForAccessed(BasicCache cache, K key, V value) {
Duration ttl = Expiration.getExpiry(expiryPolicy, Expiration.Operation.ACCESS);
if (ttl != null) {
if (ttl.equals(Duration.ZERO)) {
// TODO: Expiry of 0 does not seem to remove entry when next accessed.
// Hence, explicitly removing the entry.
cache.remove(key);
} else {
// The expiration policy could potentially return different values
// every time, so don't think we can rely on maxIdle.
long durationAmount = ttl.getDurationAmount();
TimeUnit timeUnit = ttl.getTimeUnit();
cache.put(key, value, durationAmount, timeUnit);
}
}
}
protected boolean replace(BasicCache cache, BasicCache existsCheckCache,
K key, V oldValue, V value, boolean isConditional) {
checkNotNull(value, "value");
if (isConditional) {
// Even if replace fails, values have to be validated (required by TCK)
checkNotNull(oldValue, "oldValue");
}
V current = existsCheckCache.get(key);
if (current != null) {
if (isConditional && !current.equals(oldValue)) {
updateTTLForAccessed(cache, key, value);
return false;
}
Duration ttl = Expiration.getExpiry(expiryPolicy, Expiration.Operation.UPDATE);
if (ttl == null || ttl.isEternal()) {
return isConditional
? cache.replace(key, oldValue, value)
: cache.replace(key, value) != null;
} else if (ttl.equals(Duration.ZERO)) {
// TODO: Can this be avoided?
// Remove explicitly
return cache.remove(key) != null;
} else {
long duration = ttl.getDurationAmount();
TimeUnit timeUnit = ttl.getTimeUnit();
return isConditional
? cache.replace(key, oldValue, value, duration, timeUnit)
: cache.replace(key, value, duration, timeUnit) != null;
}
}
return false;
}
protected V replace(BasicCache cache, K key, V value) {
checkNotNull(value, "value");
boolean exists = cache.containsKey(key);
if (exists) {
Duration ttl = Expiration.getExpiry(expiryPolicy, Expiration.Operation.UPDATE);
if (ttl == null || ttl.isEternal()) {
return cache.replace(key, value);
} else if (ttl.equals(Duration.ZERO)) {
// TODO: Can this be avoided?
// Remove explicitly
return cache.remove(key);
} else {
long duration = ttl.getDurationAmount();
TimeUnit timeUnit = ttl.getTimeUnit();
return cache.replace(key, value, duration, timeUnit);
}
}
return null;
}
protected boolean remove(BasicCache cache, K key, V oldValue) {
V current = cache.get(key);
if (current != null && !current.equals(oldValue)) {
updateTTLForAccessed(cache, key, current);
return false;
}
return cache.remove(key, oldValue);
}
@Override
public CacheManager getCacheManager() {
return cacheManager;
}
//TODO: these were initially package-level
protected abstract MBeanServer getMBeanServer();
//TODO: these were initially package-level
protected Object getCacheMXBean() {
return mxBean;
}
//TODO: these were initially package-level
protected abstract Object getCacheStatisticsMXBean();
//TODO: was package-level initially
public void setManagementEnabled(boolean enabled) {
configuration.setManagementEnabled(enabled);
if (enabled)
RIMBeanServerRegistrationUtility.registerCacheObject(this, CONFIGURATION);
else
RIMBeanServerRegistrationUtility.unregisterCacheObject(this, CONFIGURATION);
}
//TODO: was package-level initially
public void setStatisticsEnabled(boolean enabled) {
configuration.setStatisticsEnabled(enabled);
if (enabled)
RIMBeanServerRegistrationUtility.registerCacheObject(this, STATISTICS);
else
RIMBeanServerRegistrationUtility.unregisterCacheObject(this, STATISTICS);
}
protected abstract void addListener(AbstractJCacheListenerAdapter listenerAdapter);
protected abstract void removeListener(AbstractJCacheListenerAdapter listenerAdapter);
@Override
public T unwrap(Class clazz) {
return ReflectionUtil.unwrap(this, clazz);
}
protected void addCacheEntryListenerConfiguration(
CacheEntryListenerConfiguration listenerCfg) {
if (listenerCfg == null) {
throw new NullPointerException("CacheEntryListenerConfiguration can't be null");
}
boolean alreadyExists = false;
for (CacheEntryListenerConfiguration super K, ? super V> c
: configuration.getCacheEntryListenerConfigurations()) {
if (c.equals(listenerCfg)) {
alreadyExists = true;
}
}
if (!alreadyExists) {
configuration.addCacheEntryListenerConfiguration(listenerCfg);
} else {
throw new IllegalArgumentException("A CacheEntryListenerConfiguration can " +
"be registered only once");
}
}
protected void removeCacheEntryListenerConfiguration(CacheEntryListenerConfiguration listenerCfg) {
configuration.removeCacheEntryListenerConfiguration(listenerCfg);
}
protected boolean statisticsEnabled() {
return getConfiguration(CompleteConfiguration.class).isStatisticsEnabled();
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy