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.
keycloakjar.com.github.benmanes.caffeine.cache.UnboundedLocalCache Maven / Gradle / Ivy
Go to download
Camunda 7 Keycloak Identity Provider Plugin including all transitive dependencies
/*
* Copyright 2014 Ben Manes. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.github.benmanes.caffeine.cache;
import static com.github.benmanes.caffeine.cache.Caffeine.calculateHashMapCapacity;
import static com.github.benmanes.caffeine.cache.LocalLoadingCache.newBulkMappingFunction; // NOPMD
import static com.github.benmanes.caffeine.cache.LocalLoadingCache.newMappingFunction; // NOPMD
import static java.util.Objects.requireNonNull;
import static java.util.function.Function.identity;
import java.io.InvalidObjectException;
import java.io.ObjectInputStream;
import java.io.Serializable;
import java.lang.System.Logger;
import java.lang.System.Logger.Level;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.VarHandle;
import java.util.AbstractCollection;
import java.util.AbstractSet;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.Spliterator;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.Executor;
import java.util.function.BiConsumer;
import java.util.function.BiFunction;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Predicate;
import org.checkerframework.checker.nullness.qual.Nullable;
import com.github.benmanes.caffeine.cache.stats.StatsCounter;
import com.google.errorprone.annotations.CanIgnoreReturnValue;
/**
* An in-memory cache that has no capabilities for bounding the map. This implementation provides
* a lightweight wrapper on top of {@link ConcurrentHashMap}.
*
* @author [email protected] (Ben Manes)
*/
@SuppressWarnings("serial")
final class UnboundedLocalCache implements LocalCache {
static final Logger logger = System.getLogger(UnboundedLocalCache.class.getName());
static final VarHandle REFRESHES;
@Nullable final RemovalListener removalListener;
final ConcurrentHashMap data;
final StatsCounter statsCounter;
final boolean isRecordingStats;
final Executor executor;
final boolean isAsync;
final Ticker ticker;
@Nullable Set keySet;
@Nullable Collection values;
@Nullable Set> entrySet;
@Nullable volatile ConcurrentMap> refreshes;
UnboundedLocalCache(Caffeine super K, ? super V> builder, boolean isAsync) {
this.data = new ConcurrentHashMap<>(builder.getInitialCapacity());
this.statsCounter = builder.getStatsCounterSupplier().get();
this.removalListener = builder.getRemovalListener(isAsync);
this.isRecordingStats = builder.isRecordingStats();
this.executor = builder.getExecutor();
this.ticker = builder.getTicker();
this.isAsync = isAsync;
}
static {
try {
REFRESHES = MethodHandles.lookup()
.findVarHandle(UnboundedLocalCache.class, "refreshes", ConcurrentMap.class);
} catch (ReflectiveOperationException e) {
throw new ExceptionInInitializerError(e);
}
}
@Override
public boolean isAsync() {
return isAsync;
}
@Override
@SuppressWarnings("NullAway")
public Expiry expiry() {
return null;
}
@Override
@CanIgnoreReturnValue
public Object referenceKey(K key) {
return key;
}
@Override
public boolean isPendingEviction(K key) {
return false;
}
/* --------------- Cache --------------- */
@Override
@SuppressWarnings("SuspiciousMethodCalls")
public @Nullable V getIfPresent(Object key, boolean recordStats) {
V value = data.get(key);
if (recordStats) {
if (value == null) {
statsCounter.recordMisses(1);
} else {
statsCounter.recordHits(1);
}
}
return value;
}
@Override
public @Nullable V getIfPresentQuietly(K key) {
return data.get(key);
}
@Override
public long estimatedSize() {
return data.mappingCount();
}
@Override
public Map getAllPresent(Iterable extends K> keys) {
var result = new LinkedHashMap(calculateHashMapCapacity(keys));
for (K key : keys) {
result.put(key, null);
}
int uniqueKeys = result.size();
for (var iter = result.entrySet().iterator(); iter.hasNext();) {
Map.Entry entry = iter.next();
V value = data.get(entry.getKey());
if (value == null) {
iter.remove();
} else {
entry.setValue(value);
}
}
statsCounter.recordHits(result.size());
statsCounter.recordMisses(uniqueKeys - result.size());
return Collections.unmodifiableMap(result);
}
@Override
public void cleanUp() {}
@Override
public StatsCounter statsCounter() {
return statsCounter;
}
private boolean hasRemovalListener() {
return (removalListener != null);
}
@Override
@SuppressWarnings("NullAway")
public void notifyRemoval(@Nullable K key, @Nullable V value, RemovalCause cause) {
if (!hasRemovalListener()) {
return;
}
Runnable task = () -> {
try {
removalListener.onRemoval(key, value, cause);
} catch (Throwable t) {
logger.log(Level.WARNING, "Exception thrown by removal listener", t);
}
};
try {
executor.execute(task);
} catch (Throwable t) {
logger.log(Level.ERROR, "Exception thrown when submitting removal listener", t);
task.run();
}
}
@Override
public boolean isRecordingStats() {
return isRecordingStats;
}
@Override
public Executor executor() {
return executor;
}
@Override
@SuppressWarnings("NullAway")
public ConcurrentMap> refreshes() {
var pending = refreshes;
if (pending == null) {
pending = new ConcurrentHashMap<>();
if (!REFRESHES.compareAndSet(this, null, pending)) {
pending = refreshes;
}
}
return pending;
}
/** Invalidate the in-flight refresh. */
void discardRefresh(Object keyReference) {
var pending = refreshes;
if (pending != null) {
pending.remove(keyReference);
}
}
@Override
public Ticker statsTicker() {
return ticker;
}
/* --------------- JDK8+ Map extensions --------------- */
@Override
public void forEach(BiConsumer super K, ? super V> action) {
data.forEach(action);
}
@Override
public void replaceAll(BiFunction super K, ? super V, ? extends V> function) {
requireNonNull(function);
// ensures that the removal notification is processed after the removal has completed
@SuppressWarnings({"rawtypes", "unchecked"})
K[] notificationKey = (K[]) new Object[1];
@SuppressWarnings({"rawtypes", "unchecked"})
V[] notificationValue = (V[]) new Object[1];
data.replaceAll((key, value) -> {
if (notificationKey[0] != null) {
notifyRemoval(notificationKey[0], notificationValue[0], RemovalCause.REPLACED);
notificationValue[0] = null;
notificationKey[0] = null;
}
V newValue = requireNonNull(function.apply(key, value));
if (newValue != value) {
notificationKey[0] = key;
notificationValue[0] = value;
}
return newValue;
});
if (notificationKey[0] != null) {
notifyRemoval(notificationKey[0], notificationValue[0], RemovalCause.REPLACED);
}
}
@Override
public V computeIfAbsent(K key, Function super K, ? extends V> mappingFunction,
boolean recordStats, boolean recordLoad) {
requireNonNull(mappingFunction);
// optimistic fast path due to computeIfAbsent always locking
V value = data.get(key);
if (value != null) {
if (recordStats) {
statsCounter.recordHits(1);
}
return value;
}
boolean[] missed = new boolean[1];
value = data.computeIfAbsent(key, k -> {
// Do not communicate to CacheWriter on a load
missed[0] = true;
return recordStats
? statsAware(mappingFunction, recordLoad).apply(key)
: mappingFunction.apply(key);
});
if (!missed[0] && recordStats) {
statsCounter.recordHits(1);
}
return value;
}
@Override
public @Nullable V computeIfPresent(K key,
BiFunction super K, ? super V, ? extends V> remappingFunction) {
requireNonNull(remappingFunction);
// optimistic fast path due to computeIfAbsent always locking
if (!data.containsKey(key)) {
return null;
}
// ensures that the removal notification is processed after the removal has completed
@SuppressWarnings({"rawtypes", "unchecked"})
V[] oldValue = (V[]) new Object[1];
boolean[] replaced = new boolean[1];
V nv = data.computeIfPresent(key, (K k, V value) -> {
BiFunction super K, ? super V, ? extends V> function = statsAware(remappingFunction,
/* recordLoad */ true, /* recordLoadFailure */ true);
V newValue = function.apply(k, value);
replaced[0] = (newValue != null);
if (newValue != value) {
oldValue[0] = value;
}
discardRefresh(k);
return newValue;
});
if (replaced[0]) {
notifyOnReplace(key, oldValue[0], nv);
} else if (oldValue[0] != null) {
notifyRemoval(key, oldValue[0], RemovalCause.EXPLICIT);
}
return nv;
}
@Override
public V compute(K key, BiFunction super K, ? super V, ? extends V> remappingFunction,
@Nullable Expiry super K, ? super V> expiry, boolean recordLoad,
boolean recordLoadFailure) {
requireNonNull(remappingFunction);
return remap(key, statsAware(remappingFunction, recordLoad, recordLoadFailure));
}
@Override
public V merge(K key, V value, BiFunction super V, ? super V, ? extends V> remappingFunction) {
requireNonNull(remappingFunction);
requireNonNull(value);
return remap(key, (k, oldValue) ->
(oldValue == null) ? value : statsAware(remappingFunction).apply(oldValue, value));
}
/**
* A {@link Map#compute(Object, BiFunction)} that does not directly record any cache statistics.
*
* @param key key with which the specified value is to be associated
* @param remappingFunction the function to compute a value
* @return the new value associated with the specified key, or null if none
*/
V remap(K key, BiFunction super K, ? super V, ? extends V> remappingFunction) {
// ensures that the removal notification is processed after the removal has completed
@SuppressWarnings({"rawtypes", "unchecked"})
V[] oldValue = (V[]) new Object[1];
boolean[] replaced = new boolean[1];
V nv = data.compute(key, (K k, V value) -> {
V newValue = remappingFunction.apply(k, value);
if ((value == null) && (newValue == null)) {
return null;
}
replaced[0] = (newValue != null);
if ((value != null) && (newValue != value)) {
oldValue[0] = value;
}
discardRefresh(k);
return newValue;
});
if (replaced[0]) {
notifyOnReplace(key, oldValue[0], nv);
} else if (oldValue[0] != null) {
notifyRemoval(key, oldValue[0], RemovalCause.EXPLICIT);
}
return nv;
}
/* --------------- Concurrent Map --------------- */
@Override
public boolean isEmpty() {
return data.isEmpty();
}
@Override
public int size() {
return data.size();
}
@Override
public void clear() {
if (!hasRemovalListener() && ((refreshes == null) || refreshes.isEmpty())) {
data.clear();
return;
}
for (K key : List.copyOf(data.keySet())) {
remove(key);
}
}
@Override
public boolean containsKey(Object key) {
return data.containsKey(key);
}
@Override
public boolean containsValue(Object value) {
return data.containsValue(value);
}
@Override
public @Nullable V get(Object key) {
return getIfPresent(key, /* recordStats */ false);
}
@Override
public @Nullable V put(K key, V value) {
V oldValue = data.put(key, value);
notifyOnReplace(key, oldValue, value);
return oldValue;
}
@Override
public @Nullable V putIfAbsent(K key, V value) {
return data.putIfAbsent(key, value);
}
@Override
public void putAll(Map extends K, ? extends V> map) {
if (hasRemovalListener()) {
map.forEach(this::put);
} else {
data.putAll(map);
}
}
@Override
public @Nullable V remove(Object key) {
@SuppressWarnings("unchecked")
K castKey = (K) key;
@SuppressWarnings({"rawtypes", "unchecked"})
V[] oldValue = (V[]) new Object[1];
data.computeIfPresent(castKey, (k, v) -> {
discardRefresh(k);
oldValue[0] = v;
return null;
});
if (oldValue[0] != null) {
notifyRemoval(castKey, oldValue[0], RemovalCause.EXPLICIT);
}
return oldValue[0];
}
@Override
public boolean remove(Object key, Object value) {
if (value == null) {
requireNonNull(key);
return false;
}
@SuppressWarnings("unchecked")
K castKey = (K) key;
@SuppressWarnings({"rawtypes", "unchecked"})
V[] oldValue = (V[]) new Object[1];
data.computeIfPresent(castKey, (k, v) -> {
if (v.equals(value)) {
discardRefresh(k);
oldValue[0] = v;
return null;
}
return v;
});
if (oldValue[0] != null) {
notifyRemoval(castKey, oldValue[0], RemovalCause.EXPLICIT);
return true;
}
return false;
}
@Override
public @Nullable V replace(K key, V value) {
requireNonNull(value);
@SuppressWarnings({"rawtypes", "unchecked"})
V[] oldValue = (V[]) new Object[1];
data.computeIfPresent(key, (k, v) -> {
discardRefresh(k);
oldValue[0] = v;
return value;
});
if ((oldValue[0] != null) && (oldValue[0] != value)) {
notifyRemoval(key, oldValue[0], RemovalCause.REPLACED);
}
return oldValue[0];
}
@Override
public boolean replace(K key, V oldValue, V newValue) {
return replace(key, oldValue, newValue, /* shouldDiscardRefresh */ true);
}
@Override
public boolean replace(K key, V oldValue, V newValue, boolean shouldDiscardRefresh) {
requireNonNull(oldValue);
requireNonNull(newValue);
@SuppressWarnings({"rawtypes", "unchecked"})
V[] prev = (V[]) new Object[1];
data.computeIfPresent(key, (k, v) -> {
if (v.equals(oldValue)) {
if (shouldDiscardRefresh) {
discardRefresh(k);
}
prev[0] = v;
return newValue;
}
return v;
});
boolean replaced = (prev[0] != null);
if (replaced && (prev[0] != newValue)) {
notifyRemoval(key, prev[0], RemovalCause.REPLACED);
}
return replaced;
}
@Override
public boolean equals(Object o) {
return (o == this) || data.equals(o);
}
@Override
public int hashCode() {
return data.hashCode();
}
@Override
public String toString() {
var result = new StringBuilder(50).append('{');
data.forEach((key, value) -> {
if (result.length() != 1) {
result.append(", ");
}
result.append((key == this) ? "(this Map)" : key)
.append('=')
.append((value == this) ? "(this Map)" : value);
});
return result.append('}').toString();
}
@Override
public Set keySet() {
final Set ks = keySet;
return (ks == null) ? (keySet = new KeySetView<>(this)) : ks;
}
@Override
public Collection values() {
final Collection vs = values;
return (vs == null) ? (values = new ValuesView<>(this)) : vs;
}
@Override
public Set> entrySet() {
final Set> es = entrySet;
return (es == null) ? (entrySet = new EntrySetView<>(this)) : es;
}
/** An adapter to safely externalize the keys. */
static final class KeySetView extends AbstractSet {
final UnboundedLocalCache cache;
KeySetView(UnboundedLocalCache cache) {
this.cache = requireNonNull(cache);
}
@Override
public boolean isEmpty() {
return cache.isEmpty();
}
@Override
public int size() {
return cache.size();
}
@Override
public void clear() {
cache.clear();
}
@Override
@SuppressWarnings("SuspiciousMethodCalls")
public boolean contains(Object o) {
return cache.containsKey(o);
}
@Override
public boolean removeAll(Collection> collection) {
requireNonNull(collection);
boolean modified = false;
if ((collection instanceof Set>) && (collection.size() > size())) {
for (K key : this) {
if (collection.contains(key)) {
modified |= remove(key);
}
}
} else {
for (var o : collection) {
modified |= (o != null) && remove(o);
}
}
return modified;
}
@Override
public boolean remove(Object o) {
return (cache.remove(o) != null);
}
@Override
public boolean removeIf(Predicate super K> filter) {
requireNonNull(filter);
boolean modified = false;
for (K key : this) {
if (filter.test(key) && remove(key)) {
modified = true;
}
}
return modified;
}
@Override
public boolean retainAll(Collection> collection) {
requireNonNull(collection);
boolean modified = false;
for (K key : this) {
if (!collection.contains(key) && remove(key)) {
modified = true;
}
}
return modified;
}
@Override
public void forEach(Consumer super K> action) {
cache.data.keySet().forEach(action);
}
@Override
public Iterator iterator() {
return new KeyIterator<>(cache);
}
@Override
public Spliterator spliterator() {
return cache.data.keySet().spliterator();
}
}
/** An adapter to safely externalize the key iterator. */
static final class KeyIterator implements Iterator {
final UnboundedLocalCache cache;
final Iterator iterator;
@Nullable K current;
KeyIterator(UnboundedLocalCache cache) {
this.iterator = cache.data.keySet().iterator();
this.cache = cache;
}
@Override
public boolean hasNext() {
return iterator.hasNext();
}
@Override
public K next() {
current = iterator.next();
return current;
}
@Override
public void remove() {
if (current == null) {
throw new IllegalStateException();
}
cache.remove(current);
current = null;
}
}
/** An adapter to safely externalize the values. */
static final class ValuesView extends AbstractCollection {
final UnboundedLocalCache cache;
ValuesView(UnboundedLocalCache cache) {
this.cache = requireNonNull(cache);
}
@Override
public boolean isEmpty() {
return cache.isEmpty();
}
@Override
public int size() {
return cache.size();
}
@Override
public void clear() {
cache.clear();
}
@Override
@SuppressWarnings("SuspiciousMethodCalls")
public boolean contains(Object o) {
return cache.containsValue(o);
}
@Override
public boolean removeAll(Collection> collection) {
requireNonNull(collection);
boolean modified = false;
for (var entry : cache.data.entrySet()) {
if (collection.contains(entry.getValue())
&& cache.remove(entry.getKey(), entry.getValue())) {
modified = true;
}
}
return modified;
}
@Override
public boolean remove(Object o) {
if (o == null) {
return false;
}
for (var entry : cache.data.entrySet()) {
if (o.equals(entry.getValue()) && cache.remove(entry.getKey(), entry.getValue())) {
return true;
}
}
return false;
}
@Override
public boolean removeIf(Predicate super V> filter) {
requireNonNull(filter);
boolean removed = false;
for (var entry : cache.data.entrySet()) {
if (filter.test(entry.getValue())) {
removed |= cache.remove(entry.getKey(), entry.getValue());
}
}
return removed;
}
@Override
public boolean retainAll(Collection> collection) {
requireNonNull(collection);
boolean modified = false;
for (var entry : cache.data.entrySet()) {
if (!collection.contains(entry.getValue())
&& cache.remove(entry.getKey(), entry.getValue())) {
modified = true;
}
}
return modified;
}
@Override
public void forEach(Consumer super V> action) {
cache.data.values().forEach(action);
}
@Override
public Iterator iterator() {
return new ValuesIterator<>(cache);
}
@Override
public Spliterator spliterator() {
return cache.data.values().spliterator();
}
}
/** An adapter to safely externalize the value iterator. */
static final class ValuesIterator implements Iterator {
final UnboundedLocalCache cache;
final Iterator> iterator;
@Nullable Entry entry;
ValuesIterator(UnboundedLocalCache cache) {
this.iterator = cache.data.entrySet().iterator();
this.cache = cache;
}
@Override
public boolean hasNext() {
return iterator.hasNext();
}
@Override
public V next() {
entry = iterator.next();
return entry.getValue();
}
@Override
public void remove() {
if (entry == null) {
throw new IllegalStateException();
}
cache.remove(entry.getKey());
entry = null;
}
}
/** An adapter to safely externalize the entries. */
static final class EntrySetView extends AbstractSet> {
final UnboundedLocalCache cache;
EntrySetView(UnboundedLocalCache cache) {
this.cache = requireNonNull(cache);
}
@Override
public boolean isEmpty() {
return cache.isEmpty();
}
@Override
public int size() {
return cache.size();
}
@Override
public void clear() {
cache.clear();
}
@Override
@SuppressWarnings("SuspiciousMethodCalls")
public boolean contains(Object o) {
if (!(o instanceof Entry, ?>)) {
return false;
}
var entry = (Entry, ?>) o;
var key = entry.getKey();
var value = entry.getValue();
if ((key == null) || (value == null)) {
return false;
}
V cachedValue = cache.get(key);
return (cachedValue != null) && cachedValue.equals(value);
}
@Override
public boolean removeAll(Collection> collection) {
requireNonNull(collection);
boolean modified = false;
if ((collection instanceof Set>) && (collection.size() > size())) {
for (var entry : this) {
if (collection.contains(entry)) {
modified |= remove(entry);
}
}
} else {
for (var o : collection) {
modified |= remove(o);
}
}
return modified;
}
@Override
@SuppressWarnings("SuspiciousMethodCalls")
public boolean remove(Object o) {
if (!(o instanceof Entry, ?>)) {
return false;
}
var entry = (Entry, ?>) o;
var key = entry.getKey();
return (key != null) && cache.remove(key, entry.getValue());
}
@Override
public boolean removeIf(Predicate super Entry> filter) {
requireNonNull(filter);
boolean removed = false;
for (var entry : cache.data.entrySet()) {
if (filter.test(entry)) {
removed |= cache.remove(entry.getKey(), entry.getValue());
}
}
return removed;
}
@Override
public boolean retainAll(Collection> collection) {
requireNonNull(collection);
boolean modified = false;
for (var entry : this) {
if (!collection.contains(entry) && remove(entry)) {
modified = true;
}
}
return modified;
}
@Override
public Iterator> iterator() {
return new EntryIterator<>(cache);
}
@Override
public Spliterator> spliterator() {
return new EntrySpliterator<>(cache);
}
}
/** An adapter to safely externalize the entry iterator. */
static final class EntryIterator implements Iterator> {
final UnboundedLocalCache cache;
final Iterator> iterator;
@Nullable Entry entry;
EntryIterator(UnboundedLocalCache cache) {
this.iterator = cache.data.entrySet().iterator();
this.cache = cache;
}
@Override
public boolean hasNext() {
return iterator.hasNext();
}
@Override
public Entry next() {
entry = iterator.next();
return new WriteThroughEntry<>(cache, entry.getKey(), entry.getValue());
}
@Override
public void remove() {
if (entry == null) {
throw new IllegalStateException();
}
cache.remove(entry.getKey());
entry = null;
}
}
/** An adapter to safely externalize the entry spliterator. */
static final class EntrySpliterator implements Spliterator> {
final Spliterator> spliterator;
final UnboundedLocalCache cache;
EntrySpliterator(UnboundedLocalCache cache) {
this(cache, cache.data.entrySet().spliterator());
}
EntrySpliterator(UnboundedLocalCache cache, Spliterator> spliterator) {
this.spliterator = requireNonNull(spliterator);
this.cache = requireNonNull(cache);
}
@Override
public void forEachRemaining(Consumer super Entry> action) {
requireNonNull(action);
spliterator.forEachRemaining(entry -> {
var e = new WriteThroughEntry(cache, entry.getKey(), entry.getValue());
action.accept(e);
});
}
@Override
public boolean tryAdvance(Consumer super Entry> action) {
requireNonNull(action);
return spliterator.tryAdvance(entry -> {
var e = new WriteThroughEntry(cache, entry.getKey(), entry.getValue());
action.accept(e);
});
}
@Override
public @Nullable EntrySpliterator trySplit() {
Spliterator> split = spliterator.trySplit();
return (split == null) ? null : new EntrySpliterator<>(cache, split);
}
@Override
public long estimateSize() {
return spliterator.estimateSize();
}
@Override
public int characteristics() {
return spliterator.characteristics();
}
}
/* --------------- Manual Cache --------------- */
static class UnboundedLocalManualCache implements LocalManualCache, Serializable {
private static final long serialVersionUID = 1;
final UnboundedLocalCache cache;
@Nullable Policy policy;
UnboundedLocalManualCache(Caffeine builder) {
cache = new UnboundedLocalCache<>(builder, /* async */ false);
}
@Override
public UnboundedLocalCache cache() {
return cache;
}
@Override
public Policy policy() {
return (policy == null)
? (policy = new UnboundedPolicy<>(cache, identity()))
: policy;
}
@SuppressWarnings("UnusedVariable")
private void readObject(ObjectInputStream stream) throws InvalidObjectException {
throw new InvalidObjectException("Proxy required");
}
Object writeReplace() {
SerializationProxy proxy = new SerializationProxy<>();
proxy.isRecordingStats = cache.isRecordingStats;
proxy.removalListener = cache.removalListener;
proxy.ticker = cache.ticker;
return proxy;
}
}
/** An eviction policy that supports no boundings. */
static final class UnboundedPolicy implements Policy {
final UnboundedLocalCache cache;
final Function transformer;
UnboundedPolicy(UnboundedLocalCache cache, Function transformer) {
this.transformer = transformer;
this.cache = cache;
}
@Override public boolean isRecordingStats() {
return cache.isRecordingStats;
}
@Override public @Nullable V getIfPresentQuietly(K key) {
return transformer.apply(cache.data.get(key));
}
@Override public @Nullable CacheEntry getEntryIfPresentQuietly(K key) {
V value = transformer.apply(cache.data.get(key));
return (value == null) ? null : SnapshotEntry.forEntry(key, value);
}
@Override public Map> refreshes() {
var refreshes = cache.refreshes;
if ((refreshes == null) || refreshes.isEmpty()) {
@SuppressWarnings("ImmutableMapOf")
Map> emptyMap = Collections.unmodifiableMap(Collections.emptyMap());
return emptyMap;
}
@SuppressWarnings("unchecked")
var castedRefreshes = (Map>) (Object) refreshes;
return Collections.unmodifiableMap(new HashMap<>(castedRefreshes));
}
@Override public Optional> eviction() {
return Optional.empty();
}
@Override public Optional> expireAfterAccess() {
return Optional.empty();
}
@Override public Optional> expireAfterWrite() {
return Optional.empty();
}
@Override public Optional> expireVariably() {
return Optional.empty();
}
@Override public Optional> refreshAfterWrite() {
return Optional.empty();
}
}
/* --------------- Loading Cache --------------- */
static final class UnboundedLocalLoadingCache extends UnboundedLocalManualCache
implements LocalLoadingCache {
private static final long serialVersionUID = 1;
final Function mappingFunction;
final CacheLoader super K, V> cacheLoader;
@Nullable final Function, Map> bulkMappingFunction;
UnboundedLocalLoadingCache(Caffeine builder, CacheLoader super K, V> cacheLoader) {
super(builder);
this.cacheLoader = cacheLoader;
this.mappingFunction = newMappingFunction(cacheLoader);
this.bulkMappingFunction = newBulkMappingFunction(cacheLoader);
}
@Override
public AsyncCacheLoader super K, V> cacheLoader() {
return cacheLoader;
}
@Override
public Function mappingFunction() {
return mappingFunction;
}
@Override
public @Nullable Function, Map> bulkMappingFunction() {
return bulkMappingFunction;
}
@Override
Object writeReplace() {
@SuppressWarnings("unchecked")
var proxy = (SerializationProxy) super.writeReplace();
proxy.cacheLoader = cacheLoader;
return proxy;
}
@SuppressWarnings("UnusedVariable")
private void readObject(ObjectInputStream stream) throws InvalidObjectException {
throw new InvalidObjectException("Proxy required");
}
}
/* --------------- Async Cache --------------- */
static final class UnboundedLocalAsyncCache implements LocalAsyncCache, Serializable {
private static final long serialVersionUID = 1;
final UnboundedLocalCache> cache;
@Nullable ConcurrentMap> mapView;
@Nullable CacheView cacheView;
@Nullable Policy policy;
@SuppressWarnings("unchecked")
UnboundedLocalAsyncCache(Caffeine builder) {
cache = new UnboundedLocalCache<>(
(Caffeine>) builder, /* async */ true);
}
@Override
public UnboundedLocalCache> cache() {
return cache;
}
@Override
public ConcurrentMap> asMap() {
return (mapView == null) ? (mapView = new AsyncAsMapView<>(this)) : mapView;
}
@Override
public Cache synchronous() {
return (cacheView == null) ? (cacheView = new CacheView<>(this)) : cacheView;
}
@Override
public Policy policy() {
@SuppressWarnings("unchecked")
UnboundedLocalCache castCache = (UnboundedLocalCache) cache;
Function, V> transformer = Async::getIfReady;
@SuppressWarnings("unchecked")
Function castTransformer = (Function) transformer;
return (policy == null)
? (policy = new UnboundedPolicy<>(castCache, castTransformer))
: policy;
}
@SuppressWarnings("UnusedVariable")
private void readObject(ObjectInputStream stream) throws InvalidObjectException {
throw new InvalidObjectException("Proxy required");
}
Object writeReplace() {
SerializationProxy proxy = new SerializationProxy<>();
proxy.isRecordingStats = cache.isRecordingStats;
proxy.removalListener = cache.removalListener;
proxy.ticker = cache.ticker;
proxy.async = true;
return proxy;
}
}
/* --------------- Async Loading Cache --------------- */
static final class UnboundedLocalAsyncLoadingCache
extends LocalAsyncLoadingCache implements Serializable {
private static final long serialVersionUID = 1;
final UnboundedLocalCache> cache;
@Nullable ConcurrentMap> mapView;
@Nullable Policy policy;
@SuppressWarnings("unchecked")
UnboundedLocalAsyncLoadingCache(Caffeine builder, AsyncCacheLoader super K, V> loader) {
super(loader);
cache = new UnboundedLocalCache<>(
(Caffeine>) builder, /* async */ true);
}
@Override
public LocalCache> cache() {
return cache;
}
@Override
public ConcurrentMap> asMap() {
return (mapView == null) ? (mapView = new AsyncAsMapView<>(this)) : mapView;
}
@Override
public Policy policy() {
@SuppressWarnings("unchecked")
UnboundedLocalCache castCache = (UnboundedLocalCache) cache;
Function, V> transformer = Async::getIfReady;
@SuppressWarnings("unchecked")
Function castTransformer = (Function) transformer;
return (policy == null)
? (policy = new UnboundedPolicy<>(castCache, castTransformer))
: policy;
}
@SuppressWarnings("UnusedVariable")
private void readObject(ObjectInputStream stream) throws InvalidObjectException {
throw new InvalidObjectException("Proxy required");
}
Object writeReplace() {
SerializationProxy proxy = new SerializationProxy<>();
proxy.isRecordingStats = cache.isRecordingStats();
proxy.removalListener = cache.removalListener;
proxy.cacheLoader = cacheLoader;
proxy.ticker = cache.ticker;
proxy.async = true;
return proxy;
}
}
}