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