View Javadoc

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   * Base implementation of a service.
50   *
51   * @since 1.0.0
52   */
53  public abstract class AbstractVCacheService implements VCacheFactory, VCacheManagement, VCacheLifecycleManager {
54      // List of Marshaller class names that support Serializable values. This is a slightly
55      // dodgy approach, but the rationale is to not be forced to make implementations
56      // visible, or to make it an attribute of a Marshaller. The fact is, this is a temporary
57      // hack for internal use only.
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      * Returns the logger for the implementation service.
103      *
104      * @return the logger for the implementation service.
105      */
106     protected abstract Logger log();
107 
108     /**
109      * Creates a {@link JvmCache} with the supplied parameters.
110      *
111      * @param name     the name of the cache
112      * @param settings the setting for the cache
113      * @param <K>      the key type
114      * @param <V>      the value type
115      * @return a freshly minted {@link JvmCache}
116      */
117     protected abstract <K, V> JvmCache<K, V> createJvmCache(String name, JvmCacheSettings settings);
118 
119     /**
120      * Creates a {@link TransactionalExternalCache} with the supplied parameters.
121      *
122      * @param name              the name of the cache
123      * @param settings          the settings for the cache
124      * @param valueMarshalling  the marshalling pair for values
125      * @param valueSerializable whether the values are {@link java.io.Serializable}
126      * @param <V>               the value type
127      * @return a freshly minted {@link TransactionalExternalCache}
128      */
129     protected abstract <V> TransactionalExternalCache<V> createTransactionalExternalCache(
130             String name, ExternalCacheSettings settings, MarshallingPair<V> valueMarshalling, boolean valueSerializable);
131 
132     /**
133      * Creates a {@link StableReadExternalCache} with the supplied parameters.
134      *
135      * @param name              the name of the cache
136      * @param settings          the settings for the cache
137      * @param valueMarshalling  the marshalling pair for values
138      * @param valueSerializable whether the values are {@link java.io.Serializable}
139      * @param <V>               the value type
140      * @return a freshly minted {@link StableReadExternalCache}
141      */
142     protected abstract <V> StableReadExternalCache<V> createStableReadExternalCache(
143             String name, ExternalCacheSettings settings, MarshallingPair<V> valueMarshalling, boolean valueSerializable);
144 
145     /**
146      * Creates a {@link DirectExternalCache} with the supplied parameters.
147      *
148      * @param name              the name of the cache
149      * @param settings          the settings for the cache
150      * @param valueMarshalling  the marshalling pair for values
151      * @param valueSerializable whether the values are {@link java.io.Serializable}
152      * @param <V>               the value type
153      * @return a freshly minted {@link DirectExternalCache}
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                     // Check if attempting to change the policy, a no no
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                     // Now always create a new cache wrapper, regardless of whether one existed before.
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 }