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