View Javadoc
1   package com.atlassian.cache.vcache;
2   
3   import java.io.Serializable;
4   import java.time.Duration;
5   import javax.annotation.Nonnull;
6   import javax.annotation.Nullable;
7   
8   import com.atlassian.cache.Cache;
9   import com.atlassian.cache.CacheLoader;
10  import com.atlassian.cache.CacheSettings;
11  import com.atlassian.cache.CacheSettingsDefaultsProvider;
12  import com.atlassian.cache.CachedReference;
13  import com.atlassian.cache.ManagedCache;
14  import com.atlassian.cache.Supplier;
15  import com.atlassian.cache.impl.AbstractCacheManager;
16  import com.atlassian.cache.impl.StrongSupplier;
17  import com.atlassian.cache.impl.WeakSupplier;
18  import com.atlassian.vcache.DirectExternalCache;
19  import com.atlassian.vcache.ExternalCacheSettings;
20  import com.atlassian.vcache.ExternalCacheSettingsBuilder;
21  import com.atlassian.vcache.JvmCache;
22  import com.atlassian.vcache.JvmCacheSettings;
23  import com.atlassian.vcache.JvmCacheSettingsBuilder;
24  import com.atlassian.vcache.StableReadExternalCache;
25  import com.atlassian.vcache.VCacheFactory;
26  import com.atlassian.vcache.marshallers.MarshallerFactory;
27  
28  import static java.util.Objects.requireNonNull;
29  
30  /**
31   * VCache implementation of the {@link com.atlassian.cache.CacheManager} contract.
32   *
33   * @since 2.9.0
34   */
35  public class VCacheCacheManager extends AbstractCacheManager
36  {
37      private static final String PREFIX = "atlassian-cache.";
38      private static final String PREFIX_CACHE = PREFIX + "Cache.";
39      private static final String PREFIX_HYBRID_LOCAL_CACHE = PREFIX + "Hybrid.Local.";
40      private static final String PREFIX_HYBRID_GLOBAL_CACHE = PREFIX + "Hybrid.Global.";
41      private static final String PREFIX_CACHE_REFERENCE = PREFIX + "CacheReference.";
42  
43      private final VCacheFactory vCacheFactory;
44  
45      public VCacheCacheManager(VCacheFactory vCacheFactory,
46                                CacheSettingsDefaultsProvider cacheSettingsDefaultsProvider)
47      {
48          super(cacheSettingsDefaultsProvider);
49          this.vCacheFactory = requireNonNull(vCacheFactory);
50      }
51  
52      @Override
53      protected <K, V> ManagedCache createComputingCache(final String name, final CacheSettings settings, @Nullable final CacheLoader<K, V> loader)
54      {
55          // when a loader is provided, always create a new ManagedCache to ensure the correct loader is being used.
56          // if the cache already existed, the backing values will be reused
57          return cacheCreationLocks.apply(name).withLock((java.util.function.Supplier<ManagedCache>) () -> {
58              if (!caches.containsKey(name) || loader != null)
59              {
60                  caches.put(name, new WeakSupplier<>((ManagedCache) doCreateCache(name, loader, settings)));
61              }
62              return caches.get(name).get();
63          });
64      }
65  
66      @Override
67      protected ManagedCache createSimpleCache(@Nonnull final String name, @Nonnull final CacheSettings settings)
68      {
69          ManagedCache existing = getManagedCache(name);
70          if (existing != null)
71          {
72              return existing;
73          }
74  
75          return cacheCreationLocks.apply(name).withLock((java.util.function.Supplier<ManagedCache>) () -> {
76              if (!caches.containsKey(name))
77              {
78                  caches.put(name, new StrongSupplier<>((ManagedCache) doCreateCache(name, null, settings)));
79              }
80              return caches.get(name).get();
81          });
82      }
83  
84  
85      @Nonnull
86      @Override
87      public <V> CachedReference<V> getCachedReference(@Nonnull final String name, @Nonnull final Supplier<V> supplier,
88                                                       @Nonnull final CacheSettings settings)
89      {
90          return cacheCreationLocks.apply(name).withLock((java.util.function.Supplier<CachedReference<V>>) () -> {
91              caches.put(name, new WeakSupplier<>((ManagedCache) doCreateCachedReference(name, supplier, settings)));
92              //noinspection unchecked
93              return (CachedReference<V>) caches.get(name).get();
94          });
95      }
96  
97      private <K, V> Cache<K, V> createLocalCache(String name, @Nullable CacheLoader<K, V> loader, CacheSettings settings)
98      {
99          final String mapName = PREFIX_CACHE + name;
100         final JvmCacheSettings vSettings = buildJvmSettings(settings);
101         final JvmCache<K, V> vcache = vCacheFactory.getJvmCache(mapName, vSettings);
102 
103         return new JvmCacheDelegate<>(name, vcache, loader, settings);
104     }
105 
106     private Cache<String, Serializable> createDistributedCache(String name, @Nullable CacheLoader<String, Serializable> loader, CacheSettings settings)
107     {
108         final ExternalCacheSettings vSettings = buildExternalCacheSettings(settings);
109 
110         final String mapName = PREFIX_CACHE + name;
111         final DirectExternalCache<Serializable> vcache =
112                 vCacheFactory.getDirectExternalCache(
113                         mapName, MarshallerFactory.serializableMarshaller(Serializable.class), vSettings);
114 
115         //noinspection unchecked
116         return new ExternalCacheDelegate(name, vcache, loader, settings);
117     }
118 
119     private <K, V> Cache createHybridCache(String name, @Nullable CacheLoader<K, V> loader, CacheSettings settings)
120     {
121         final JvmCacheSettings localSettings = buildJvmSettings(settings);
122         final ExternalCacheSettings externalSettings = buildExternalCacheSettings(settings);
123 
124         final JvmCache<K, Versioned<V>> localVersioned =
125                 vCacheFactory.getJvmCache(PREFIX_HYBRID_LOCAL_CACHE + name, localSettings);
126         final StableReadExternalCache<String> globalVersions =
127                 vCacheFactory.getStableReadExternalCache(
128                         PREFIX_HYBRID_GLOBAL_CACHE + name, MarshallerFactory.stringMarshaller(), externalSettings);
129 
130         return new HybridCache<>(name, localVersioned, globalVersions, loader, settings);
131     }
132 
133     private <K, V> Cache<K, V> doCreateCache(String name, @Nullable CacheLoader<K, V> loader, CacheSettings settings)
134     {
135         if (settings.getLocal(false))
136         {
137             return createLocalCache(name, loader, settings);
138         }
139 
140         if (settings.getReplicateViaCopy(true))
141         {
142             // This is just horrendous. No way to confirm if the keys are strings, and the values are instances
143             // of Serializable.
144             //noinspection unchecked
145             return (Cache<K, V>) createDistributedCache(name, (CacheLoader<String, Serializable>) loader, settings);
146         }
147 
148         //noinspection unchecked
149         return (Cache<K, V>) createHybridCache(name, loader, settings);
150     }
151 
152     private <V> CachedReference<V> createLocalCachedReference(String name, Supplier<V> supplier, CacheSettings settings)
153     {
154         final String mapName = PREFIX_CACHE_REFERENCE + name;
155         final JvmCacheSettings vSettings = buildJvmSettings(settings);
156         final JvmCache<String, V> vCache = vCacheFactory.getJvmCache(mapName, vSettings);
157 
158         return new JvmCachedReference<>(name, vCache, supplier, settings);
159     }
160 
161     private <V> CachedReference<V> createDistributedCachedReference(String name, Supplier<V> supplier, CacheSettings settings)
162     {
163         final String mapName = PREFIX_CACHE_REFERENCE + name;
164         final ExternalCacheSettings vSettings = buildExternalCacheSettings(settings);
165 
166         final StableReadExternalCache<Serializable> vCache =
167                 vCacheFactory.getStableReadExternalCache(
168                         mapName,
169                         MarshallerFactory.serializableMarshaller(Serializable.class),
170                         vSettings);
171 
172         return new ExternalCachedReference<>(name, vCache, supplier, settings);
173     }
174 
175     private <V> CachedReference<V> createHybridCachedReference(String name, Supplier<V> supplier, CacheSettings settings)
176     {
177         final JvmCacheSettings localSettings = buildJvmSettings(settings);
178         final ExternalCacheSettings externalSettings = buildExternalCacheSettings(settings);
179 
180         final JvmCache<String, Versioned<V>> localVersioned =
181                 vCacheFactory.getJvmCache(PREFIX_HYBRID_LOCAL_CACHE + name, localSettings);
182         final StableReadExternalCache<String> globalVersions =
183                 vCacheFactory.getStableReadExternalCache(
184                         PREFIX_HYBRID_GLOBAL_CACHE + name, MarshallerFactory.stringMarshaller(), externalSettings);
185 
186         return new HybridCachedReference<>(name, localVersioned, globalVersions, supplier, settings);
187     }
188 
189     private <V> CachedReference<V> doCreateCachedReference(String name, Supplier<V> supplier, CacheSettings settings)
190     {
191         if (settings.getLocal(false))
192         {
193             return createLocalCachedReference(name, supplier, settings);
194         }
195 
196         if (settings.getReplicateViaCopy(true))
197         {
198             return createDistributedCachedReference(name, supplier, settings);
199         }
200 
201         return createHybridCachedReference(name, supplier, settings);
202     }
203 
204     private JvmCacheSettings buildJvmSettings(CacheSettings settings)
205     {
206         final JvmCacheSettingsBuilder bob = new JvmCacheSettingsBuilder();
207         if (settings.getExpireAfterWrite() != null)
208         {
209             bob.defaultTtl(Duration.ofMillis(settings.getExpireAfterWrite()));
210         }
211         if (settings.getExpireAfterAccess() != null)
212         {
213             bob.defaultTtl(Duration.ofMillis(settings.getExpireAfterAccess()));
214         }
215         if (settings.getMaxEntries() != null)
216         {
217             bob.maxEntries(settings.getMaxEntries());
218         }
219 
220         return bob.build();
221     }
222 
223     private ExternalCacheSettings buildExternalCacheSettings(CacheSettings settings)
224     {
225         final ExternalCacheSettingsBuilder bob = new ExternalCacheSettingsBuilder();
226         if (settings.getExpireAfterWrite() != null)
227         {
228             bob.defaultTtl(Duration.ofMillis(settings.getExpireAfterWrite()));
229         }
230         if (settings.getExpireAfterAccess() != null)
231         {
232             bob.defaultTtl(Duration.ofMillis(settings.getExpireAfterAccess()));
233         }
234         if (settings.getMaxEntries() != null)
235         {
236             bob.entryCountHint(settings.getMaxEntries());
237         }
238 
239         return bob.build();
240     }
241 }