View Javadoc

1   package com.atlassian.cache.memory;
2   
3   import javax.annotation.Nonnull;
4   
5   import com.atlassian.cache.CacheLoader;
6   import com.atlassian.cache.CacheSettings;
7   import com.atlassian.cache.CacheSettingsBuilder;
8   import com.atlassian.cache.CacheSettingsDefaultsProvider;
9   import com.atlassian.cache.CachedReference;
10  import com.atlassian.cache.ManagedCache;
11  import com.atlassian.cache.impl.AbstractCacheManager;
12  import com.atlassian.cache.impl.ReferenceKey;
13  import com.atlassian.cache.impl.StrongSupplier;
14  import com.atlassian.cache.impl.WeakSupplier;
15  import com.atlassian.util.concurrent.Supplier;
16  
17  import com.google.common.cache.Cache;
18  import com.google.common.cache.CacheBuilder;
19  import com.google.common.cache.LoadingCache;
20  
21  import static java.util.concurrent.TimeUnit.MILLISECONDS;
22  
23  /**
24   * Maintains a mapping of name -> Cache and provides factory methods for creating and getting caches.
25   *
26   * @since 2.0
27   */
28  public class MemoryCacheManager extends AbstractCacheManager
29  {
30      @Nonnull
31      public MemoryCacheManager()
32      {
33          super(null);
34      }
35  
36      public MemoryCacheManager(CacheSettingsDefaultsProvider cacheSettingsDefaultsProvider)
37      {
38          super(cacheSettingsDefaultsProvider);
39      }
40  
41      @Nonnull
42      @Override
43      public <V> CachedReference<V> getCachedReference(@Nonnull final String name,
44                                                       @Nonnull final com.atlassian.cache.Supplier<V> supplier,
45                                                       @Nonnull final CacheSettings settings)
46      {
47          // Force the cache settings to be flushable.
48          final CacheSettings overridenSettings = mergeSettings(name,
49                  settings.override(new CacheSettingsBuilder().flushable().build()));
50  
51          return cacheCreationLocks.get(name).withLock(new com.atlassian.util.concurrent.Supplier<DelegatingCachedReference<V>>()
52          {
53              @Override
54              public DelegatingCachedReference<V> get()
55              {
56                  // We need to always create a new instance as any old loader may belong to a plugin that has gone away,
57                  // resulting in a whole world of ClassLoader pain.
58                  final DelegatingCachedReference.DelegatingReferenceRemovalListener<V> listener = new DelegatingCachedReference.DelegatingReferenceRemovalListener<V>();
59  
60                  LoadingCache<ReferenceKey, V> computingCache = createCacheBuilder(overridenSettings)
61                          .removalListener(listener)
62                          .build(new com.google.common.cache.CacheLoader<ReferenceKey, V>()
63                          {
64                              @Override
65                              public V load(@Nonnull final ReferenceKey key) throws Exception
66                              {
67                                  V value = supplier.get();
68                                  listener.onSupply(value);
69                                  return value;
70                              }
71                          });
72  
73                  DelegatingCachedReference<V> cache = DelegatingCachedReference.create(computingCache, name, overridenSettings);
74                  listener.setCachedReference(cache);
75                  caches.put(name, new WeakSupplier<ManagedCache>(cache));
76                  return cache;
77              }
78          });
79      }
80  
81      protected ManagedCache createSimpleCache(@Nonnull final String name, @Nonnull final CacheSettings settings)
82      {
83  
84          Supplier<ManagedCache> cacheSupplier = caches.get(name);
85          if (cacheSupplier != null)
86          {
87              ManagedCache cache = cacheSupplier.get();
88              if (cache != null)
89              {
90                  return cache;
91              }
92          }
93          return cacheCreationLocks.get(name).withLock(new com.atlassian.util.concurrent.Supplier<ManagedCache>()
94              {
95                  @Override
96                  public ManagedCache get()
97                  {
98                      if (!caches.containsKey(name))
99                      {
100                         DelegatingCache.DelegatingRemovalListener<Object,Object> listener = new DelegatingCache.DelegatingRemovalListener<Object,Object>();
101 
102                         final Cache<Object, Object> simpleCache = createCacheBuilder(settings)
103                                 .removalListener(listener)
104                                 .build();
105 
106                         DelegatingCache<Object,Object> cache = DelegatingCache.create(simpleCache, name, settings);
107                         listener.setCache(cache);
108 
109                         caches.put(name, new StrongSupplier<ManagedCache>(cache));
110                     }
111                     return caches.get(name).get();
112                 }
113             });
114     }
115 
116     protected <K, V> ManagedCache createComputingCache(@Nonnull final String name, @Nonnull final CacheSettings settings, final CacheLoader<K, V> loader)
117     {
118         return cacheCreationLocks.get(name).withLock(new com.atlassian.util.concurrent.Supplier<ManagedCache>()
119         {
120             @Override
121             public ManagedCache get()
122             {
123                 // We need to always create a new instance as any old loader may belong to a plugin that has gone away,
124                 // resulting in a whole world of ClassLoader pain.
125                 final DelegatingCache.DelegatingRemovalListener<K,V> listener = new DelegatingCache.DelegatingRemovalListener<K,V>();
126                 final BlockingCacheLoader<K,V> cacheLoader = new BlockingCacheLoader<K, V>(
127                         new com.google.common.cache.CacheLoader<K,V>()
128                         {
129                             @Override
130                             public V load(@Nonnull final K key) throws Exception
131                             {
132                                 V value = loader.load(key);
133                                 listener.onSupply(key, value);
134                                 return value;
135                             }
136 
137                         });
138 
139                 LoadingCache<K, V> computingCache = createCacheBuilder(settings)
140                         .removalListener(listener)
141                         .build(cacheLoader);
142                 DelegatingCache<K,V> cache = DelegatingCache.create(computingCache, name, settings, cacheLoader);
143                 listener.setCache(cache);
144                 caches.put(name, new WeakSupplier<ManagedCache>(cache));
145                 return cache;
146             }
147         });
148     }
149 
150     private static CacheBuilder<Object,Object> createCacheBuilder(CacheSettings settings)
151     {
152         final CacheBuilder<Object,Object> cacheBuilder = CacheBuilder.newBuilder();
153         if (null != settings.getMaxEntries())
154         {
155             cacheBuilder.maximumSize(settings.getMaxEntries());
156         }
157 
158         if (null != settings.getStatisticsEnabled() && settings.getStatisticsEnabled())
159         {
160             cacheBuilder.recordStats();
161         }
162 
163         if (null != settings.getExpireAfterAccess())
164         {
165             cacheBuilder.expireAfterAccess(settings.getExpireAfterAccess(), MILLISECONDS);
166         }
167         else if (null != settings.getExpireAfterWrite())
168         {
169             cacheBuilder.expireAfterWrite(settings.getExpireAfterWrite(), MILLISECONDS);
170         }
171         return cacheBuilder;
172     }
173 }