View Javadoc
1   package com.atlassian.cache.hazelcast;
2   
3   import java.util.Collection;
4   
5   import javax.annotation.Nonnull;
6   
7   import com.atlassian.cache.Cache;
8   import com.atlassian.cache.CacheEntryEvent;
9   import com.atlassian.cache.CacheEntryListener;
10  import com.atlassian.cache.CacheException;
11  import com.atlassian.cache.CacheLoader;
12  import com.atlassian.cache.ManagedCache;
13  import com.atlassian.cache.Supplier;
14  import com.atlassian.cache.impl.CacheEntryListenerSupport;
15  import com.atlassian.cache.impl.CacheLoaderSupplier;
16  import com.atlassian.cache.impl.DefaultCacheEntryEvent;
17  import com.atlassian.cache.impl.ValueCacheEntryListenerSupport;
18  import com.atlassian.hazelcast.serialization.OsgiSafe;
19  
20  import com.google.common.base.MoreObjects;
21  import com.google.common.base.Throwables;
22  import com.hazelcast.core.EntryAdapter;
23  import com.hazelcast.core.EntryEvent;
24  import com.hazelcast.core.IMap;
25  
26  import org.slf4j.Logger;
27  import org.slf4j.LoggerFactory;
28  
29  import static com.atlassian.cache.hazelcast.OsgiSafeUtils.unwrap;
30  import static com.atlassian.cache.hazelcast.OsgiSafeUtils.wrap;
31  import static com.google.common.base.Preconditions.checkNotNull;
32  
33  /**
34   * Hazelcast implementation of the {@link Cache} and {@link ManagedCache} interfaces
35   *
36   * @since 2.4.0
37   */
38  public class HazelcastCache<K, V> extends ManagedCacheSupport implements Cache<K, V>
39  {
40      private static final Logger log = LoggerFactory.getLogger(HazelcastCache.class);
41  
42      private final CacheLoader<K, V> cacheLoader;
43      private final IMap<K, OsgiSafe<V>> map;
44      private final CacheVersion cacheVersion;
45  
46      private final CacheEntryListenerSupport<K, OsgiSafe<V>> listenerSupport = new ValueCacheEntryListenerSupport<K, OsgiSafe<V>>()
47      {
48          @Override
49          protected void initValue(final CacheEntryListenerSupport<K, OsgiSafe<V>> actualListenerSupport)
50          {
51              map.addEntryListener(new HazelcastCacheEntryListener<>(actualListenerSupport), true);
52          }
53  
54          @Override
55          protected void initValueless(final CacheEntryListenerSupport<K, OsgiSafe<V>> actualListenerSupport)
56          {
57              map.addEntryListener(new HazelcastCacheEntryListener<>(actualListenerSupport), false);
58          }
59      };
60  
61      public HazelcastCache(String name, IMap<K, OsgiSafe<V>> map, CacheLoader<K, V> cacheLoader,  CacheVersion cacheVersion, HazelcastCacheManager cacheManager)
62      {
63          super(name, cacheManager);
64  
65          this.map = map;
66          this.cacheVersion = checkNotNull(cacheVersion);
67          if (cacheLoader != null)
68          {
69              this.cacheLoader = new CacheVersionAwareCacheLoader<>(cacheLoader, this.cacheVersion);
70          }
71          else
72          {
73              this.cacheLoader = cacheLoader;
74          }
75      }
76  
77      @Override
78      public void clear()
79      {
80          cleanupMap();
81      }
82  
83      @Override
84      public boolean containsKey(@Nonnull K k)
85      {
86          return map.containsKey(k);
87      }
88  
89      @SuppressWarnings ("unchecked")
90      @Override
91      public V get(@Nonnull final K key)
92      {
93          return getOrLoad(key, cacheLoader == null ? null : new CacheLoaderSupplier<K, V>(key, cacheLoader));
94      }
95  
96      @Nonnull
97      @Override
98      public V get(@Nonnull final K key, @Nonnull final Supplier<? extends V> valueSupplier)
99      {
100         return getOrLoad(key, valueSupplier);
101     }
102 
103     @Nonnull
104     @Override
105     public Collection<K> getKeys()
106     {
107         return map.keySet();
108     }
109 
110     @Override
111     public void put(@Nonnull K key, @Nonnull V value)
112     {
113         map.put(checkNotNull(key, "key"), wrap(checkNotNull(value, "value")));
114     }
115 
116     @Override
117     public V putIfAbsent(@Nonnull K key, @Nonnull V value)
118     {
119         return unwrap(map.putIfAbsent(checkNotNull(key, "key"), wrap(checkNotNull(value, "value"))));
120     }
121 
122     @Override
123     public void remove(@Nonnull K key)
124     {
125         map.remove(checkNotNull(key, "key"));
126     }
127 
128     @Override
129     public boolean remove(@Nonnull K key, @Nonnull V value)
130     {
131         return map.remove(checkNotNull(key, "key"), wrap(checkNotNull(value, "value")));
132     }
133 
134     @Override
135     public void removeAll()
136     {
137         cleanupMap();
138     }
139 
140     private void cleanupMap()
141     {
142       cacheVersion.incrementAndGet();
143       map.clear();
144     }
145 
146     @Override
147     public boolean replace(@Nonnull K key, @Nonnull V oldValue, @Nonnull V newValue)
148     {
149         return map.replace(checkNotNull(key, "key"),
150                 wrap(checkNotNull(oldValue, "oldValue")), wrap(checkNotNull(newValue, "newValue")));
151     }
152 
153     @Override
154     public void addListener(@Nonnull CacheEntryListener<K, V> listener, boolean includeValues)
155     {
156         listenerSupport.add(new OsgiSafeCacheEntryListener<K, V>(listener), includeValues);
157     }
158 
159     @Override
160     public void removeListener(@Nonnull CacheEntryListener<K, V> listener)
161     {
162         listenerSupport.remove(new OsgiSafeCacheEntryListener<K, V>(listener));
163     }
164 
165     @Nonnull
166     @Override
167     protected String getHazelcastMapName()
168     {
169         return map.getName();
170     }
171 
172     private static class OsgiSafeCacheEntryEvent<K, V> extends DefaultCacheEntryEvent<K, V>
173     {
174         public OsgiSafeCacheEntryEvent(CacheEntryEvent<K, OsgiSafe<V>> event)
175         {
176             super(event.getKey(), unwrap(event.getValue()), unwrap(event.getOldValue()));
177         }
178     }
179 
180     private static class OsgiSafeCacheEntryListener<K, V> implements CacheEntryListener<K, OsgiSafe<V>>
181     {
182         private final CacheEntryListener<K, V> delegate;
183 
184         private OsgiSafeCacheEntryListener(CacheEntryListener<K, V> listener)
185         {
186             this.delegate = checkNotNull(listener, "listener");
187         }
188 
189         @Override
190         public void onAdd(@Nonnull CacheEntryEvent<K, OsgiSafe<V>> event)
191         {
192             delegate.onAdd(new OsgiSafeCacheEntryEvent<>(event));
193         }
194 
195         @Override
196         public void onEvict(@Nonnull CacheEntryEvent<K, OsgiSafe<V>> event)
197         {
198             delegate.onEvict(new OsgiSafeCacheEntryEvent<>(event));
199         }
200 
201         @Override
202         public void onRemove(@Nonnull CacheEntryEvent<K, OsgiSafe<V>> event)
203         {
204             delegate.onRemove(new OsgiSafeCacheEntryEvent<>(event));
205         }
206 
207         @Override
208         public void onUpdate(@Nonnull CacheEntryEvent<K, OsgiSafe<V>> event)
209         {
210             delegate.onUpdate(new OsgiSafeCacheEntryEvent<>(event));
211         }
212 
213         @Override
214         public boolean equals(Object o)
215         {
216             if (this == o)
217             {
218                 return true;
219             }
220             if (o == null || getClass() != o.getClass())
221             {
222                 return false;
223             }
224 
225             OsgiSafeCacheEntryListener that = (OsgiSafeCacheEntryListener) o;
226             return delegate.equals(that.delegate);
227         }
228 
229         @Override
230         public int hashCode()
231         {
232             return delegate.hashCode();
233         }
234     }
235 
236 
237     private V getOrLoad(final K key, final Supplier<? extends V> valueSupplier)
238     {
239         try
240         {
241             OsgiSafe<V> value = map.get(checkNotNull(key, "key"));
242             if (value != null)
243             {
244                 return value.getValue();
245             }
246             else if (valueSupplier == null)
247             {
248                 return null;
249             }
250 
251             V newValue = valueSupplier.get();
252             //noinspection ConstantConditions
253             if (newValue == null)
254             {
255                 throw new CacheException("The provided cacheLoader returned null. Null values are not supported.");
256             }
257             value = wrap(newValue);
258             OsgiSafe<V> current = map.putIfAbsent(key, value);
259 
260             return unwrap(MoreObjects.firstNonNull(current, value));
261         }
262         catch (RuntimeException e)
263         {
264             Throwables.propagateIfInstanceOf(e, CacheException.class);
265             throw new CacheException("Problem retrieving a value from cache " + getName(), e);
266         }
267     }
268 
269     private static class HazelcastCacheEntryListener<K, V> extends EntryAdapter<K, V>
270     {
271         private final CacheEntryListenerSupport<K, V> listenerSupport;
272 
273         private HazelcastCacheEntryListener(final CacheEntryListenerSupport<K, V> listenerSupport)
274         {
275             this.listenerSupport = checkNotNull(listenerSupport, "listenerSupport");
276         }
277 
278         @Override
279         public void entryAdded(EntryEvent<K, V> event)
280         {
281             listenerSupport.notifyAdd(event.getKey(), event.getValue());
282         }
283 
284         @Override
285         public void entryRemoved(EntryEvent<K, V> event)
286         {
287             listenerSupport.notifyRemove(event.getKey(), event.getOldValue());
288         }
289 
290         @Override
291         public void entryUpdated(EntryEvent<K, V> event)
292         {
293             listenerSupport.notifyUpdate(event.getKey(), event.getValue(), event.getOldValue());
294         }
295 
296         @Override
297         public void entryEvicted(EntryEvent<K, V> event)
298         {
299             listenerSupport.notifyEvict(event.getKey(), event.getOldValue());
300         }
301     }
302 }