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