org.rx.core.cache.DiskCache Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of rxlib Show documentation
Show all versions of rxlib Show documentation
A set of utilities for Java
package org.rx.core.cache;
import com.github.benmanes.caffeine.cache.RemovalCause;
import lombok.extern.slf4j.Slf4j;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.checkerframework.checker.nullness.qual.Nullable;
import org.rx.core.*;
import org.rx.io.KeyValueStore;
import java.io.Serializable;
import java.util.*;
import static org.rx.core.Constants.NON_UNCHECKED;
@Slf4j
public class DiskCache implements Cache, EventPublisher> {
static {
IOC.register(DiskCache.class, new DiskCache<>());
}
public final Delegate, Map.Entry> onExpired = Delegate.create();
final Cache> cache;
final KeyValueStore> store;
int defaultExpireSeconds = 60 * 60 * 24 * 365; //1 year
public DiskCache() {
this(1000);
}
public DiskCache(int cacheSize) {
cache = new MemoryCache<>(b -> b.maximumSize(cacheSize), this::onRemoval);
store = (KeyValueStore) KeyValueStore.getInstance(Object.class, DiskCacheItem.class);
}
void onRemoval(@Nullable TK key, DiskCacheItem item, @NonNull RemovalCause removalCause) {
if (item == null) {
return;
}
log.info("onRemoval {}[{}] -> {}", key, item.getExpiration(), removalCause);
if (item.value == null || removalCause == RemovalCause.REPLACED || removalCause == RemovalCause.EXPLICIT) {
return;
}
if (item.isExpired()) {
raiseEvent(onExpired, new AbstractMap.SimpleEntry<>(key, item.value));
return;
}
if (!(key instanceof Serializable && item.value instanceof Serializable)) {
return;
}
store.fastPut(key, item);
log.info("onRemoval copy to store {} -> {}ms", key, item.getExpiration() - System.currentTimeMillis());
}
@Override
public int size() {
return cache.size() + store.size();
}
@Override
public boolean containsKey(Object key) {
return get(key) != null;
}
@Override
public boolean containsValue(Object value) {
return cache.containsValue(value) || store.containsValue(value);
}
@SuppressWarnings(NON_UNCHECKED)
@Override
public TV get(Object key) {
boolean doRenew = false;
DiskCacheItem item = cache.get(key);
if (item == null) {
item = store.get(key);
doRenew = true;
}
return unwrap((TK) key, item, doRenew);
}
private TV unwrap(TK key, DiskCacheItem item, boolean doRenew) {
if (item == null) {
return null;
}
if (item.isExpired()) {
remove(key);
if (onExpired == null) {
return null;
}
Map.Entry args = new AbstractMap.SimpleEntry<>(key, item.value);
raiseEvent(onExpired, args);
return args.getValue();
}
if (doRenew && item.slidingRenew()) {
cache.put(key, item);
}
return item.value;
}
@Override
public TV put(TK key, TV value, CachePolicy policy) {
if (policy == null) {
policy = CachePolicy.absolute(defaultExpireSeconds);
}
DiskCacheItem old = cache.put(key, new DiskCacheItem<>(value, policy));
return unwrap(key, old, false);
}
@Override
public TV remove(Object key) {
DiskCacheItem remove = cache.remove(key);
if (remove == null) {
remove = store.remove(key);
}
return remove == null ? null : remove.value;
}
@Override
public void clear() {
cache.clear();
store.clear();
}
@Override
public Set keySet() {
return cache.keySet();
}
@Override
public Collection values() {
return Linq.from(keySet()).select(k -> get(k)).where(Objects::nonNull).toList();
}
@Override
public Set> entrySet() {
return Linq.from(keySet()).select(k -> {
TV v = get(k);
return v == null ? null : (Map.Entry) new AbstractMap.SimpleEntry<>(k, v);
}).where(Objects::nonNull).toSet();
}
}