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.instrumentation.DefaultInstrumentRegistry;
16  import com.atlassian.instrumentation.SimpleTimer;
17  import com.atlassian.instrumentation.caches.CacheCollector;
18  import com.atlassian.instrumentation.caches.CacheKeys;
19  import com.atlassian.util.concurrent.Supplier;
20  
21  import com.google.common.cache.Cache;
22  import com.google.common.cache.CacheBuilder;
23  import com.google.common.cache.LoadingCache;
24  
25  import static java.util.concurrent.TimeUnit.MILLISECONDS;
26  
27  /**
28   * Maintains a mapping of name -> Cache and provides factory methods for creating and getting caches.
29   *
30   * @since 2.0
31   */
32  public class MemoryCacheManager extends AbstractCacheManager
33  {
34      @Nonnull
35      public MemoryCacheManager()
36      {
37          super(null);
38      }
39  
40      public MemoryCacheManager(CacheSettingsDefaultsProvider cacheSettingsDefaultsProvider)
41      {
42          super(cacheSettingsDefaultsProvider);
43      }
44  
45      @Nonnull
46      @Override
47      public <V> CachedReference<V> getCachedReference(@Nonnull final String name,
48              @Nonnull final com.atlassian.cache.Supplier<V> supplier,
49              @Nonnull final CacheSettings settings)
50      {
51          // Force the cache settings to be flushable.
52          final CacheSettings overridenSettings = mergeSettings(name,
53                  settings.override(new CacheSettingsBuilder().flushable().build()));
54  
55          return cacheCreationLocks.get(name).withLock(new com.atlassian.util.concurrent.Supplier<DelegatingCachedReference<V>>()
56          {
57              @Override
58              public DelegatingCachedReference<V> get()
59              {
60                  // We need to always create a new instance as any old loader may belong to a plugin that has gone away,
61                  // resulting in a whole world of ClassLoader pain.
62                  final DelegatingCachedReference.DelegatingReferenceRemovalListener<V> listener = new DelegatingCachedReference.DelegatingReferenceRemovalListener<V>();
63  
64                  final CacheCollector collector = new DefaultInstrumentRegistry().pullCacheCollector(name);
65  
66                  LoadingCache<ReferenceKey, V> computingCache = createCacheBuilder(overridenSettings)
67                          .removalListener(listener)
68                          .build(new com.google.common.cache.CacheLoader<ReferenceKey, V>()
69                          {
70                              // Wrap the loader with timing code so if stats are enabled we can get numbers.
71                              @Override
72                              public V load(@Nonnull final ReferenceKey key) throws Exception
73                              {
74                                  if(collector.isEnabled())
75                                  {
76                                      SimpleTimer timer = new SimpleTimer(CacheKeys.LOAD_TIME.toString());
77                                      timer.start();
78                                      try
79                                      {
80                                          V value = supplier.get();
81                                          listener.onSupply(value);
82                                          return value;
83                                      }
84                                      finally
85                                      {
86                                          timer.end();
87                                          collector.miss();
88                                          collector.getSplits().add(timer);
89                                      }
90                                  }
91                                  else
92                                  {
93                                      V value = supplier.get();
94                                      listener.onSupply(value);
95                                      return value;
96                                  }
97                              }
98                          });
99  
100                 DelegatingCachedReference<V> cache = DelegatingCachedReference.create(computingCache, name, overridenSettings, collector);
101                 listener.setCachedReference(cache);
102                 caches.put(name, new WeakSupplier<ManagedCache>(cache));
103                 return cache;
104             }
105         });
106     }
107 
108     protected ManagedCache createSimpleCache(@Nonnull final String name, @Nonnull final CacheSettings settings)
109     {
110 
111         Supplier<ManagedCache> cacheSupplier = caches.get(name);
112         if (cacheSupplier != null)
113         {
114             ManagedCache cache = cacheSupplier.get();
115             if (cache != null)
116             {
117                 return cache;
118             }
119         }
120         return cacheCreationLocks.get(name).withLock(new com.atlassian.util.concurrent.Supplier<ManagedCache>()
121         {
122             @Override
123             public ManagedCache get()
124             {
125                 if (!caches.containsKey(name))
126                 {
127                     DelegatingCache.DelegatingRemovalListener<Object, Object> listener = new DelegatingCache.DelegatingRemovalListener<Object, Object>();
128 
129                     final Cache<Object, Object> simpleCache = createCacheBuilder(settings)
130                             .removalListener(listener)
131                             .build();
132 
133                     DelegatingCache<Object, Object> cache = DelegatingCache.create(simpleCache, name, settings, null);
134                     listener.setCache(cache);
135 
136                     caches.put(name, new StrongSupplier<ManagedCache>(cache));
137                 }
138                 return caches.get(name).get();
139             }
140         });
141     }
142 
143     protected <K, V> ManagedCache createComputingCache(@Nonnull final String name,
144                                                        @Nonnull final CacheSettings settings,
145                                                        final CacheLoader<K, V> loader)
146     {
147         return cacheCreationLocks.get(name).withLock(new com.atlassian.util.concurrent.Supplier<ManagedCache>()
148         {
149             @Override
150             public ManagedCache get()
151             {
152                 // We need to always create a new instance as any old loader may belong to a plugin that has gone away,
153                 // resulting in a whole world of ClassLoader pain.
154                 final DelegatingCache.DelegatingRemovalListener<K, V> listener = new DelegatingCache.DelegatingRemovalListener<K, V>();
155 
156                 final CacheLoader<K, V> wrappedLoader = new CacheLoader<K, V>()
157                 {
158                     @Nonnull
159                     @Override
160                     public V load(@Nonnull K key)
161                     {
162                         V value = loader.load(key);
163                         listener.onSupply(key, value);
164                         return value;
165                     }
166                 };
167 
168                 final Cache<K, V> simpleCache = createCacheBuilder(settings)
169                         .removalListener(listener)
170                         .build();
171 
172                 DelegatingCache<K, V> cache = DelegatingCache.create(simpleCache, name, settings, wrappedLoader);
173                 listener.setCache(cache);
174                 caches.put(name, new WeakSupplier<ManagedCache>(cache));
175                 return cache;
176             }
177         });
178     }
179 
180     private static CacheBuilder<Object, Object> createCacheBuilder(CacheSettings settings)
181     {
182         final CacheBuilder<Object, Object> cacheBuilder = CacheBuilder.newBuilder();
183         if (null != settings.getMaxEntries())
184         {
185             cacheBuilder.maximumSize(settings.getMaxEntries());
186         }
187 
188         // Old form of stats collection - leave in place. It allows stats from the delegate.
189         if (null != settings.getStatisticsEnabled() && settings.getStatisticsEnabled())
190         {
191             cacheBuilder.recordStats();
192         }
193 
194         if (null != settings.getExpireAfterAccess())
195         {
196             cacheBuilder.expireAfterAccess(settings.getExpireAfterAccess(), MILLISECONDS);
197         }
198         else if (null != settings.getExpireAfterWrite())
199         {
200             cacheBuilder.expireAfterWrite(settings.getExpireAfterWrite(), MILLISECONDS);
201         }
202         return cacheBuilder;
203     }
204 }