View Javadoc

1   package com.atlassian.cache.hazelcast;
2   
3   import javax.annotation.Nonnull;
4   
5   import com.atlassian.cache.Cache;
6   import com.atlassian.cache.CacheFactory;
7   import com.atlassian.cache.CacheLoader;
8   import com.atlassian.cache.CacheSettings;
9   import com.atlassian.cache.CacheSettingsBuilder;
10  import com.atlassian.cache.CacheSettingsDefaultsProvider;
11  import com.atlassian.cache.CachedReference;
12  import com.atlassian.cache.ManagedCache;
13  import com.atlassian.cache.Supplier;
14  import com.atlassian.cache.impl.AbstractCacheManager;
15  import com.atlassian.cache.impl.ReferenceKey;
16  import com.atlassian.cache.impl.StrongSupplier;
17  import com.atlassian.cache.impl.WeakSupplier;
18  import com.atlassian.hazelcast.serialization.OsgiSafe;
19  
20  import com.hazelcast.config.Config;
21  import com.hazelcast.config.MapConfig;
22  import com.hazelcast.core.HazelcastInstance;
23  import com.hazelcast.core.IMap;
24  
25  import static com.google.common.base.Preconditions.checkNotNull;
26  
27  /**
28   * Hazelcast implementation of the {@link com.atlassian.cache.CacheManager} contract
29   */
30  public class HazelcastCacheManager extends AbstractCacheManager
31  {
32  
33      protected static final String PREFIX = "atlassian-cache.";
34      protected static final String PREFIX_CACHE = PREFIX + "Cache.";
35      protected static final String PREFIX_CACHE_REFERENCE = PREFIX + "CacheReference.";
36  
37      private final HazelcastInstance hazelcast;
38      private final CacheFactory localCacheFactory;
39  
40      public HazelcastCacheManager(HazelcastInstance hazelcast, CacheFactory localCacheFactory,
41              CacheSettingsDefaultsProvider cacheSettingsDefaultsProvider)
42      {
43          super(cacheSettingsDefaultsProvider);
44  
45          this.hazelcast = hazelcast;
46          this.localCacheFactory = localCacheFactory;
47      }
48  
49      @Override
50      protected <K, V> ManagedCache createComputingCache(@Nonnull final String name, @Nonnull final CacheSettings settings, final CacheLoader<K, V> loader)
51      {
52          checkSettingsAreCompatible(name, settings);
53  
54          // when a loader is provided, always create a new ManagedCache to ensure the correct loader is being used.
55          // if the cache already existed, the backing values will be reused
56          return cacheCreationLocks.get(name).withLock(new com.atlassian.util.concurrent.Supplier<ManagedCache>()
57          {
58              @Override
59              public ManagedCache get()
60              {
61                  if (!caches.containsKey(name) || loader != null)
62                  {
63                      caches.put(name, new WeakSupplier<ManagedCache>((ManagedCache) doCreateCache(name, loader, settings)));
64                  }
65                  return caches.get(name).get();
66              }
67          });
68      }
69  
70      @Override
71      protected ManagedCache createSimpleCache(@Nonnull final String name, @Nonnull final CacheSettings settings)
72      {
73          checkSettingsAreCompatible(name, settings);
74  
75          ManagedCache existing = getManagedCache(name);
76          if (existing != null)
77          {
78              return existing;
79          }
80  
81          return cacheCreationLocks.get(name).withLock(new com.atlassian.util.concurrent.Supplier<ManagedCache>()
82          {
83              @Override
84              public ManagedCache get()
85              {
86                  if (!caches.containsKey(name))
87                  {
88                      caches.put(name, new StrongSupplier<ManagedCache>((ManagedCache) doCreateCache(name, null, settings)));
89                  }
90                  return caches.get(name).get();
91              }
92          });
93      }
94  
95      @Nonnull
96      @Override
97      public <V> CachedReference<V> getCachedReference(@Nonnull final String name, @Nonnull final Supplier<V> supplier,
98              @Nonnull final CacheSettings settings)
99      {
100         checkSettingsAreCompatible(name, settings);
101 
102         return cacheCreationLocks.get(name).withLock(new com.atlassian.util.concurrent.Supplier<CachedReference<V>>()
103         {
104             @Override
105             public CachedReference<V> get()
106             {
107                 caches.put(name, new WeakSupplier<ManagedCache>((ManagedCache) doCreateCachedReference(name, supplier, settings)));
108                 //noinspection unchecked
109                 return (CachedReference<V>) caches.get(name).get();
110             }
111         });
112     }
113 
114     protected void checkSettingsAreCompatible(String name, CacheSettings settings)
115     {
116     }
117 
118     protected MapConfig configureMap(String mapName, CacheSettings settings)
119     {
120         Config config = hazelcast.getConfig();
121         MapConfig mapConfig = config.getMapConfig(mapName);
122         mapConfig.setStatisticsEnabled(true);
123 
124         new HazelcastMapConfigConfigurator().configureMapConfig(settings, mapConfig);
125         config.addMapConfig(mapConfig);
126 
127         return mapConfig;
128     }
129 
130     protected <K, V> Cache<K, V> createDistributedCache(String name, CacheLoader<K, V> loader, CacheSettings settings)
131     {
132         String mapName = PREFIX_CACHE + name;
133         MapConfig config = configureMap(mapName, settings);
134         IMap<K, OsgiSafe<V>> map = hazelcast.getMap(mapName);
135 
136         return new HazelcastCache<K, V>(name, map, config, loader, settings);
137     }
138 
139     protected <V> CachedReference<V> createDistributedCachedReference(String name, Supplier<V> supplier, CacheSettings settings)
140     {
141         // override the settings to ensure the reference is flushable and the max is set to 1000. A low value for
142         // maxEntries would trigger continuous cache invalidations because of the way map eviction works in Hazelcast.
143         final CacheSettings overriddenSettings = checkNotNull(settings, "settings").override(
144                 new CacheSettingsBuilder().flushable().maxEntries(1000).build());
145 
146         String mapName = PREFIX_CACHE_REFERENCE + name;
147         MapConfig config = configureMap(mapName, overriddenSettings);
148         IMap<String, OsgiSafe<V>> map = hazelcast.getMap(mapName);
149 
150         return new HazelcastCachedReference<V>(name, map, config, supplier, overriddenSettings);
151     }
152 
153     protected <K, V> Cache<K, V> createHybridCache(String name, CacheLoader<K, V> loader, CacheSettings settings)
154     {
155         String mapName = PREFIX_CACHE + name;
156         MapConfig config = configureMap(mapName, settings);
157         IMap<K, Long> map = hazelcast.getMap(mapName);
158 
159         return new HazelcastHybridCache<K, V>(name, localCacheFactory, map, config, loader, settings);
160     }
161 
162     protected <V> CachedReference<V> createHybridCachedReference(String name, Supplier<V> supplier, CacheSettings settings)
163     {
164         String mapName = PREFIX_CACHE_REFERENCE + name;
165         MapConfig config = configureMap(mapName, settings);
166         IMap<ReferenceKey, Long> map = hazelcast.getMap(mapName);
167 
168         return new HazelcastHybridCachedReference<V>(name, localCacheFactory, map, supplier, settings);
169     }
170 
171     private <K, V> Cache<K, V> doCreateCache(String name, CacheLoader<K, V> loader, CacheSettings settings)
172     {
173         if (settings.getLocal(false))
174         {
175             return localCacheFactory.getCache(name, loader, settings);
176         }
177 
178         if (settings.getReplicateViaCopy(true))
179         {
180             return createDistributedCache(name, loader, settings);
181         }
182 
183         return createHybridCache(name, loader, settings);
184     }
185 
186     protected <V> CachedReference<V> doCreateCachedReference(String name, Supplier<V> supplier, CacheSettings settings)
187     {
188         if (settings.getLocal(false))
189         {
190             return localCacheFactory.getCachedReference(name, supplier, settings);
191         }
192 
193         // remote cached reference
194         if (settings.getReplicateViaCopy(true))
195         {
196             return createDistributedCachedReference(name, supplier, settings);
197         }
198 
199         return createHybridCachedReference(name, supplier, settings);
200     }
201 }