package org.infinispan.cache.impl;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Properties;
import java.util.Set;
import java.util.Spliterator;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;
import java.util.function.BiConsumer;
import java.util.function.BiFunction;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.stream.Stream;
import java.util.stream.StreamSupport;
import javax.transaction.TransactionManager;
import javax.transaction.xa.XAResource;
import org.infinispan.AdvancedCache;
import org.infinispan.CacheCollection;
import org.infinispan.CacheSet;
import org.infinispan.CacheStream;
import org.infinispan.Version;
import org.infinispan.atomic.Delta;
import org.infinispan.batch.BatchContainer;
import org.infinispan.commons.api.BasicCacheContainer;
import org.infinispan.commons.util.ByRef;
import org.infinispan.commons.util.CloseableIterator;
import org.infinispan.commons.util.CloseableIteratorCollectionAdapter;
import org.infinispan.commons.util.CloseableIteratorSetAdapter;
import org.infinispan.commons.util.CloseableSpliterator;
import org.infinispan.commons.util.Closeables;
import org.infinispan.commons.util.CollectionFactory;
import org.infinispan.commons.util.IteratorMapper;
import org.infinispan.commons.util.Util;
import org.infinispan.configuration.cache.Configuration;
import org.infinispan.configuration.format.PropertyFormatter;
import org.infinispan.container.DataContainer;
import org.infinispan.container.InternalEntryFactory;
import org.infinispan.container.entries.CacheEntry;
import org.infinispan.container.entries.InternalCacheEntry;
import org.infinispan.context.Flag;
import org.infinispan.context.InvocationContextContainer;
import org.infinispan.context.impl.ImmutableContext;
import org.infinispan.distribution.DistributionManager;
import org.infinispan.eviction.EvictionManager;
import org.infinispan.expiration.ExpirationManager;
import org.infinispan.factories.ComponentRegistry;
import org.infinispan.factories.annotations.Inject;
import org.infinispan.filter.KeyFilter;
import org.infinispan.interceptors.AsyncInterceptorChain;
import org.infinispan.interceptors.EmptyAsyncInterceptorChain;
import org.infinispan.interceptors.base.CommandInterceptor;
import org.infinispan.jmx.annotations.DataType;
import org.infinispan.jmx.annotations.DisplayType;
import org.infinispan.jmx.annotations.MBean;
import org.infinispan.jmx.annotations.ManagedAttribute;
import org.infinispan.jmx.annotations.ManagedOperation;
import org.infinispan.lifecycle.ComponentStatus;
import org.infinispan.manager.EmbeddedCacheManager;
import org.infinispan.metadata.EmbeddedMetadata;
import org.infinispan.metadata.Metadata;
import org.infinispan.notifications.cachelistener.CacheNotifier;
import org.infinispan.notifications.cachelistener.annotation.CacheEntriesEvicted;
import org.infinispan.notifications.cachelistener.annotation.CacheEntryCreated;
import org.infinispan.notifications.cachelistener.annotation.CacheEntryExpired;
import org.infinispan.notifications.cachelistener.annotation.CacheEntryInvalidated;
import org.infinispan.notifications.cachelistener.annotation.CacheEntryModified;
import org.infinispan.notifications.cachelistener.annotation.CacheEntryRemoved;
import org.infinispan.notifications.cachelistener.annotation.CacheEntryVisited;
import org.infinispan.notifications.cachelistener.filter.CacheEventConverter;
import org.infinispan.notifications.cachelistener.filter.CacheEventFilter;
import org.infinispan.partitionhandling.AvailabilityMode;
import org.infinispan.remoting.rpc.RpcManager;
import org.infinispan.security.AuthorizationManager;
import org.infinispan.stats.Stats;
import org.infinispan.stream.impl.local.EntryStreamSupplier;
import org.infinispan.stream.impl.local.KeyStreamSupplier;
import org.infinispan.stream.impl.local.LocalCacheStream;
import org.infinispan.util.DataContainerRemoveIterator;
import org.infinispan.util.TimeService;
import org.infinispan.util.concurrent.CompletableFutures;
import org.infinispan.util.concurrent.locks.LockManager;
import org.infinispan.util.logging.Log;
import org.infinispan.util.logging.LogFactory;
/**
* Simple local cache without interceptor stack.
* The cache still implements {@link AdvancedCache} since it is too troublesome to omit that.
*
* @author Radim Vansa <rvansa@redhat.com>
*/
@MBean(objectName = CacheImpl.OBJECT_NAME, description = "Component that represents a simplified cache instance.")
public class SimpleCacheImpl<K, V> implements AdvancedCache<K, V> {
private final static Log log = LogFactory.getLog(SimpleCacheImpl.class);
private final static String NULL_KEYS_NOT_SUPPORTED = "Null keys are not supported!";
private final static String NULL_VALUES_NOT_SUPPORTED = "Null values are not supported!";
private final static Class<? extends Annotation>[] FIRED_EVENTS = new Class[] {
CacheEntryCreated.class, CacheEntryRemoved.class, CacheEntryVisited.class,
CacheEntryModified.class, CacheEntriesEvicted.class, CacheEntryInvalidated.class,
CacheEntryExpired.class };
private final String name;
private ComponentRegistry componentRegistry;
private Configuration configuration;
private EmbeddedCacheManager cacheManager;
private DataContainer<K, V> dataContainer;
private CacheNotifier<K, V> cacheNotifier;
private TimeService timeService;
private Metadata defaultMetadata;
private boolean hasListeners = false;
public SimpleCacheImpl(String cacheName) {
this.name = cacheName;
}
@Inject
public void injectDependencies(ComponentRegistry componentRegistry,
Configuration configuration,
EmbeddedCacheManager cacheManager,
DataContainer dataContainer,
CacheNotifier cacheNotifier,
TimeService timeService) {
this.componentRegistry = componentRegistry;
this.configuration = configuration;
this.cacheManager = cacheManager;
this.dataContainer = dataContainer;
this.cacheNotifier = cacheNotifier;
this.timeService = timeService;
}
@Override
@ManagedOperation(
description = "Starts the cache.",
displayName = "Starts cache."
)
public void start() {
this.defaultMetadata = new EmbeddedMetadata.Builder()
.lifespan(configuration.expiration().lifespan())
.maxIdle(configuration.expiration().maxIdle()).build();
componentRegistry.start();
}
@Override
@ManagedOperation(
description = "Stops the cache.",
displayName = "Stops cache."
)
public void stop() {
if (log.isDebugEnabled())
log.debugf("Stopping cache %s on %s", getName(), getCacheManager().getAddress());
dataContainer = null;
componentRegistry.stop();
}
@Override
public void putForExternalRead(K key, V value) {
ByRef.Boolean isCreatedRef = new ByRef.Boolean(false);
putForExternalReadInternal(key, value, defaultMetadata, isCreatedRef);
}
@Override
public void putForExternalRead(K key, V value, long lifespan, TimeUnit unit) {
Metadata metadata = createMetadata(lifespan, unit);
ByRef.Boolean isCreatedRef = new ByRef.Boolean(false);
putForExternalReadInternal(key, value, metadata, isCreatedRef);
}
@Override
public void putForExternalRead(K key, V value, long lifespan, TimeUnit lifespanUnit, long maxIdle, TimeUnit maxIdleUnit) {
Metadata metadata = createMetadata(lifespan, lifespanUnit, maxIdle, maxIdleUnit);
ByRef.Boolean isCreatedRef = new ByRef.Boolean(false);
putForExternalReadInternal(key, value, metadata, isCreatedRef);
}
@Override
public void putForExternalRead(K key, V value, Metadata metadata) {
ByRef.Boolean isCreatedRef = new ByRef.Boolean(false);
putForExternalReadInternal(key, value, applyDefaultMetadata(metadata), isCreatedRef);
}
protected void putForExternalReadInternal(K key, V value, Metadata metadata, ByRef.Boolean isCreatedRef) {
Objects.requireNonNull(key, NULL_KEYS_NOT_SUPPORTED);
Objects.requireNonNull(value, NULL_VALUES_NOT_SUPPORTED);
boolean hasListeners = this.hasListeners;
getDataContainer().compute(key, (k, oldEntry, factory) -> {
// entry cannot be marked for removal in DC but it compute does not deal with expiration
if (isNull(oldEntry)) {
if (hasListeners) {
cacheNotifier.notifyCacheEntryCreated(k, value, metadata, true, ImmutableContext.INSTANCE, null);
}
isCreatedRef.set(true);
return factory.create(k, value, metadata);
} else {
return oldEntry;
}
});
if (hasListeners && isCreatedRef.get()) {
cacheNotifier.notifyCacheEntryCreated(key, value, metadata, false, ImmutableContext.INSTANCE, null);
}
}
@Override
public CompletableFuture<V> putAsync(K key, V value, Metadata metadata) {
return CompletableFuture.completedFuture(getAndPutInternal(key, value, applyDefaultMetadata(metadata)));
}
@Override
public Map<K, V> getAll(Set<?> keys) {
Map<K, V> map = CollectionFactory
.makeMap(CollectionFactory.computeCapacity(keys.size()));
for (Object k : keys) {
Objects.requireNonNull(k, NULL_KEYS_NOT_SUPPORTED);
InternalCacheEntry<K, V> entry = getDataContainer().get(k);
if (entry != null) {
K key = entry.getKey();
V value = entry.getValue();
if (hasListeners) {
cacheNotifier.notifyCacheEntryVisited(key, value, true, ImmutableContext.INSTANCE, null);
cacheNotifier.notifyCacheEntryVisited(key, value, false, ImmutableContext.INSTANCE, null);
}
map.put(key, value);
}
}
return map;
}
@Override
public CacheEntry<K, V> getCacheEntry(Object k) {
InternalCacheEntry<K, V> entry = getDataContainer().get(k);
if (entry != null) {
K key = entry.getKey();
V value = entry.getValue();
if (hasListeners) {
cacheNotifier.notifyCacheEntryVisited(key, value, true, ImmutableContext.INSTANCE, null);
cacheNotifier.notifyCacheEntryVisited(key, value, false, ImmutableContext.INSTANCE, null);
}
}
return entry;
}
@Override
public Map<K, CacheEntry<K, V>> getAllCacheEntries(Set<?> keys) {
Map<K, CacheEntry<K, V>> map = CollectionFactory
.makeMap(CollectionFactory.computeCapacity(keys.size()));
for (Object key : keys) {
Objects.requireNonNull(key, NULL_KEYS_NOT_SUPPORTED);
InternalCacheEntry<K, V> entry = getDataContainer().get(key);
if (entry != null) {
V value = entry.getValue();
if (hasListeners) {
cacheNotifier.notifyCacheEntryVisited((K) key, value, true, ImmutableContext.INSTANCE, null);
cacheNotifier.notifyCacheEntryVisited((K) key, value, false, ImmutableContext.INSTANCE, null);
}
map.put(entry.getKey(), entry);
}
}
return map;
}
@Override
public Map<K, V> getGroup(String groupName) {
return Collections.emptyMap();
}
@Override
public void removeGroup(String groupName) {
}
@Override
public AvailabilityMode getAvailability() {
return AvailabilityMode.AVAILABLE;
}
@Override
public void setAvailability(AvailabilityMode availabilityMode) {
throw new UnsupportedOperationException();
}
@Override
public void evict(K key) {
ByRef<InternalCacheEntry<K, V>> oldEntryRef = new ByRef<>(null);
getDataContainer().compute(key, (k, oldEntry, factory) -> {
if (!isNull(oldEntry)) {
oldEntryRef.set(oldEntry);
}
return null;
});
InternalCacheEntry<K, V> oldEntry = oldEntryRef.get();
if (hasListeners && oldEntry != null) {
cacheNotifier.notifyCacheEntriesEvicted(Collections.singleton(oldEntry), ImmutableContext.INSTANCE, null);
}
}
@Override
public Configuration getCacheConfiguration() {
return configuration;
}
@Override
public EmbeddedCacheManager getCacheManager() {
return cacheManager;
}
@Override
public AdvancedCache<K, V> getAdvancedCache() {
return this;
}
@Override
public ComponentStatus getStatus() {
return componentRegistry.getStatus();
}
@ManagedAttribute(
description = "Returns the cache status",
displayName = "Cache status",
dataType = DataType.TRAIT,
displayType = DisplayType.SUMMARY
)
public String getCacheStatus() {
return getStatus().toString();
}
protected boolean checkExpiration(InternalCacheEntry<K, V> entry, long now) {
if (entry.isExpired(now)) {
// we have to check the expiration under lock
return null == dataContainer.compute(entry.getKey(), (key, oldEntry, factory) -> {
if (entry.isExpired(now)) {
cacheNotifier.notifyCacheEntryExpired(key, oldEntry.getValue(), oldEntry.getMetadata(), ImmutableContext.INSTANCE);
return null;
}
return oldEntry;
});
}
return false;
}
@Override
public int size() {
// we have to iterate in order to provide precise result in case of expiration
long now = Long.MIN_VALUE;
int size = 0;
DataContainer<K, V> dataContainer = getDataContainer();
for (InternalCacheEntry<K, V> entry : dataContainer) {
if (entry.canExpire()) {
if (now == Long.MIN_VALUE) now = timeService.wallClockTime();
if (!checkExpiration(entry, now)) {
++size;
if (size < 0) {
return Integer.MAX_VALUE;
}
}
} else {
++size;
if (size < 0) {
return Integer.MAX_VALUE;
}
}
}
return size;
}
@Override
public boolean isEmpty() {
long now = Long.MIN_VALUE;
DataContainer<K, V> dataContainer = getDataContainer();
for (InternalCacheEntry<K, V> entry : dataContainer) {
if (entry.canExpire()) {
if (now == Long.MIN_VALUE) now = timeService.wallClockTime();
if (!checkExpiration(entry, now)) {
return false;
}
} else {
return false;
}
}
return true;
}
@Override
public boolean containsKey(Object key) {
return get(key) != null;
}
@Override
public boolean containsValue(Object value) {
Objects.requireNonNull(value, NULL_VALUES_NOT_SUPPORTED);
for (V v : getDataContainer().values()) {
if (Objects.equals(v, value)) return true;
}
return false;
}
@Override
public V get(Object key) {
Objects.requireNonNull(key, NULL_KEYS_NOT_SUPPORTED);
InternalCacheEntry<K, V> entry = getDataContainer().get(key);
if (entry == null) {
return null;
} else {
if (hasListeners) {
cacheNotifier.notifyCacheEntryVisited(entry.getKey(), entry.getValue(), true, ImmutableContext.INSTANCE, null);
cacheNotifier.notifyCacheEntryVisited(entry.getKey(), entry.getValue(), false, ImmutableContext.INSTANCE, null);
}
return entry.getValue();
}
}
@Override
public CacheSet<K> keySet() {
return new KeySet();
}
@Override
public CacheCollection<V> values() {
return new Values();
}
@Override
public CacheSet<Entry<K, V>> entrySet() {
return new EntrySet();
}
@Override
public CacheSet<CacheEntry<K, V>> cacheEntrySet() {
return new CacheEntrySet();
}
@Override
public void removeExpired(K key, V value, Long lifespan) {
checkExpiration(getDataContainer().get(key), timeService.wallClockTime());
}
@Override
@ManagedOperation(
description = "Clears the cache",
displayName = "Clears the cache", name = "clear"
)
public void clear() {
DataContainer<K, V> dataContainer = getDataContainer();
boolean hasListeners = this.hasListeners;
ArrayList<InternalCacheEntry<K, V>> copyEntries;
if (hasListeners) {
copyEntries = new ArrayList<>(dataContainer.sizeIncludingExpired());
dataContainer.iterator().forEachRemaining(entry -> {
copyEntries.add(entry);
cacheNotifier.notifyCacheEntryRemoved(entry.getKey(), entry.getValue(), entry.getMetadata(), true, ImmutableContext.INSTANCE, null);
});
} else {
copyEntries = null;
}
dataContainer.clear();
if (hasListeners) {
for (InternalCacheEntry<K, V> entry : copyEntries) {
cacheNotifier.notifyCacheEntryRemoved(entry.getKey(), entry.getValue(), entry.getMetadata(), false, ImmutableContext.INSTANCE, null);
}
}
}
@Override
public String getName() {
return name;
}
@ManagedAttribute(
description = "Returns the cache name",
displayName = "Cache name",
dataType = DataType.TRAIT,
displayType = DisplayType.SUMMARY
)
public String getCacheName() {
String name = getName().equals(BasicCacheContainer.DEFAULT_CACHE_NAME) ? "Default Cache" : getName();
return name + "(" + getCacheConfiguration().clustering().cacheMode().toString().toLowerCase() + ")";
}
@Override
@ManagedAttribute(
description = "Returns the version of Infinispan",
displayName = "Infinispan version",
dataType = DataType.TRAIT,
displayType = DisplayType.SUMMARY
)
public String getVersion() {
return Version.getVersion();
}
@ManagedAttribute(
description = "Returns the cache configuration in form of properties",
displayName = "Cache configuration properties",
dataType = DataType.TRAIT,
displayType = DisplayType.SUMMARY
)
public Properties getConfigurationAsProperties() {
return new PropertyFormatter().format(configuration);
}
@Override
public V put(K key, V value) {
return getAndPutInternal(key, value, defaultMetadata);
}
@Override
public V put(K key, V value, long lifespan, TimeUnit unit) {
Metadata metadata = createMetadata(lifespan, unit);
return getAndPutInternal(key, value, metadata);
}
protected V getAndPutInternal(K key, V value, Metadata metadata) {
Objects.requireNonNull(key, NULL_KEYS_NOT_SUPPORTED);
Objects.requireNonNull(value, NULL_VALUES_NOT_SUPPORTED);
ValueAndMetadata<V> oldRef = new ValueAndMetadata<>();
boolean hasListeners = this.hasListeners;
getDataContainer().compute(key, (k, oldEntry, factory) -> {
if (isNull(oldEntry)) {
if (hasListeners) {
cacheNotifier.notifyCacheEntryCreated(key, value, metadata, true, ImmutableContext.INSTANCE, null);
}
} else {
oldRef.set(oldEntry.getValue(), oldEntry.getMetadata());
if (hasListeners) {
cacheNotifier.notifyCacheEntryModified(key, value, metadata, oldEntry.getValue(), oldEntry.getMetadata(), true, ImmutableContext.INSTANCE, null);
}
}
if (oldEntry == null) {
return factory.create(k, value, metadata);
} else {
return factory.update(oldEntry, value, metadata);
}
});
V oldValue = oldRef.getValue();
if (hasListeners) {
if (oldValue == null) {
cacheNotifier.notifyCacheEntryCreated(key, value, metadata, false, ImmutableContext.INSTANCE, null);
} else {
cacheNotifier.notifyCacheEntryModified(key, value, metadata, oldValue, oldRef.getMetadata(), false, ImmutableContext.INSTANCE, null);
}
}
return oldValue;
}
@Override
public V putIfAbsent(K key, V value, long lifespan, TimeUnit unit) {
Metadata metadata = createMetadata(lifespan, unit);
return putIfAbsentInternal(key, value, metadata);
}
@Override
public V putIfAbsent(K key, V value, Metadata metadata) {
return putIfAbsentInternal(key, value, applyDefaultMetadata(metadata));
}
protected V putIfAbsentInternal(K key, V value, Metadata metadata) {
Objects.requireNonNull(key, NULL_KEYS_NOT_SUPPORTED);
Objects.requireNonNull(value, NULL_VALUES_NOT_SUPPORTED);
ByRef<V> oldValueRef = new ByRef<>(null);
boolean hasListeners = this.hasListeners;
getDataContainer().compute(key, (k, oldEntry, factory) -> {
if (isNull(oldEntry)) {
if (hasListeners) {
cacheNotifier.notifyCacheEntryCreated(key, value, metadata, true, ImmutableContext.INSTANCE, null);
}
return factory.create(k, value, metadata);
} else {
oldValueRef.set(oldEntry.getValue());
return oldEntry;
}
});
V oldValue = oldValueRef.get();
if (hasListeners && oldValue == null) {
cacheNotifier.notifyCacheEntryCreated(key, value, metadata, false, ImmutableContext.INSTANCE, null);
}
return oldValue;
}
@Override
public void putAll(Map<? extends K, ? extends V> map, long lifespan, TimeUnit unit) {
putAllInternal(map, createMetadata(lifespan, unit));
}
@Override
public V replace(K key, V value, long lifespan, TimeUnit unit) {
Objects.requireNonNull(key, NULL_KEYS_NOT_SUPPORTED);
Objects.requireNonNull(value, NULL_VALUES_NOT_SUPPORTED);
Metadata metadata = createMetadata(lifespan, unit);
return getAndReplaceInternal(key, value, metadata);
}
@Override
public boolean replace(K key, V oldValue, V value, long lifespan, TimeUnit unit) {
return replaceInternal(key, oldValue, value, createMetadata(lifespan, unit));
}
protected V getAndReplaceInternal(K key, V value, Metadata metadata) {
Objects.requireNonNull(key, NULL_KEYS_NOT_SUPPORTED);
Objects.requireNonNull(value, NULL_VALUES_NOT_SUPPORTED);
ValueAndMetadata<V> oldRef = new ValueAndMetadata<>();
boolean hasListeners = this.hasListeners;
getDataContainer().compute(key, (k, oldEntry, factory) -> {
if (!isNull(oldEntry)) {
if (hasListeners) {
cacheNotifier.notifyCacheEntryModified(key, value, metadata, oldEntry.getValue(), oldEntry.getMetadata(), true, ImmutableContext.INSTANCE, null);
}
oldRef.set(oldEntry.getValue(), oldEntry.getMetadata());
return factory.update(oldEntry, value, metadata);
} else {
return oldEntry;
}
});
V oldValue = oldRef.getValue();
if (hasListeners && oldValue != null) {
cacheNotifier.notifyCacheEntryModified(key, value, metadata, oldValue, oldRef.getMetadata(), false, ImmutableContext.INSTANCE, null);
}
return oldValue;
}
@Override
public V put(K key, V value, long lifespan, TimeUnit lifespanUnit, long maxIdleTime, TimeUnit maxIdleTimeUnit) {
Metadata metadata = createMetadata(lifespan, lifespanUnit, maxIdleTime, maxIdleTimeUnit);
return getAndPutInternal(key, value, metadata);
}
@Override
public V putIfAbsent(K key, V value, long lifespan, TimeUnit lifespanUnit, long maxIdleTime, TimeUnit maxIdleTimeUnit) {
Metadata metadata = createMetadata(lifespan, lifespanUnit, maxIdleTime, maxIdleTimeUnit);
return putIfAbsentInternal(key, value, metadata);
}
@Override
public void putAll(Map<? extends K, ? extends V> map, long lifespan, TimeUnit lifespanUnit, long maxIdleTime, TimeUnit maxIdleTimeUnit) {
putAllInternal(map, createMetadata(lifespan, lifespanUnit, maxIdleTime, maxIdleTimeUnit));
}
@Override
public V replace(K key, V value, long lifespan, TimeUnit lifespanUnit, long maxIdleTime, TimeUnit maxIdleTimeUnit) {
Metadata metadata = createMetadata(lifespan, lifespanUnit, maxIdleTime, maxIdleTimeUnit);
return getAndReplaceInternal(key, value, metadata);
}
@Override
public boolean replace(K key, V oldValue, V value, long lifespan, TimeUnit lifespanUnit, long maxIdleTime, TimeUnit maxIdleTimeUnit) {
Metadata metadata = createMetadata(lifespan, lifespanUnit, maxIdleTime, maxIdleTimeUnit);
return replaceInternal(key, oldValue, value, metadata);
}
@Override
public boolean replace(K key, V oldValue, V value, Metadata metadata) {
return replaceInternal(key, oldValue, value, applyDefaultMetadata(metadata));
}
protected boolean replaceInternal(K key, V oldValue, V value, Metadata metadata) {
Objects.requireNonNull(key, NULL_KEYS_NOT_SUPPORTED);
Objects.requireNonNull(value, NULL_VALUES_NOT_SUPPORTED);
Objects.requireNonNull(oldValue, NULL_VALUES_NOT_SUPPORTED);
ValueAndMetadata<V> oldRef = new ValueAndMetadata<>();
boolean hasListeners = this.hasListeners;
getDataContainer().compute(key, (k, oldEntry, factory) -> {
V prevValue = getValue(oldEntry);
if (Objects.equals(prevValue, oldValue)) {
oldRef.set(prevValue, oldEntry.getMetadata());
if (hasListeners) {
cacheNotifier.notifyCacheEntryModified(key, value, metadata, prevValue, oldEntry.getMetadata(), true, ImmutableContext.INSTANCE, null);
}
return factory.update(oldEntry, value, metadata);
} else {
return oldEntry;
}
});
if (oldRef.getValue() != null) {
if (hasListeners) {
cacheNotifier.notifyCacheEntryModified(key, value, metadata, oldRef.getValue(), oldRef.getMetadata(), false, ImmutableContext.INSTANCE, null);
}
return true;
} else {
return false;
}
}
@Override
public V remove(Object key) {
Objects.requireNonNull(key, NULL_KEYS_NOT_SUPPORTED);
ByRef<InternalCacheEntry<K, V>> oldEntryRef = new ByRef<>(null);
boolean hasListeners = this.hasListeners;
getDataContainer().compute((K) key, (k, oldEntry, factory) -> {
if (!isNull(oldEntry)) {
if (hasListeners) {
cacheNotifier.notifyCacheEntryRemoved(oldEntry.getKey(), oldEntry.getValue(), oldEntry.getMetadata(), true, ImmutableContext.INSTANCE, null);
}
oldEntryRef.set(oldEntry);
}
return null;
});
InternalCacheEntry<K, V> oldEntry = oldEntryRef.get();
if (oldEntry != null) {
if (hasListeners) {
cacheNotifier.notifyCacheEntryRemoved(oldEntry.getKey(), oldEntry.getValue(), oldEntry.getMetadata(), false, ImmutableContext.INSTANCE, null);
}
return oldEntry.getValue();
} else {
return null;
}
}
@Override
public void putAll(Map<? extends K, ? extends V> map) {
putAllInternal(map, defaultMetadata);
}
@Override
public CompletableFuture<V> putAsync(K key, V value) {
return CompletableFuture.completedFuture(put(key, value));
}
@Override
public CompletableFuture<V> putAsync(K key, V value, long lifespan, TimeUnit unit) {
return CompletableFuture.completedFuture(put(key, value, lifespan, unit));
}
@Override
public CompletableFuture<V> putAsync(K key, V value, long lifespan, TimeUnit lifespanUnit, long maxIdle, TimeUnit maxIdleUnit) {
return CompletableFuture.completedFuture(put(key, value, lifespan, lifespanUnit, maxIdle, maxIdleUnit));
}
@Override
public CompletableFuture<Void> putAllAsync(Map<? extends K, ? extends V> data) {
putAll(data);
return CompletableFutures.completedNull();
}
@Override
public CompletableFuture<Void> putAllAsync(Map<? extends K, ? extends V> data, long lifespan, TimeUnit unit) {
putAll(data, lifespan, unit);
return CompletableFutures.completedNull();
}
@Override
public CompletableFuture<Void> putAllAsync(Map<? extends K, ? extends V> data, long lifespan, TimeUnit lifespanUnit, long maxIdle, TimeUnit maxIdleUnit) {
putAll(data, lifespan, lifespanUnit, maxIdle, maxIdleUnit);
return CompletableFutures.completedNull();
}
@Override
public CompletableFuture<Void> clearAsync() {
clear();
return CompletableFutures.completedNull();
}
@Override
public CompletableFuture<V> putIfAbsentAsync(K key, V value) {
return CompletableFuture.completedFuture(putIfAbsent(key, value));
}
@Override
public CompletableFuture<V> putIfAbsentAsync(K key, V value, long lifespan, TimeUnit unit) {
return CompletableFuture.completedFuture(putIfAbsent(key, value, lifespan, unit));
}
@Override
public CompletableFuture<V> putIfAbsentAsync(K key, V value, long lifespan, TimeUnit lifespanUnit, long maxIdle, TimeUnit maxIdleUnit) {
return CompletableFuture.completedFuture(putIfAbsent(key, value, lifespan, lifespanUnit, maxIdle, maxIdleUnit));
}
@Override
public CompletableFuture<V> removeAsync(Object key) {
return CompletableFuture.completedFuture(remove(key));
}
@Override
public CompletableFuture<Boolean> removeAsync(Object key, Object value) {
return CompletableFuture.completedFuture(remove(key, value));
}
@Override
public CompletableFuture<V> replaceAsync(K key, V value) {
return CompletableFuture.completedFuture(replace(key, value));
}
@Override
public CompletableFuture<V> replaceAsync(K key, V value, long lifespan, TimeUnit unit) {
return CompletableFuture.completedFuture(replace(key, value, lifespan, unit));
}
@Override
public CompletableFuture<V> replaceAsync(K key, V value, long lifespan, TimeUnit lifespanUnit, long maxIdle, TimeUnit maxIdleUnit) {
return CompletableFuture.completedFuture(replace(key, value, lifespan, lifespanUnit, maxIdle, maxIdleUnit));
}
@Override
public CompletableFuture<Boolean> replaceAsync(K key, V oldValue, V newValue) {
return CompletableFuture.completedFuture(replace(key, oldValue, newValue));
}
@Override
public CompletableFuture<Boolean> replaceAsync(K key, V oldValue, V newValue, long lifespan, TimeUnit unit) {
return CompletableFuture.completedFuture(replace(key, oldValue, newValue, lifespan, unit));
}
@Override
public CompletableFuture<Boolean> replaceAsync(K key, V oldValue, V newValue, long lifespan, TimeUnit lifespanUnit, long maxIdle, TimeUnit maxIdleUnit) {
return CompletableFuture.completedFuture(replace(key, oldValue, newValue, lifespan, lifespanUnit, maxIdle, maxIdleUnit));
}
@Override
public CompletableFuture<V> getAsync(K key) {
return CompletableFuture.completedFuture(get(key));
}
@Override
public boolean startBatch() {
// invocation batching implies CacheImpl
throw log.invocationBatchingNotEnabled();
}
@Override
public void endBatch(boolean successful) {
// invocation batching implies CacheImpl
throw log.invocationBatchingNotEnabled();
}
@Override
public V putIfAbsent(K key, V value) {
return putIfAbsentInternal(key, value, defaultMetadata);
}
@Override
public boolean remove(Object key, Object value) {
Objects.requireNonNull(key, NULL_KEYS_NOT_SUPPORTED);
Objects.requireNonNull(value, NULL_VALUES_NOT_SUPPORTED);
ByRef<InternalCacheEntry<K, V>> oldEntryRef = new ByRef<>(null);
boolean hasListeners = this.hasListeners;
getDataContainer().compute((K) key, (k, oldEntry, factory) -> {
V oldValue = getValue(oldEntry);
if (Objects.equals(oldValue, value)) {
if (hasListeners) {
cacheNotifier.notifyCacheEntryRemoved(oldEntry.getKey(), oldValue, oldEntry.getMetadata(), true, ImmutableContext.INSTANCE, null);
}
oldEntryRef.set(oldEntry);
return null;
} else {
return oldEntry;
}
});
InternalCacheEntry<K, V> oldEntry = oldEntryRef.get();
if (oldEntry != null) {
if (hasListeners) {
cacheNotifier.notifyCacheEntryRemoved(oldEntry.getKey(), oldEntry.getValue(), oldEntry.getMetadata(), false, ImmutableContext.INSTANCE, null);
}
return true;
} else {
return false;
}
}
@Override
public boolean replace(K key, V oldValue, V newValue) {
return replaceInternal(key, oldValue, newValue, defaultMetadata);
}
@Override
public V replace(K key, V value) {
return getAndReplaceInternal(key, value, defaultMetadata);
}
@Override
public void addListener(Object listener, KeyFilter<? super K> filter) {
cacheNotifier.addListener(listener, filter);
if (!hasListeners && canFire(listener)) {
hasListeners = true;
}
}
@Override
public <C> void addListener(Object listener, CacheEventFilter<? super K, ? super V> filter, CacheEventConverter<? super K, ? super V, C> converter) {
cacheNotifier.addListener(listener, filter, converter);
if (!hasListeners && canFire(listener)) {
hasListeners = true;
}
}
@Override
public void addListener(Object listener) {
cacheNotifier.addListener(listener);
if (!hasListeners && canFire(listener)) {
hasListeners = true;
}
}
@Override
public void removeListener(Object listener) {
cacheNotifier.removeListener(listener);
}
@Override
public Set<Object> getListeners() {
return cacheNotifier.getListeners();
}
@Override
public <C> void addFilteredListener(Object listener,
CacheEventFilter<? super K, ? super V> filter, CacheEventConverter<? super K, ? super V, C> converter,
Set<Class<? extends Annotation>> filterAnnotations) {
cacheNotifier.addFilteredListener(listener, filter, converter, filterAnnotations);
if (!hasListeners && canFire(listener)) {
hasListeners = true;
}
}
private boolean canFire(Object listener) {
for (Method m : listener.getClass().getMethods()) {
for (Class<? extends Annotation> annotation : FIRED_EVENTS) {
if (m.isAnnotationPresent(annotation)) {
return true;
}
}
}
return false;
}
private Metadata applyDefaultMetadata(Metadata metadata) {
Metadata.Builder builder = metadata.builder();
return builder != null ? builder.merge(defaultMetadata).build() : metadata;
}
private Metadata createMetadata(long lifespan, TimeUnit unit) {
return new EmbeddedMetadata.Builder().lifespan(lifespan, unit).maxIdle(configuration.expiration().maxIdle()).build();
}
private Metadata createMetadata(long lifespan, TimeUnit lifespanUnit, long maxIdleTime, TimeUnit maxIdleTimeUnit) {
return new EmbeddedMetadata.Builder()
.lifespan(lifespan, lifespanUnit)
.maxIdle(maxIdleTime, maxIdleTimeUnit)
.build();
}
@Override
public AdvancedCache<K, V> withFlags(Flag... flags) {
// the flags are mostly ignored
return this;
}
@Override
public void addInterceptor(CommandInterceptor i, int position) {
throw log.interceptorStackNotSupported();
}
@Override
public AsyncInterceptorChain getAsyncInterceptorChain() {
return EmptyAsyncInterceptorChain.INSTANCE;
}
@Override
public boolean addInterceptorAfter(CommandInterceptor i, Class<? extends CommandInterceptor> afterInterceptor) {
throw log.interceptorStackNotSupported();
}
@Override
public boolean addInterceptorBefore(CommandInterceptor i, Class<? extends CommandInterceptor> beforeInterceptor) {
throw log.interceptorStackNotSupported();
}
@Override
public void removeInterceptor(int position) {
throw log.interceptorStackNotSupported();
}
@Override
public void removeInterceptor(Class<? extends CommandInterceptor> interceptorType) {
throw log.interceptorStackNotSupported();
}
@Override
public List<CommandInterceptor> getInterceptorChain() {
return Collections.emptyList();
}
@Override
public EvictionManager getEvictionManager() {
return getComponentRegistry().getComponent(EvictionManager.class);
}
@Override
public ExpirationManager<K, V> getExpirationManager() {
return getComponentRegistry().getComponent(ExpirationManager.class);
}
@Override
public ComponentRegistry getComponentRegistry() {
return componentRegistry;
}
@Override
public DistributionManager getDistributionManager() {
return getComponentRegistry().getComponent(DistributionManager.class);
}
@Override
public AuthorizationManager getAuthorizationManager() {
return getComponentRegistry().getComponent(AuthorizationManager.class);
}
@Override
public boolean lock(K... keys) {
throw log.lockOperationsNotSupported();
}
@Override
public boolean lock(Collection<? extends K> keys) {
throw log.lockOperationsNotSupported();
}
@Override
public void applyDelta(K deltaAwareValueKey, Delta delta, Object... locksToAcquire) {
throw new UnsupportedOperationException();
}
@Override
public RpcManager getRpcManager() {
return null;
}
@Override
public BatchContainer getBatchContainer() {
return null;
}
@Override
public InvocationContextContainer getInvocationContextContainer() {
return null;
}
@Override
public DataContainer<K, V> getDataContainer() {
DataContainer<K, V> dataContainer = this.dataContainer;
if (dataContainer == null) {
ComponentStatus status = getStatus();
switch (status) {
case STOPPING:
throw log.cacheIsStopping(name);
case TERMINATED:
case FAILED:
throw log.cacheIsTerminated(name, status.toString());
default:
throw new IllegalStateException("Status: " + status);
}
}
return dataContainer;
}
@Override
public TransactionManager getTransactionManager() {
return null;
}
@Override
public LockManager getLockManager() {
return null;
}
@Override
public Stats getStats() {
return null;
}
@Override
public XAResource getXAResource() {
return null;
}
@Override
public ClassLoader getClassLoader() {
return null;
}
@Override
public AdvancedCache<K, V> with(ClassLoader classLoader) {
return this;
}
@Override
public V put(K key, V value, Metadata metadata) {
return getAndPutInternal(key, value, applyDefaultMetadata(metadata));
}
@Override
public void putAll(Map<? extends K, ? extends V> map, Metadata metadata) {
putAllInternal(map, applyDefaultMetadata(metadata));
}
protected void putAllInternal(Map<? extends K, ? extends V> map, Metadata metadata) {
for (Entry<? extends K, ? extends V> entry : map.entrySet()) {
Objects.requireNonNull(entry.getKey(), NULL_KEYS_NOT_SUPPORTED);
Objects.requireNonNull(entry.getValue(), NULL_VALUES_NOT_SUPPORTED);
}
for (Entry<? extends K, ? extends V> entry : map.entrySet()) {
getAndPutInternal(entry.getKey(), entry.getValue(), metadata);
}
}
@Override
public V replace(K key, V value, Metadata metadata) {
return getAndReplaceInternal(key, value, applyDefaultMetadata(metadata));
}
@Override
public V computeIfAbsent(K key, Function<? super K, ? extends V> mappingFunction) {
Objects.requireNonNull(key, NULL_KEYS_NOT_SUPPORTED);
ByRef<V> newValueRef = new ByRef<>(null);
return computeIfAbsentInternal(key, mappingFunction, newValueRef);
}
protected V computeIfAbsentInternal(K key, Function<? super K, ? extends V> mappingFunction, ByRef<V> newValueRef) {
boolean hasListeners = this.hasListeners;
InternalCacheEntry<K, V> returnEntry = getDataContainer().compute(key, (k, oldEntry, factory) -> {
V oldValue = getValue(oldEntry);
if (oldValue == null) {
V newValue = mappingFunction.apply(k);
if (newValue == null) {
return null;
} else {
if (hasListeners) {
cacheNotifier.notifyCacheEntryCreated(k, newValue, defaultMetadata, true, ImmutableContext.INSTANCE, null);
}
newValueRef.set(newValue);
return factory.create(k, newValue, defaultMetadata);
}
} else {
return oldEntry;
}
});
V newValue = newValueRef.get();
if (hasListeners && newValue != null) {
cacheNotifier.notifyCacheEntryCreated(key, newValueRef.get(), defaultMetadata, false, ImmutableContext.INSTANCE, null);
}
return returnEntry == null ? null : returnEntry.getValue();
}
@Override
public V computeIfPresent(K key, BiFunction<? super K, ? super V, ? extends V> remappingFunction) {
Objects.requireNonNull(key, NULL_KEYS_NOT_SUPPORTED);
CacheEntryChange<K, V> ref = new CacheEntryChange<>();
return computeIfPresentInternal(key, remappingFunction, ref);
}
protected V computeIfPresentInternal(K key, BiFunction<? super K, ? super V, ? extends V> remappingFunction, CacheEntryChange<K, V> ref) {
boolean hasListeners = this.hasListeners;
getDataContainer().compute(key, (k, oldEntry, factory) -> {
V oldValue = getValue(oldEntry);
if (oldValue != null) {
V newValue = remappingFunction.apply(k, oldValue);
if (newValue == null) {
if (hasListeners) {
cacheNotifier.notifyCacheEntryRemoved(k, oldValue, oldEntry.getMetadata(), true, ImmutableContext.INSTANCE, null);
}
ref.set(k, null, oldValue, oldEntry.getMetadata());
return null;
} else {
if (hasListeners) {
cacheNotifier.notifyCacheEntryModified(k, newValue, defaultMetadata, oldValue, oldEntry.getMetadata(), true, ImmutableContext.INSTANCE, null);
}
ref.set(k, newValue, oldValue, oldEntry.getMetadata());
return factory.update(oldEntry, newValue, defaultMetadata);
}
} else {
return null;
}
});
V newValue = ref.getNewValue();
if (hasListeners) {
if (newValue != null) {
cacheNotifier.notifyCacheEntryModified(ref.getKey(), newValue, defaultMetadata, ref.getOldValue(), ref.getOldMetadata(), false, ImmutableContext.INSTANCE, null);
} else {
cacheNotifier.notifyCacheEntryRemoved(ref.getKey(), ref.getOldValue(), ref.getOldMetadata(), false, ImmutableContext.INSTANCE, null);
}
}
return newValue;
}
@Override
public V compute(K key, BiFunction<? super K, ? super V, ? extends V> remappingFunction) {
CacheEntryChange<K, V> ref = new CacheEntryChange<>();
return computeInternal(key, remappingFunction, ref);
}
protected V computeInternal(K key, BiFunction<? super K, ? super V, ? extends V> remappingFunction, CacheEntryChange<K, V> ref) {
boolean hasListeners = this.hasListeners;
getDataContainer().compute(key, (k, oldEntry, factory) -> {
V oldValue = getValue(oldEntry);
V newValue = remappingFunction.apply(k, oldValue);
return getUpdatedEntry(k, oldEntry, factory, oldValue, newValue, ref, hasListeners);
});
return notifyAndReturn(ref, hasListeners);
}
@Override
public V merge(K key, V value, BiFunction<? super V, ? super V, ? extends V> remappingFunction) {
CacheEntryChange<K, V> ref = new CacheEntryChange<>();
return mergeInternal(key, value, remappingFunction, ref);
}
protected V mergeInternal(K key, V value, BiFunction<? super V, ? super V, ? extends V> remappingFunction, CacheEntryChange<K, V> ref) {
boolean hasListeners = this.hasListeners;
getDataContainer().compute(key, (k, oldEntry, factory) -> {
V oldValue = getValue(oldEntry);
V newValue = oldValue == null ? value : remappingFunction.apply(oldValue, value);
return getUpdatedEntry(k, oldEntry, factory, oldValue, newValue, ref, hasListeners);
});
return notifyAndReturn(ref, hasListeners);
}
private V notifyAndReturn(CacheEntryChange<K, V> ref, boolean hasListeners) {
K key = ref.getKey();
V newValue = ref.getNewValue();
if (key != null) {
V oldValue = ref.getOldValue();
if (hasListeners) {
if (newValue == null) {
cacheNotifier.notifyCacheEntryRemoved(key, oldValue, ref.getOldMetadata(), false, ImmutableContext.INSTANCE, null);
} else if (oldValue == null) {
cacheNotifier.notifyCacheEntryCreated(key, newValue, defaultMetadata, false, ImmutableContext.INSTANCE, null);
} else {
cacheNotifier.notifyCacheEntryModified(key, newValue, defaultMetadata, oldValue, ref.getOldMetadata(), false, ImmutableContext.INSTANCE, null);
}
}
}
return newValue;
}
private InternalCacheEntry<K, V> getUpdatedEntry(K k, InternalCacheEntry<K, V> oldEntry, InternalEntryFactory factory, V oldValue, V newValue, CacheEntryChange<K, V> ref, boolean hasListeners) {
if (newValue == null) {
if (oldValue != null) {
if (hasListeners) {
cacheNotifier.notifyCacheEntryRemoved(k, oldValue, oldEntry.getMetadata(), true, ImmutableContext.INSTANCE, null);
}
ref.set(k, null, oldValue, oldEntry.getMetadata());
}
return null;
} else if (oldValue == null) {
if (hasListeners) {
cacheNotifier.notifyCacheEntryCreated(k, newValue, defaultMetadata, true, ImmutableContext.INSTANCE, null);
}
ref.set(k, newValue, null, null);
return factory.create(k, newValue, defaultMetadata);
} else if (Objects.equals(oldValue, newValue)) {
return oldEntry;
} else {
if (hasListeners) {
cacheNotifier.notifyCacheEntryModified(k, newValue, defaultMetadata, oldValue, oldEntry.getMetadata(), true, ImmutableContext.INSTANCE, null);
}
ref.set(k, newValue, oldValue, oldEntry.getMetadata());
return factory.update(oldEntry, newValue, defaultMetadata);
}
}
// This method can be called only from dataContainer.compute()'s action;
// as we'll replace the old value when it's expired
private boolean isNull(InternalCacheEntry<K, V> entry) {
if (entry == null) {
return true;
} else if (entry.canExpire()) {
if (entry.isExpired(timeService.wallClockTime())) {
cacheNotifier.notifyCacheEntryExpired(entry.getKey(), entry.getValue(), entry.getMetadata(), ImmutableContext.INSTANCE);
return true;
}
}
return false;
}
// This method can be called only from dataContainer.compute()'s action!
private V getValue(InternalCacheEntry<K, V> entry) {
return isNull(entry) ? null : entry.getValue();
}
@Override
public void forEach(BiConsumer<? super K, ? super V> action) {
for (Iterator<InternalCacheEntry<K, V>> it = dataContainer.iterator(); it.hasNext(); ) {
InternalCacheEntry<K, V> ice = it.next();
action.accept(ice.getKey(), ice.getValue());
}
}
@Override
public void replaceAll(BiFunction<? super K, ? super V, ? extends V> function) {
boolean hasListeners = this.hasListeners;
CacheEntryChange<K, V> ref = new CacheEntryChange<>();
for (Iterator<InternalCacheEntry<K, V>> it = dataContainer.iterator(); it.hasNext(); ) {
InternalCacheEntry<K, V> ice = it.next();
getDataContainer().compute(ice.getKey(), (k, oldEntry, factory) -> {
V oldValue = getValue(oldEntry);
if (oldValue != null) {
V newValue = function.apply(k, oldValue);
Objects.requireNonNull(newValue, NULL_VALUES_NOT_SUPPORTED);
if (hasListeners) {
cacheNotifier.notifyCacheEntryModified(k, newValue, defaultMetadata, oldValue, oldEntry.getMetadata(),
true, ImmutableContext.INSTANCE, null);
}
ref.set(k, newValue, oldValue, oldEntry.getMetadata());
return factory.update(oldEntry, newValue, defaultMetadata);
} else {
return null;
}
});
if (hasListeners) {
cacheNotifier.notifyCacheEntryModified(ref.getKey(), ref.getNewValue(), defaultMetadata, ref.getOldValue(),
ref.getOldMetadata(), false, ImmutableContext.INSTANCE, null);
}
}
}
protected static class ValueAndMetadata<V> {
private V value;
private Metadata metadata;
public void set(V value, Metadata metadata) {
this.value = value;
this.metadata = metadata;
}
public V getValue() {
return value;
}
public Metadata getMetadata() {
return metadata;
}
}
protected static class CacheEntryChange<K, V> {
private K key;
private V newValue;
private V oldValue;
private Metadata oldMetadata;
public void set(K key, V newValue, V oldValue, Metadata oldMetadata) {
this.key = key;
this.newValue = newValue;
this.oldValue = oldValue;
this.oldMetadata = oldMetadata;
}
public K getKey() {
return key;
}
public V getNewValue() {
return newValue;
}
public V getOldValue() {
return oldValue;
}
public Metadata getOldMetadata() {
return oldMetadata;
}
}
protected abstract class EntrySetBase<T extends Entry<K, V>> implements CacheSet<T> {
private final Set<? extends Entry<K, V>> delegate = getDataContainer().entrySet();
@Override
public int size() {
return SimpleCacheImpl.this.size();
}
@Override
public boolean isEmpty() {
return SimpleCacheImpl.this.isEmpty();
}
@Override
public boolean contains(Object o) {
return delegate.contains(o);
}
@Override
public Object[] toArray() {
return delegate.toArray();
}
@Override
public <U> U[] toArray(U[] a) {
return delegate.toArray(a);
}
@Override
public boolean remove(Object o) {
if (o instanceof Entry) {
Entry<K, V> entry = (Entry<K, V>) o;
return SimpleCacheImpl.this.remove(entry.getKey(), entry.getValue());
}
return false;
}
@Override
public boolean containsAll(Collection<?> c) {
return delegate.containsAll(c);
}
@Override
public boolean retainAll(Collection<?> c) {
boolean changed = false;
for (InternalCacheEntry<K, V> entry : getDataContainer()) {
if (!c.contains(entry)) {
changed |= SimpleCacheImpl.this.remove(entry.getKey(), entry.getValue());
}
}
return changed;
}
@Override
public boolean removeAll(Collection<?> c) {
boolean changed = false;
for (Object o : c) {
if (o instanceof Entry) {
Entry<K, V> entry = (Entry<K, V>) o;
changed |= SimpleCacheImpl.this.remove(entry.getKey(), entry.getValue());
}
}
return changed;
}
@Override
public void clear() {
SimpleCacheImpl.this.clear();
}
}
protected class EntrySet extends EntrySetBase<Entry<K, V>> implements CacheSet<Entry<K, V>> {
@Override
public CloseableIterator<Entry<K, V>> iterator() {
return Closeables.iterator(new DataContainerRemoveIterator<>(SimpleCacheImpl.this));
}
@Override
public CloseableSpliterator<Entry<K, V>> spliterator() {
return Closeables.spliterator(iterator(), dataContainer.sizeIncludingExpired(),
Spliterator.CONCURRENT | Spliterator.NONNULL | Spliterator.DISTINCT);
}
@Override
public boolean add(Entry<K, V> entry) {
throw new UnsupportedOperationException();
}
@Override
public boolean addAll(Collection<? extends Entry<K, V>> c) {
throw new UnsupportedOperationException();
}
@Override
public CacheStream<Entry<K, V>> stream() {
return cacheStreamCast(new LocalCacheStream<>(new EntryStreamSupplier<>(SimpleCacheImpl.this, null,
getStreamSupplier(false)), false, componentRegistry));
}
@Override
public CacheStream<Entry<K, V>> parallelStream() {
return cacheStreamCast(new LocalCacheStream<>(new EntryStreamSupplier<>(SimpleCacheImpl.this, null,
getStreamSupplier(false)), true, componentRegistry));
}
}
// This is a hack to allow the cast to work. Java doesn't like subtypes in generics
private static <K, V> CacheStream<Entry<K, V>> cacheStreamCast(CacheStream stream) {
return stream;
}
protected class CacheEntrySet extends EntrySetBase<CacheEntry<K, V>> implements CacheSet<CacheEntry<K, V>> {
@Override
public CloseableIterator<CacheEntry<K, V>> iterator() {
return Closeables.iterator(new DataContainerRemoveIterator<>(SimpleCacheImpl.this));
}
@Override
public CloseableSpliterator<CacheEntry<K, V>> spliterator() {
return Closeables.spliterator(iterator(), dataContainer.sizeIncludingExpired(),
Spliterator.CONCURRENT | Spliterator.NONNULL | Spliterator.DISTINCT);
}
@Override
public boolean add(CacheEntry<K, V> entry) {
throw new UnsupportedOperationException();
}
@Override
public boolean addAll(Collection<? extends CacheEntry<K, V>> c) {
throw new UnsupportedOperationException();
}
@Override
public CacheStream<CacheEntry<K, V>> stream() {
return new LocalCacheStream<>(new EntryStreamSupplier<>(SimpleCacheImpl.this, null, getStreamSupplier(false)),
false, componentRegistry);
}
@Override
public CacheStream<CacheEntry<K, V>> parallelStream() {
return new LocalCacheStream<>(new EntryStreamSupplier<>(SimpleCacheImpl.this, null, getStreamSupplier(true)),
true, componentRegistry);
}
}
protected class Values extends CloseableIteratorCollectionAdapter<V> implements CacheCollection<V> {
public Values() {
super(getDataContainer().values());
}
@Override
public boolean retainAll(Collection<?> c) {
Set<Object> retained = CollectionFactory.makeSet(c.size());
retained.addAll(c);
boolean changed = false;
for (InternalCacheEntry<K, V> entry : getDataContainer()) {
if (!retained.contains(entry.getValue())) {
changed |= SimpleCacheImpl.this.remove(entry.getKey(), entry.getValue());
}
}
return changed;
}
@Override
public boolean removeAll(Collection<?> c) {
int removeSize = c.size();
if (removeSize == 0) {
return false;
} else if (removeSize == 1) {
return remove(c.iterator().next());
}
Set<Object> removed = CollectionFactory.makeSet(removeSize);
removed.addAll(c);
boolean changed = false;
for (InternalCacheEntry<K, V> entry : getDataContainer()) {
if (removed.contains(entry.getValue())) {
changed |= SimpleCacheImpl.this.remove(entry.getKey(), entry.getValue());
}
}
return changed;
}
@Override
public boolean remove(Object o) {
for (InternalCacheEntry<K, V> entry : getDataContainer()) {
if (Objects.equals(entry.getValue(), o)) {
if (SimpleCacheImpl.this.remove(entry.getKey(), entry.getValue())) {
return true;
}
}
}
return false;
}
@Override
public void clear() {
SimpleCacheImpl.this.clear();
}
@Override
public CloseableIterator<V> iterator() {
return Closeables.iterator(new IteratorMapper<>(dataContainer.iterator(), Map.Entry::getValue));
}
@Override
public int size() {
return SimpleCacheImpl.this.size();
}
@Override
public boolean isEmpty() {
return SimpleCacheImpl.this.isEmpty();
}
@Override
public CacheStream<V> stream() {
LocalCacheStream<CacheEntry<K, V>> lcs = new LocalCacheStream<>(new EntryStreamSupplier<>(SimpleCacheImpl.this,
null, getStreamSupplier(false)), false, componentRegistry);
return lcs.map(CacheEntry::getValue);
}
@Override
public CacheStream<V> parallelStream() {
LocalCacheStream<CacheEntry<K, V>> lcs = new LocalCacheStream<>(new EntryStreamSupplier<>(SimpleCacheImpl.this,
null, getStreamSupplier(false)), true, componentRegistry);
return lcs.map(CacheEntry::getValue);
}
}
protected class KeySet extends CloseableIteratorSetAdapter<K> implements CacheSet<K> {
public KeySet() {
super(getDataContainer().keySet());
}
@Override
public boolean retainAll(Collection<?> c) {
Set<Object> retained = CollectionFactory.makeSet(c.size());
retained.addAll(c);
boolean changed = false;
for (InternalCacheEntry<K, V> entry : getDataContainer()) {
if (!retained.contains(entry.getKey())) {
changed |= SimpleCacheImpl.this.remove(entry.getKey()) != null;
}
}
return changed;
}
@Override
public boolean remove(Object o) {
return SimpleCacheImpl.this.remove(o) != null;
}
@Override
public boolean removeAll(Collection<?> c) {
boolean changed = false;
for (Object key : c) {
changed |= SimpleCacheImpl.this.remove(key) != null;
}
return changed;
}
@Override
public void clear() {
SimpleCacheImpl.this.clear();
}
@Override
public CloseableIterator<K> iterator() {
return Closeables.iterator(new IteratorMapper<>(new DataContainerRemoveIterator<>(SimpleCacheImpl.this), Map.Entry::getKey));
}
@Override
public int size() {
return SimpleCacheImpl.this.size();
}
@Override
public boolean isEmpty() {
return SimpleCacheImpl.this.isEmpty();
}
@Override
public CacheStream<K> stream() {
Supplier<Stream<CacheEntry<K, V>>> supplier = getStreamSupplier(false);
return new LocalCacheStream<>(new KeyStreamSupplier<>(SimpleCacheImpl.this, null,
() -> supplier.get().map(CacheEntry::getKey)), false, componentRegistry);
}
@Override
public CacheStream<K> parallelStream() {
Supplier<Stream<CacheEntry<K, V>>> supplier = getStreamSupplier(true);
return new LocalCacheStream<>(new KeyStreamSupplier<>(SimpleCacheImpl.this, null,
() -> supplier.get().map(CacheEntry::getKey)), true, componentRegistry);
}
}
protected Supplier<Stream<CacheEntry<K, V>>> getStreamSupplier(boolean parallel) {
CloseableSpliterator<CacheEntry<K, V>> spliterator =
Closeables.spliterator(Closeables.iterator(dataContainer.iterator()), dataContainer.sizeIncludingExpired(),
Spliterator.CONCURRENT | Spliterator.NONNULL | Spliterator.DISTINCT);
return () -> StreamSupport.stream(spliterator, parallel);
}
@Override
public String toString() {
return "SimpleCache '" + getName() + "'@" + Util.hexIdHashCode(getCacheManager());
}
}