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.
commons.box.app.DataCache Maven / Gradle / Ivy
package commons.box.app;
import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import commons.box.app.func.DataConsumer;
import commons.box.app.func.DataFunction;
import commons.box.util.Maps;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.io.Serializable;
import java.util.Arrays;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.TimeUnit;
import java.util.function.Function;
import java.util.function.Supplier;
/**
* Cache 实现 使用了 guava cache
*
* 此缓存具备序列化声明,避免目标应用因此对象而导致序列化失败。但序列化时不会序列化内部具体缓存内容,仅序列化缓存的配置选项。
*/
public class DataCache implements Serializable {
private final transient Cache> cache;
private final Spec spec;
private final DataFunction loader;
private final DataConsumer> builderConfig;
protected DataCache() {
this(null, null, null);
}
public DataCache(@Nullable Spec spec) {
this(spec, null, null);
}
public DataCache(@Nullable Spec spec, @Nullable DataFunction loader) {
this(spec, loader, null);
}
public DataCache(@Nullable Spec spec, @Nullable DataFunction loader, @Nullable DataConsumer> builderConfig) {
this.spec = spec;
this.loader = loader;
this.builderConfig = builderConfig;
CacheBuilder cb = CacheBuilder.newBuilder();
if (this.spec != null) {
if (spec.getMax() > 0) cb = cb.maximumSize(spec.getMax());
if (spec.getExpireAfterAccess() > 0) cb = cb.expireAfterAccess(spec.getExpireAfterAccess(), TimeUnit.MILLISECONDS);
if (spec.getExpireAfterWrite() > 0) cb = cb.expireAfterWrite(spec.getExpireAfterWrite(), TimeUnit.MILLISECONDS);
if (spec.getInitCapacity() > 0) cb = cb.initialCapacity(spec.getInitCapacity());
}
if (this.builderConfig != null) this.builderConfig.accept(cb);
this.cache = cb.build();
}
private Object readResolve() {
return new DataCache<>(this.spec, this.loader, this.builderConfig);
}
public static DataCache build() {
return build(Spec.from(-1), null);
}
public static DataCache build(DataFunction loader) {
return build(Spec.from(-1), loader);
}
public static DataCache build(long max) {
return build(Spec.from(max));
}
public static DataCache build(long max, long expireAfterAccess) {
return build(Spec.from(max, expireAfterAccess), null, null);
}
public static DataCache build(long max, long expireAfterAccess, long expireAfterWrite) {
return build(Spec.from(max, expireAfterAccess, expireAfterWrite));
}
public static DataCache build(long max, DataFunction loader) {
return build(Spec.from(max), loader);
}
public static DataCache build(long max, long expireAfterAccess, DataFunction loader) {
return build(Spec.from(max, expireAfterAccess), loader, null);
}
public static DataCache build(long max, long expireAfterAccess, long expireAfterWrite, DataFunction loader) {
return build(Spec.from(max, expireAfterAccess, expireAfterWrite), loader);
}
public static DataCache build(@Nullable Spec spec) {
return build(spec, null, null);
}
public static DataCache build(@Nullable Spec spec, @Nullable DataFunction loader) {
return build(spec, loader, null);
}
public static DataCache build(@Nullable Spec spec, @Nullable DataFunction loader, @Nullable DataConsumer> builderConfig) {
return new DataCache<>(spec, loader, builderConfig);
}
@Nonnull
protected SafeKey createKey(Object... param) {
return new SafeKey(param);
}
@Nonnull
protected SafeObject createEntry(Function supplier, Object... key) {
return new SafeObject<>((supplier != null) ? supplier.apply(key) : null);
}
@Nonnull
protected SafeObject createEntry(T value) {
return new SafeObject<>(value);
}
@Nullable
protected Object[] keyValue(SafeKey key) {
return (key != null) ? key.objs() : null;
}
@Nullable
protected T entryValue(SafeObject entry) {
return (entry != null) ? entry.getObject() : null;
}
/**
* 内部操作 cache
*
* @return
*/
protected Cache> cache() {
return this.cache;
}
/**
* 获取值,如果失败则调用loader重新获取值
*
* 只有当值存在或者已经声明了 loader 的情况下才会返回正确结果,否则返回空
*
* @return
*/
public T get(Object... key) {
try {
return checkAndGet(key);
} catch (Throwable ignored) {
}
return this.loader.apply(key);
}
/**
* 与 get 方法相同,此处检查内部异常,如果存在异常则抛出
*
* @param key
* @return
* @throws AppError
*/
public T checkAndGet(Object... key) throws AppError {
if (key == null) throw AppError.error("不允许空的 key");
try {
SafeKey k = this.createKey(key);
SafeObject so = this.cache.getIfPresent(k);
if (so != null) return this.entryValue(so);
if (this.loader == null) return null;
so = this.createEntry(this.loader.apply(key));
this.cache.put(k, so);
return this.entryValue(so);
} catch (Throwable e) {
throw AppError.error("缓存获取值失败 key=" + Arrays.toString(key));
}
}
/**
* 获取对应值,如果值未命中则使用 supplier 中的定义返回内容
*
* 与 guava cache 不同的是此处的 key 可能是参数化数组,为了写法上的方便将key值放置在后面
*
* @param supplier
* @param key
* @return
*/
public T getOr(Function supplier, Object... key) {
if (supplier == null) return this.get(key);
try {
return checkAndGetOr(supplier, key);
} catch (Throwable ignored) {
}
return supplier.apply(key);
}
/**
* 获取对应值
*
* 与 guava cache 不同的是此处的 key 可能是参数化数组,为了写法上的方便将key值放置在后面
*
* @param supplier
* @param key
* @return
* @throws AppError
*/
public T checkAndGetOr(Function supplier, Object... key) throws AppError {
if (key == null) throw AppError.error("不允许空的 key");
if (supplier == null) return this.checkAndGet(key);
try {
SafeKey k = this.createKey(key);
SafeObject so = this.cache.getIfPresent(k);
if (so != null) return this.entryValue(so);
so = this.createEntry(supplier.apply(key));
this.cache.put(k, so);
return this.entryValue(so);
} catch (Throwable e) {
throw AppError.error("缓存获取值失败 key=" + Arrays.toString(key));
}
}
/**
* 尝试获取 如果失败则返回 空
*
* 此方法不会调用内部 loader,仅根据当前快照直接返回值
*
* @param key
* @return
*/
public T getIfPresent(Object... key) {
SafeObject val = this.cache.getIfPresent(this.createKey(key));
return (val != null) ? this.entryValue(val) : null;
}
/**
* 放入新值
*
* @param key
* @param value
*/
public void put(Object[] key, T value) {
this.cache.put(this.createKey(key), this.createEntry(value));
}
/**
* 放入新值
*
* @param map
*/
public void putAll(Map map) {
if (map == null) return;
map.forEach(this::put);
}
public void refresh(Object... key) {
if (key == null) return;
this.cache.invalidate(this.createKey(key));
}
public void refreshAll(Iterable keys) {
if (keys == null) return;
for (Object[] k : keys) this.refresh(k);
}
public void refreshAll() {
this.cache.cleanUp();
this.cache.invalidateAll();
}
public Map doAsMap() {
ConcurrentMap> map = this.cache.asMap();
if (map == null) return Maps.immmap();
Map am = new LinkedHashMap<>();
map.forEach((k, v) -> {
Object[] p = this.keyValue(k);
if (p == null) return;
T e = this.entryValue(v);
if (e == null) return;
am.put(p, e);
});
return Maps.immmap(am);
}
/**
* 用于类型转换 给定的 supplier 不会执行 仅作为类型判断
*
* 一般用于通用类型定义但需额外使用特定类型的 get 时需要
*
* @param supplier
* @param
* @return
*/
@SuppressWarnings("unchecked")
@Nonnull
public
DataCache
as(Supplier
supplier) {
return (DataCache
) this;
}
/**
* 用于类型转换 给定的 supplier 不会执行 仅作为类型判断
*
* 一般用于通用类型定义但需额外使用特定类型的 get 时需要
*
* @param supplier
* @param
* @return
*/
@SuppressWarnings("unchecked")
@Nonnull
public
DataCache
as(Function supplier) {
return (DataCache) this;
}
/**
* 缓存特性 各特性值为-1时使用默认值
*/
public static final class Spec implements Serializable {
private final long max;
private final long expireAfterAccess;
private final long expireAfterWrite;
private final int initCapacity;
public Spec(long max) {
this(max, -1, -1, -1);
}
public Spec(long max, long expireAfterAccess) {
this(max, expireAfterAccess, -1, -1);
}
public Spec(long max, long expireAfterAccess, long expireAfterWrite) {
this(max, expireAfterAccess, expireAfterWrite, -1);
}
public Spec(long max, long expireAfterAccess, long expireAfterWrite, int initCapacity) {
this.max = max;
this.expireAfterAccess = expireAfterAccess;
this.expireAfterWrite = expireAfterWrite;
this.initCapacity = initCapacity;
}
public static Spec from(long max) {
return from(max, -1, -1, -1);
}
public static Spec from(long max, long expireAfterAccess) {
return from(max, expireAfterAccess, -1, -1);
}
public static Spec from(long max, long expireAfterAccess, long expireAfterWrite) {
return from(max, expireAfterAccess, expireAfterWrite, -1);
}
public static Spec from(long max, long expireAfterAccess, long expireAfterWrite, int initCapacity) {
return new Spec(max, expireAfterAccess, expireAfterWrite, initCapacity);
}
public long getMax() {
return max;
}
public long getExpireAfterAccess() {
return expireAfterAccess;
}
public long getExpireAfterWrite() {
return expireAfterWrite;
}
public int getInitCapacity() {
return initCapacity;
}
}
}