filebot/source/net/filebot/Cache.java

203 lines
5.9 KiB
Java
Raw Normal View History

2014-04-19 02:30:29 -04:00
package net.filebot;
import static java.nio.charset.StandardCharsets.*;
import static java.util.Arrays.*;
import static java.util.stream.Collectors.*;
2016-03-08 12:47:17 -05:00
import static net.filebot.CachedResource.*;
2016-03-06 13:11:30 -05:00
import static net.filebot.Logging.*;
2016-10-27 16:03:42 -04:00
import java.io.InputStream;
import java.net.URL;
2016-03-06 13:11:30 -05:00
import java.time.Duration;
import java.util.List;
import java.util.function.Function;
2016-03-06 13:11:30 -05:00
import java.util.function.Predicate;
import org.w3c.dom.Document;
2016-03-08 12:47:17 -05:00
import net.filebot.CachedResource.Transform;
import net.sf.ehcache.Element;
public class Cache {
// grace period to make sure data is always fresh when TTL is almost about to be reached
public static final Duration ONE_DAY = Duration.ofHours(18);
public static final Duration ONE_WEEK = Duration.ofDays(6);
public static final Duration ONE_MONTH = Duration.ofDays(24);
2016-03-06 13:11:30 -05:00
public static Cache getCache(String name, CacheType type) {
2016-03-09 01:28:36 -05:00
return CacheManager.getInstance().getCache(name.toLowerCase() + "_" + type.ordinal(), type);
}
2016-03-08 12:47:17 -05:00
public <T> CachedResource<T, byte[]> bytes(T key, Transform<T, URL> resource) {
return new CachedResource<T, byte[]>(key, resource, fetchIfModified(), getBytes(), byte[].class::cast, ONE_DAY, this);
}
2016-10-27 16:03:42 -04:00
public <T> CachedResource<T, byte[]> bytes(T key, Transform<T, URL> resource, Transform<InputStream, InputStream> decompressor) {
return new CachedResource<T, byte[]>(key, resource, fetchIfModified(), getBytes(decompressor), byte[].class::cast, ONE_DAY, this);
}
2016-03-08 12:47:17 -05:00
public <T> CachedResource<T, String> text(T key, Transform<T, URL> resource) {
return new CachedResource<T, String>(key, resource, fetchIfModified(), getText(UTF_8), String.class::cast, ONE_DAY, this);
2016-03-07 07:30:09 -05:00
}
2016-03-08 12:47:17 -05:00
public <T> CachedResource<T, Document> xml(T key, Transform<T, URL> resource) {
return new CachedResource<T, Document>(key, resource, fetchIfModified(), validateXml(getText(UTF_8)), getXml(String.class::cast), ONE_DAY, this);
}
2016-03-08 12:47:17 -05:00
public <T> CachedResource<T, Object> json(T key, Transform<T, URL> resource) {
return new CachedResource<T, Object>(key, resource, fetchIfModified(), validateJson(getText(UTF_8)), getJson(String.class::cast), ONE_DAY, this);
}
private final net.sf.ehcache.Cache cache;
2016-04-25 02:31:33 -04:00
private final CacheType cacheType;
2016-04-25 02:31:33 -04:00
public Cache(net.sf.ehcache.Cache cache, CacheType cacheType) {
this.cache = cache;
2016-04-25 02:31:33 -04:00
this.cacheType = cacheType;
}
public String getName() {
return cache.getName();
}
public CacheType getCacheType() {
return cacheType;
}
2016-03-06 13:11:30 -05:00
public Object get(Object key) {
try {
return getElementValue(cache.get(key));
2016-03-06 13:11:30 -05:00
} catch (Exception e) {
debug.warning(format("Cache get: %s => %s", key, e));
}
2016-03-06 13:11:30 -05:00
return null;
}
public Object computeIf(Object key, Predicate<Element> condition, Compute<?> compute) throws Exception {
2016-03-06 13:11:30 -05:00
// get if present
Element element = null;
try {
element = cache.get(key);
2016-03-10 02:22:47 -05:00
if (element != null && !condition.test(element)) {
return getElementValue(element);
}
} catch (Exception e) {
debug.warning(format("Cache computeIf: %s => %s", key, e));
}
2016-03-06 13:11:30 -05:00
// compute if absent
Object value = compute.apply(element);
put(key, value);
2016-03-06 13:11:30 -05:00
return value;
}
public Object computeIfAbsent(Object key, Compute<?> compute) throws Exception {
2016-03-10 02:22:47 -05:00
return computeIf(key, it -> it == null, compute);
}
2016-03-06 13:11:30 -05:00
public void put(Object key, Object value) {
try {
cache.put(createElement(key, value));
2016-03-06 13:11:30 -05:00
} catch (Exception e) {
debug.warning(format("Cache put: %s => %s", key, e));
2016-03-06 13:11:30 -05:00
}
}
protected Object getElementValue(Element element) {
return element == null ? null : element.getObjectValue();
}
protected Element createElement(Object key, Object value) {
return new Element(key, value);
}
public void remove(Object key) {
try {
cache.remove(key);
2016-03-06 13:11:30 -05:00
} catch (Exception e) {
debug.warning(format("Cache remove: %s => %s", key, e));
}
}
public void flush() {
try {
cache.flush();
} catch (Exception e) {
debug.warning(format("Cache flush: %s => %s", cache.getName(), e));
}
}
2019-02-14 05:37:01 -05:00
public void clear() {
try {
cache.removeAll();
} catch (Exception e) {
debug.warning(format("Cache clear: %s => %s", cache.getName(), e));
}
}
@Override
public String toString() {
return cache.getName();
}
public static Predicate<Element> isStale(Duration expirationTime) {
return element -> System.currentTimeMillis() - element.getLatestOfCreationAndUpdateTime() > expirationTime.toMillis();
}
@FunctionalInterface
public interface Compute<R> {
R apply(Element element) throws Exception;
}
public <V> TypedCache<V> typed(Function<Object, V> read, Function<V, Object> write) {
2016-04-25 02:31:33 -04:00
return new TypedCache<V>(cache, cacheType, read, write);
}
public <V> TypedCache<V> cast(Class<V> cls) {
2016-04-25 02:31:33 -04:00
return new TypedCache<V>(cache, cacheType, it -> cls.cast(it), it -> it);
}
public <V> TypedCache<List<V>> castList(Class<V> cls) {
2016-04-25 02:31:33 -04:00
return new TypedCache<List<V>>(cache, cacheType, it -> it == null ? null : stream((Object[]) it).map(cls::cast).collect(toList()), it -> it == null ? null : it.toArray());
}
public static class TypedCache<V> extends Cache {
private final Function<Object, V> read;
private final Function<V, Object> write;
2016-04-25 02:31:33 -04:00
public TypedCache(net.sf.ehcache.Cache cache, CacheType cacheType, Function<Object, V> read, Function<V, Object> write) {
super(cache, cacheType);
this.read = read;
this.write = write;
}
@Override
public V get(Object key) {
return (V) super.get(key);
}
@Override
public V computeIf(Object key, Predicate<Element> condition, Compute<?> compute) throws Exception {
return (V) super.computeIf(key, condition, compute);
}
2016-03-09 15:36:28 -05:00
@Override
public V computeIfAbsent(Object key, Compute<?> compute) throws Exception {
return (V) super.computeIfAbsent(key, compute);
}
@Override
protected Object getElementValue(Element element) {
return read.apply(super.getElementValue(element));
}
@Override
protected Element createElement(Object key, Object value) {
return super.createElement(key, write.apply((V) value));
}
}
}