1 package com.atlassian.vcache.internal.core.service;
2
3 import java.util.Collections;
4 import java.util.HashMap;
5 import java.util.Map;
6 import java.util.Optional;
7 import java.util.Set;
8 import java.util.function.Supplier;
9 import javax.annotation.Nonnull;
10
11 import com.atlassian.vcache.PutPolicy;
12 import com.atlassian.vcache.internal.core.ExternalCacheKeyGenerator;
13
14 import com.google.common.collect.BiMap;
15 import com.google.common.collect.HashBiMap;
16 import com.google.common.collect.Maps;
17 import org.slf4j.Logger;
18 import org.slf4j.LoggerFactory;
19
20 import static java.util.Objects.requireNonNull;
21
22
23
24
25
26 public abstract class AbstractExternalCacheRequestContext<V>
27 {
28 private static final Logger log = LoggerFactory.getLogger(AbstractExternalCacheRequestContext.class);
29
30 protected final String name;
31 private final ExternalCacheKeyGenerator keyGenerator;
32 private final Supplier<String> partitionSupplier;
33
34
35 private final BiMap<String, String> internalToExternalKeyMap = Maps.synchronizedBiMap(HashBiMap.create());
36
37 private final Map<String, String> externalToInternalKeyMap = Collections.unmodifiableMap(
38 internalToExternalKeyMap.inverse());
39
40 private final Map<String, Optional<V>> internalKeyToValueMap = new HashMap<>();
41 private boolean hasRemoveAll;
42 private final Map<String, DeferredOperation<V>> keyedOperationMap = new HashMap<>();
43
44 protected AbstractExternalCacheRequestContext(ExternalCacheKeyGenerator keyGenerator,
45 String name,
46 Supplier<String> partitionSupplier)
47 {
48 this.keyGenerator = requireNonNull(keyGenerator);
49 this.name = requireNonNull(name);
50 this.partitionSupplier = requireNonNull(partitionSupplier);
51 }
52
53 protected abstract long cacheVersion();
54
55 protected void clearKeyMaps()
56 {
57 internalToExternalKeyMap.clear();
58 }
59
60 @Nonnull
61 public String externalEntryKeyFor(String internalKey)
62 {
63 final String cached = internalToExternalKeyMap.get(internalKey);
64 if (cached != null)
65 {
66 return cached;
67 }
68
69 final String result = keyGenerator.entryKey(
70 partitionSupplier.get(), name, cacheVersion(), internalKey);
71
72
73 internalToExternalKeyMap.forcePut(internalKey, result);
74 return result;
75 }
76
77
78
79
80
81
82
83
84 @Nonnull
85 public String internalEntryKeyFor(String externalKey)
86 {
87 return requireNonNull(externalToInternalKeyMap.get(externalKey));
88 }
89
90 @Nonnull
91 public Optional<Optional<V>> getValueRecorded(String internalKey)
92 {
93 return Optional.ofNullable(internalKeyToValueMap.get(internalKey));
94 }
95
96 public void recordValue(String internalKey, Optional<V> outcome)
97 {
98 log.trace("Cache {}, recording value for {}", name, internalKey);
99 internalKeyToValueMap.put(internalKey, outcome);
100 }
101
102 public void recordValues(Map<String, V> knownValues)
103 {
104 log.trace("Cache {}, recording {} known values", name, knownValues.size());
105 knownValues.entrySet().stream().collect(
106 () -> internalKeyToValueMap,
107 (m, e) -> m.put(e.getKey(), Optional.of(e.getValue())),
108 Map::putAll);
109 }
110
111 public void forgetValue(String internalKey)
112 {
113 log.trace("Cache {}, forgetting value for {}", name, internalKey);
114 internalKeyToValueMap.remove(internalKey);
115 }
116
117 public void forgetAllValues()
118 {
119 log.trace("Cache {}, forgetting all values", name);
120 internalKeyToValueMap.clear();
121 }
122
123 public void recordPut(String internalKey, V value, PutPolicy policy)
124 {
125 recordValue(internalKey, Optional.of(value));
126 keyedOperationMap.put(internalKey, DeferredOperation.putOperation(value, policy));
127 }
128
129 public void recordRemove(Iterable<String> internalKeys)
130 {
131 for (String internalKey : internalKeys)
132 {
133 recordValue(internalKey, Optional.empty());
134 keyedOperationMap.put(internalKey, DeferredOperation.removeOperation());
135 }
136 }
137
138 public void recordRemoveAll()
139 {
140 forgetAllValues();
141 hasRemoveAll = true;
142 keyedOperationMap.clear();
143 }
144
145 public boolean hasRemoveAll()
146 {
147 return hasRemoveAll;
148 }
149
150 public void forgetAll()
151 {
152 forgetAllValues();
153 hasRemoveAll = false;
154 keyedOperationMap.clear();
155 }
156
157 public Set<Map.Entry<String, DeferredOperation<V>>> getKeyedOperations()
158 {
159 return keyedOperationMap.entrySet();
160 }
161
162
163
164
165
166 public static class DeferredOperation<V>
167 {
168 private final boolean remove;
169 private final Optional<V> value;
170 private final Optional<PutPolicy> policy;
171
172 private DeferredOperation()
173 {
174 this.remove = true;
175 this.value = Optional.empty();
176 this.policy = Optional.empty();
177 }
178
179 private DeferredOperation(V value, PutPolicy policy)
180 {
181 this.remove = false;
182 this.value = Optional.of(value);
183 this.policy = Optional.of(policy);
184 }
185
186 public boolean isRemove()
187 {
188 return remove;
189 }
190
191 public boolean isPut()
192 {
193 return !remove;
194 }
195
196 public V getValue()
197 {
198 return value.get();
199 }
200
201 public PutPolicy getPolicy()
202 {
203 return policy.get();
204 }
205
206 public static <V> DeferredOperation<V> removeOperation()
207 {
208 return new DeferredOperation<>();
209 }
210
211 public static <V> DeferredOperation<V> putOperation(V value, PutPolicy policy)
212 {
213 return new DeferredOperation<>(value, policy);
214 }
215 }
216 }