1 package com.atlassian.vcache.internal.core.service;
2
3 import com.atlassian.vcache.JvmCache;
4 import com.atlassian.vcache.JvmCacheSettings;
5 import com.atlassian.vcache.VCacheException;
6 import com.atlassian.vcache.internal.core.RecursionDetector;
7 import com.google.common.cache.Cache;
8 import com.google.common.cache.CacheBuilder;
9 import com.google.common.util.concurrent.UncheckedExecutionException;
10
11 import java.util.Arrays;
12 import java.util.HashSet;
13 import java.util.Map;
14 import java.util.Optional;
15 import java.util.Set;
16 import java.util.concurrent.ExecutionException;
17 import java.util.concurrent.TimeUnit;
18 import java.util.concurrent.locks.ReentrantLock;
19 import java.util.function.Function;
20 import java.util.function.Supplier;
21
22 import static com.atlassian.vcache.internal.NameValidator.requireValidCacheName;
23 import static java.util.Objects.requireNonNull;
24
25
26
27
28
29
30
31
32 public class GuavaJvmCache<K, V> implements JvmCache<K, V> {
33 private final ReentrantLock globalLock = new ReentrantLock();
34 private final String name;
35 private final Cache<K, V> delegate;
36 private final RecursionDetector<K> recursionDetector = new RecursionDetector<>();
37
38
39
40
41
42
43
44 public GuavaJvmCache(String name, JvmCacheSettings settings) {
45 this.name = requireValidCacheName(name);
46
47 this.delegate = CacheBuilder.newBuilder()
48 .maximumSize(settings.getMaxEntries().get())
49 .expireAfterWrite(settings.getDefaultTtl().get().toMillis(), TimeUnit.MILLISECONDS)
50 .build();
51 }
52
53 @Override
54 public Set<K> getKeys() {
55 return withLock(() -> new HashSet<>(delegate.asMap().keySet()));
56 }
57
58 @Override
59 public Optional<V> get(K key) {
60 return withLock(() -> Optional.ofNullable(delegate.getIfPresent(key)));
61 }
62
63 @Override
64 public V get(K key, Supplier<? extends V> supplier) {
65 try (RecursionDetector.Guard<K> ignored = recursionDetector.guardOn(key)) {
66 return withLock(() -> {
67 try {
68 return delegate.get(requireNonNull(key), supplier::get);
69 } catch (UncheckedExecutionException | ExecutionException e) {
70 throw new VCacheException("Supplier failed", e);
71 }
72 });
73 }
74 }
75
76 @SafeVarargs
77 @Override
78 public final Map<K, V> getBulk(Function<Set<K>, Map<K, V>> factory, K... keys) {
79
80 return getBulk(factory, Arrays.asList(keys));
81 }
82
83 @Override
84 public Map<K, V> getBulk(Function<Set<K>, Map<K, V>> factory, Iterable<K> keys) {
85 return withLock(() -> LocalCacheUtils.getBulk(
86 factory,
87 keys,
88 this::get,
89 args -> put(args.key, args.value),
90 recursionDetector));
91 }
92
93 @Override
94 public void put(K key, V value) {
95 withLock(() -> delegate.put(requireNonNull(key), requireNonNull(value)));
96 }
97
98 @Override
99 public Optional<V> putIfAbsent(K key, V value) {
100 return withLock(() -> Optional.ofNullable(delegate.asMap().putIfAbsent(requireNonNull(key), requireNonNull(value))));
101 }
102
103 @Override
104 public boolean replaceIf(K key, V currentValue, V newValue) {
105 return withLock(() -> delegate.asMap().replace(requireNonNull(key), requireNonNull(currentValue), requireNonNull(newValue)));
106 }
107
108 @Override
109 public void remove(K key) {
110 withLock(() -> delegate.invalidate(key));
111 }
112
113 @Override
114 public void removeAll() {
115 withLock((Runnable) delegate::invalidateAll);
116 }
117
118 @Override
119 public boolean removeIf(K key, V value) {
120 return withLock(() -> delegate.asMap().remove(key, value));
121 }
122
123 @Override
124 public String getName() {
125 return name;
126 }
127
128 private <R> R withLock(Supplier<R> supplier) {
129 globalLock.lock();
130 try {
131 return supplier.get();
132 } finally {
133 globalLock.unlock();
134 }
135 }
136
137 private void withLock(Runnable runner) {
138 globalLock.lock();
139 try {
140 runner.run();
141 } finally {
142 globalLock.unlock();
143 }
144 }
145 }