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