1 package com.atlassian.vcache.internal.core.service;
2
3 import com.atlassian.marshalling.api.MarshallingPair;
4 import com.atlassian.vcache.DirectExternalCache;
5 import com.atlassian.vcache.ExternalCache;
6 import com.atlassian.vcache.ExternalCacheException;
7 import com.atlassian.vcache.ExternalCacheSettings;
8 import com.atlassian.vcache.JvmCache;
9 import com.atlassian.vcache.JvmCacheSettings;
10 import com.atlassian.vcache.Marshaller;
11 import com.atlassian.vcache.RequestCache;
12 import com.atlassian.vcache.StableReadExternalCache;
13 import com.atlassian.vcache.TransactionalExternalCache;
14 import com.atlassian.vcache.VCacheFactory;
15 import com.atlassian.vcache.internal.BegunTransactionalActivityHandler;
16 import com.atlassian.vcache.internal.ExternalCacheDetails;
17 import com.atlassian.vcache.internal.JvmCacheDetails;
18 import com.atlassian.vcache.internal.RequestCacheDetails;
19 import com.atlassian.vcache.internal.RequestContext;
20 import com.atlassian.vcache.internal.RequestMetrics;
21 import com.atlassian.vcache.internal.VCacheCreationHandler;
22 import com.atlassian.vcache.internal.VCacheLifecycleManager;
23 import com.atlassian.vcache.internal.VCacheManagement;
24 import com.atlassian.vcache.internal.VCacheSettingsDefaultsProvider;
25 import com.atlassian.vcache.internal.core.DefaultExternalCacheDetails;
26 import com.atlassian.vcache.internal.core.DefaultJvmCacheDetails;
27 import com.atlassian.vcache.internal.core.DefaultRequestCacheDetails;
28 import com.atlassian.vcache.internal.core.DefaultTransactionControlManager;
29 import com.atlassian.vcache.internal.core.ExternalCacheKeyGenerator;
30 import com.atlassian.vcache.internal.core.TransactionControlManager;
31 import com.atlassian.vcache.internal.core.metrics.MetricsCollector;
32 import com.google.common.annotations.VisibleForTesting;
33 import org.slf4j.Logger;
34
35 import java.time.Duration;
36 import java.util.Arrays;
37 import java.util.Collections;
38 import java.util.HashMap;
39 import java.util.HashSet;
40 import java.util.Map;
41 import java.util.Set;
42 import java.util.concurrent.ConcurrentHashMap;
43 import java.util.function.Function;
44 import java.util.function.Supplier;
45
46 import static com.atlassian.vcache.internal.NameValidator.requireValidCacheName;
47 import static java.util.Objects.requireNonNull;
48
49
50
51
52
53
54 public abstract class AbstractVCacheService implements VCacheFactory, VCacheManagement, VCacheLifecycleManager {
55
56
57
58
59 private static final Set<String> SERIALIZABLE_MARSHALLER_CLASS_NAMES =
60 Collections.unmodifiableSet(new HashSet<>(Arrays.asList(
61 "com.atlassian.vcache.marshallers.StringMarshaller",
62 "com.atlassian.vcache.marshallers.JavaSerializationMarshaller")));
63 private static final Set<String> SERIALIZABLE_MARSHALLING_CLASS_NAMES =
64 Collections.unmodifiableSet(new HashSet<>(Arrays.asList(
65 "com.atlassian.marshalling.jdk.JavaSerializationMarshalling",
66 "com.atlassian.marshalling.jdk.StringMarshalling")));
67
68 protected final Supplier<RequestContext> workContextContextSupplier;
69 protected final Supplier<RequestContext> threadLocalContextSupplier;
70 protected final TransactionControlManager transactionControlManager;
71 protected final MetricsCollector metricsCollector;
72
73 protected final ExternalCacheKeyGenerator externalCacheKeyGenerator;
74 protected final Duration lockTimeout;
75 private final VCacheSettingsDefaultsProvider defaultsProvider;
76 private final VCacheCreationHandler creationHandler;
77
78 private final ConcurrentHashMap<String, JvmCache> jvmCacheInstancesMap = new ConcurrentHashMap<>();
79 private final ConcurrentHashMap<String, JvmCacheDetails> jvmCacheDetailsMap = new ConcurrentHashMap<>();
80
81 private final ConcurrentHashMap<String, RequestCache> requestCacheInstancesMap = new ConcurrentHashMap<>();
82 private final ConcurrentHashMap<String, RequestCacheDetails> requestCacheDetailsMap = new ConcurrentHashMap<>();
83
84 private final ConcurrentHashMap<String, ExternalCache> externalCacheInstancesMap = new ConcurrentHashMap<>();
85 private final ConcurrentHashMap<String, ExternalCacheDetails> externalCacheDetailsMap = new ConcurrentHashMap<>();
86
87 public AbstractVCacheService(Supplier<RequestContext> threadLocalContextSupplier,
88 Supplier<RequestContext> workContextContextSupplier,
89 VCacheSettingsDefaultsProvider defaultsProvider,
90 VCacheCreationHandler creationHandler,
91 MetricsCollector metricsCollector,
92 ExternalCacheKeyGenerator externalCacheKeyGenerator,
93 BegunTransactionalActivityHandler begunTransactionalActivityHandler,
94 Duration lockTimeout) {
95 this.threadLocalContextSupplier = requireNonNull(threadLocalContextSupplier);
96 this.workContextContextSupplier = requireNonNull(workContextContextSupplier);
97 this.defaultsProvider = requireNonNull(defaultsProvider);
98 this.creationHandler = requireNonNull(creationHandler);
99 this.metricsCollector = requireNonNull(metricsCollector);
100 this.transactionControlManager = new DefaultTransactionControlManager(metricsCollector, begunTransactionalActivityHandler);
101 this.externalCacheKeyGenerator = requireNonNull(externalCacheKeyGenerator);
102 this.lockTimeout = requireNonNull(lockTimeout);
103 }
104
105
106
107
108
109
110 protected abstract Logger log();
111
112
113
114
115
116
117
118
119
120
121 protected abstract <K, V> JvmCache<K, V> createJvmCache(String name, JvmCacheSettings settings);
122
123
124
125
126
127
128
129
130
131
132
133 protected abstract <V> TransactionalExternalCache<V> createTransactionalExternalCache(
134 String name, ExternalCacheSettings settings, MarshallingPair<V> valueMarshalling, boolean valueSerializable);
135
136
137
138
139
140
141
142
143
144
145
146 protected abstract <V> StableReadExternalCache<V> createStableReadExternalCache(
147 String name, ExternalCacheSettings settings, MarshallingPair<V> valueMarshalling, boolean valueSerializable);
148
149
150
151
152
153
154
155
156
157
158
159 protected abstract <V> DirectExternalCache<V> createDirectExternalCache(
160 String name, ExternalCacheSettings settings, MarshallingPair<V> valueMarshalling, boolean valueSerializable);
161
162 @Override
163 @SuppressWarnings("unchecked")
164 public <K, V> JvmCache<K, V> getJvmCache(String name, JvmCacheSettings settings) {
165 return jvmCacheInstancesMap.computeIfAbsent(requireValidCacheName(name), s -> {
166 log().trace("Cache {}: creating the instance", name);
167 final JvmCacheSettings candidateSettings = defaultsProvider.getJvmDefaults(name).override(settings);
168 final JvmCacheSettings finalSettings =
169 creationHandler.jvmCacheCreation(new DefaultJvmCacheDetails(name, candidateSettings));
170 jvmCacheDetailsMap.put(name, new DefaultJvmCacheDetails(name, finalSettings));
171
172 return metricsCollector.wrap(createJvmCache(name, finalSettings));
173 });
174 }
175
176 @Override
177 @SuppressWarnings("unchecked")
178 public <K, V> RequestCache<K, V> getRequestCache(String name) {
179 return requestCacheInstancesMap.computeIfAbsent(requireValidCacheName(name), s -> {
180 log().trace("Cache {}: creating the instance", name);
181 creationHandler.requestCacheCreation(name);
182 requestCacheDetailsMap.put(name, new DefaultRequestCacheDetails(name));
183 return metricsCollector.wrap(new DefaultRequestCache(name, workContextContextSupplier, lockTimeout));
184 });
185 }
186
187 @Override
188 public <V> TransactionalExternalCache<V> getTransactionalExternalCache(String name, MarshallingPair<V> valueMarshalling, ExternalCacheSettings settings) {
189 final MarshallingPair<V> wrappedMarshalling = metricsCollector.wrap(valueMarshalling, name);
190
191 final TransactionalExternalCache<V> cache = obtainCache(
192 name, ExternalCacheDetails.BufferPolicy.FULLY, settings, finalSettings ->
193 createTransactionalExternalCache(
194 name, finalSettings, wrappedMarshalling, isValueSerializable(valueMarshalling)));
195 return metricsCollector.wrap(cache);
196 }
197
198 @Override
199 public <V> TransactionalExternalCache<V> getTransactionalExternalCache(
200 String name, @SuppressWarnings("deprecation") Marshaller<V> valueMarshaller, ExternalCacheSettings settings) {
201 final MarshallingPair<V> marshallingPair =
202 new MarshallingPair<>(valueMarshaller, valueMarshaller);
203 final MarshallingPair<V> wrappedMarshalling = metricsCollector.wrap(marshallingPair, name);
204
205 final TransactionalExternalCache<V> cache = obtainCache(
206 name, ExternalCacheDetails.BufferPolicy.FULLY, settings, finalSettings ->
207 createTransactionalExternalCache(
208 name, finalSettings, wrappedMarshalling, isValueSerializable(valueMarshaller)));
209 return metricsCollector.wrap(cache);
210 }
211
212 @Override
213 public <V> StableReadExternalCache<V> getStableReadExternalCache(String name, MarshallingPair<V> valueMarshalling, ExternalCacheSettings settings) {
214 final MarshallingPair<V> wrappedMarshalling = metricsCollector.wrap(valueMarshalling, name);
215 final StableReadExternalCache<V> cache = obtainCache(
216 name, ExternalCacheDetails.BufferPolicy.READ_ONLY, settings, finalSettings ->
217 createStableReadExternalCache(
218 name, finalSettings, wrappedMarshalling, isValueSerializable(valueMarshalling)));
219 return metricsCollector.wrap(cache);
220 }
221
222 @Override
223 public <V> StableReadExternalCache<V> getStableReadExternalCache(
224 String name, @SuppressWarnings("deprecation") Marshaller<V> valueMarshaller, ExternalCacheSettings settings) {
225 final MarshallingPair<V> marshallingPair =
226 new MarshallingPair<>(valueMarshaller, valueMarshaller);
227 final MarshallingPair<V> wrappedMarshalling = metricsCollector.wrap(marshallingPair, name);
228
229 final StableReadExternalCache<V> cache = obtainCache(
230 name, ExternalCacheDetails.BufferPolicy.READ_ONLY, settings, finalSettings ->
231 createStableReadExternalCache(
232 name, finalSettings, wrappedMarshalling, isValueSerializable(valueMarshaller)));
233 return metricsCollector.wrap(cache);
234 }
235
236 @Override
237 public <V> DirectExternalCache<V> getDirectExternalCache(String name, MarshallingPair<V> valueMarshalling, ExternalCacheSettings settings) {
238 final MarshallingPair<V> wrappedMarshalling = metricsCollector.wrap(valueMarshalling, name);
239 final DirectExternalCache<V> cache = obtainCache(
240 name, ExternalCacheDetails.BufferPolicy.NEVER, settings, finalSettings ->
241 createDirectExternalCache(
242 name, finalSettings, wrappedMarshalling, isValueSerializable(valueMarshalling)));
243 return metricsCollector.wrap(cache);
244 }
245
246 @Override
247 public <V> DirectExternalCache<V> getDirectExternalCache(
248 String name, @SuppressWarnings("deprecation") Marshaller<V> valueMarshaller, ExternalCacheSettings settings) {
249 final MarshallingPair<V> marshallingPair =
250 new MarshallingPair<>(valueMarshaller, valueMarshaller);
251 final MarshallingPair<V> wrappedMarshalling = metricsCollector.wrap(marshallingPair, name);
252
253 final DirectExternalCache<V> cache = obtainCache(
254 name, ExternalCacheDetails.BufferPolicy.NEVER, settings, finalSettings ->
255 createDirectExternalCache(
256 name, finalSettings, wrappedMarshalling, isValueSerializable(valueMarshaller)));
257 return metricsCollector.wrap(cache);
258 }
259
260 @Override
261 public void transactionSync(RequestContext context) {
262 transactionControlManager.syncAll(context);
263 }
264
265 @Override
266 public Set<String> transactionDiscard(RequestContext context) {
267 return transactionControlManager.discardAll(context);
268 }
269
270 @Override
271 public RequestMetrics metrics(RequestContext context) {
272 return metricsCollector.obtainRequestMetrics(context);
273 }
274
275 @Override
276 public Map<String, JvmCacheDetails> allJvmCacheDetails() {
277 final Map<String, JvmCacheDetails> result = new HashMap<>();
278 result.putAll(jvmCacheDetailsMap);
279 return result;
280 }
281
282 @Override
283 public Map<String, RequestCacheDetails> allRequestCacheDetails() {
284 final Map<String, RequestCacheDetails> result = new HashMap<>();
285 result.putAll(requestCacheDetailsMap);
286 return result;
287 }
288
289 @Override
290 public Map<String, ExternalCacheDetails> allExternalCacheDetails() {
291 final Map<String, ExternalCacheDetails> result = new HashMap<>();
292 result.putAll(externalCacheDetailsMap);
293 return result;
294 }
295
296 @VisibleForTesting
297 static <V> boolean isValueSerializable(@SuppressWarnings("deprecation") Marshaller<V> valueMarshaller) {
298 return SERIALIZABLE_MARSHALLER_CLASS_NAMES.contains(valueMarshaller.getClass().getName());
299 }
300
301 @VisibleForTesting
302 static <V> boolean isValueSerializable(MarshallingPair<V> valueMarshalling) {
303 return SERIALIZABLE_MARSHALLING_CLASS_NAMES.contains(valueMarshalling.getMarshaller().getClass().getName())
304 && SERIALIZABLE_MARSHALLING_CLASS_NAMES.contains(valueMarshalling.getUnmarshaller().getClass().getName());
305 }
306
307 @SuppressWarnings("unchecked")
308 private <C extends ExternalCache> C obtainCache(
309 String name,
310 ExternalCacheDetails.BufferPolicy policy,
311 ExternalCacheSettings settings,
312 Function<ExternalCacheSettings, C> factory) {
313 final ExternalCache candidateCache = externalCacheInstancesMap.compute(
314 requireValidCacheName(name),
315 (key, existing) -> {
316
317 if ((existing != null) && (externalCacheDetailsMap.get(name).getPolicy() != policy)) {
318 log().warn("Cache {}: unable to create cache with policy {}, as one already configured with policy {}",
319 name, ExternalCacheDetails.BufferPolicy.READ_ONLY, externalCacheDetailsMap.get(name).getPolicy());
320 throw new ExternalCacheException(ExternalCacheException.Reason.CREATION_FAILURE);
321 }
322
323
324 log().trace("Cache {}: creating the instance with policy {}", name, policy);
325 final ExternalCacheSettings candidateSettings =
326 defaultsProvider.getExternalDefaults(name).override(settings);
327 final ExternalCacheSettings finalSettings = creationHandler.externalCacheCreation(
328 new DefaultExternalCacheDetails(name, policy, candidateSettings));
329 externalCacheDetailsMap.put(
330 name, new DefaultExternalCacheDetails(name, policy, finalSettings));
331 return factory.apply(finalSettings);
332 });
333
334 return (C) candidateCache;
335 }
336 }