package org.jtwig.parser.cache; import com.google.common.base.Function; import com.google.common.base.Optional; import com.google.common.util.concurrent.SettableFuture; import com.googlecode.concurrentlinkedhashmap.ConcurrentLinkedHashMap; import org.jtwig.environment.Environment; import org.jtwig.model.tree.Node; import org.jtwig.parser.JtwigParser; import org.jtwig.resource.reference.ResourceReference; import java.util.concurrent.ConcurrentMap; import java.util.concurrent.Future; public class InMemoryConcurrentPersistentTemplateCache implements TemplateCache { private final ConcurrentMap<ResourceReference, Future<Result>> cache; private final Function<Future<Result>, Result> retriever = new RetrieveFuture<>(); public InMemoryConcurrentPersistentTemplateCache () { this(101, Integer.MAX_VALUE); } public InMemoryConcurrentPersistentTemplateCache(int initialCapacity, int maxValue) { this.cache = new ConcurrentLinkedHashMap.Builder<ResourceReference, Future<Result>>() .initialCapacity(initialCapacity) .maximumWeightedCapacity(maxValue) .build(); } @Override public Node get(JtwigParser parser, Environment environment, ResourceReference resource) { Optional<Future<Result>> optional = Optional.fromNullable(cache.get(resource)); if (optional.isPresent()) { return retriever.apply(optional.get()).get(); } else { SettableFuture<Result> future = SettableFuture.create(); Future<Result> result = cache.putIfAbsent(resource, future); if (result == null) { try { Node node = parser.parse(environment, resource); future.set(new Result(Optional.of(node), Optional.<RuntimeException>absent())); return node; } catch (RuntimeException e) { cache.remove(resource); future.set(new Result(Optional.<Node>absent(), Optional.of(e))); throw e; } } else { return retriever.apply(result).get(); } } } public static class Result { private final Optional<Node> node; private final Optional<RuntimeException> exception; public Result(Optional<Node> node, Optional<RuntimeException> exception) { this.node = node; this.exception = exception; } public Node get () { if (!node.isPresent()) { throw exception.get(); } else { return node.get(); } } } }