View Javadoc
1   package com.atlassian.cache.hazelcast;
2   
3   import javax.annotation.Nonnull;
4   import javax.annotation.Nullable;
5   
6   import com.atlassian.cache.CacheException;
7   import com.atlassian.cache.CachedReference;
8   import com.atlassian.cache.CachedReferenceEvent;
9   import com.atlassian.cache.CachedReferenceListener;
10  import com.atlassian.cache.Supplier;
11  import com.atlassian.cache.impl.CachedReferenceListenerSupport;
12  import com.atlassian.cache.impl.DefaultCachedReferenceEvent;
13  import com.atlassian.cache.impl.ValueCachedReferenceListenerSupport;
14  import com.atlassian.hazelcast.serialization.OsgiSafe;
15  
16  import com.google.common.base.MoreObjects;
17  import com.google.common.base.Throwables;
18  import com.hazelcast.core.EntryAdapter;
19  import com.hazelcast.core.EntryEvent;
20  import com.hazelcast.core.IMap;
21  
22  import static com.atlassian.cache.hazelcast.OsgiSafeUtils.unwrap;
23  import static com.atlassian.cache.hazelcast.OsgiSafeUtils.wrap;
24  import static com.google.common.base.Preconditions.checkNotNull;
25  
26  /**
27   * A {@link CachedReference} implementation to satisfy the requirements of {@link com.atlassian.cache.CacheFactory}.
28   *
29   * @since 2.4.0
30   */
31  public class HazelcastCachedReference<V> extends ManagedCacheSupport implements CachedReference<V>
32  {
33      private final IMap<String, OsgiSafe<V>> hazelcastMap;
34      private final Supplier<V> supplier;
35  
36      private final CachedReferenceListenerSupport<OsgiSafe<V>> listenerSupport = new ValueCachedReferenceListenerSupport<OsgiSafe<V>>()
37      {
38          @Override
39          protected void initValue(final CachedReferenceListenerSupport<OsgiSafe<V>> actualListenerSupport)
40          {
41              hazelcastMap.addEntryListener(new HazelcastCachedReferenceListener<OsgiSafe<V>>(actualListenerSupport), true);
42          }
43  
44          @Override
45          protected void initValueless(final CachedReferenceListenerSupport<OsgiSafe<V>> actualListenerSupport)
46          {
47              hazelcastMap.addEntryListener(new HazelcastCachedReferenceListener<OsgiSafe<V>>(actualListenerSupport), false);
48          }
49      };
50  
51      private static final String REFERENCE_KEY = "ReferenceKey";
52  
53      @SuppressWarnings ("AssignmentToCollectionOrArrayFieldFromParameter")
54          // The map is a delegate
55      HazelcastCachedReference(String name, IMap<String, OsgiSafe<V>> hazelcastMap, Supplier<V> supplier, HazelcastCacheManager cacheManager)
56      {
57          super(name, cacheManager);
58  
59          this.hazelcastMap = hazelcastMap;
60          this.supplier = supplier;
61      }
62  
63      @Override
64      public void clear()
65      {
66          hazelcastMap.remove(REFERENCE_KEY);
67      }
68  
69      @Nonnull
70      @SuppressWarnings ("unchecked")
71      @Override
72      public V get()
73      {
74          try
75          {
76              OsgiSafe<V> value = hazelcastMap.get(REFERENCE_KEY);
77              if (value == null)
78              {
79                  V newValue = supplier.get();
80                  if (newValue == null)
81                  {
82                      throw new CacheException("The provided supplier returned null. Null values are not supported.");
83                  }
84                  value = wrap(newValue);
85                  OsgiSafe<V> current = hazelcastMap.putIfAbsent(REFERENCE_KEY, value);
86                  return unwrap(MoreObjects.firstNonNull(current, value));
87              }
88              return unwrap(value);
89          }
90          catch (RuntimeException e)
91          {
92              Throwables.propagateIfInstanceOf(e, CacheException.class);
93              throw new CacheException(e);
94          }
95      }
96  
97      @Override
98      public void reset()
99      {
100         try
101         {
102             hazelcastMap.remove(REFERENCE_KEY);
103         }
104         catch (RuntimeException e)
105         {
106             throw new CacheException(e);
107         }
108     }
109 
110     @Override
111     public boolean equals(@Nullable final Object o)
112     {
113         if (this == o)
114         {
115             return true;
116         }
117         if (o == null || getClass() != o.getClass())
118         {
119             return false;
120         }
121         HazelcastCachedReference<?> other = (HazelcastCachedReference<?>) o;
122         return hazelcastMap.equals(other.hazelcastMap);
123     }
124 
125     @Override
126     public int hashCode()
127     {
128         return 3 + hazelcastMap.hashCode();
129     }
130 
131     @Override
132     public void addListener(@Nonnull CachedReferenceListener<V> listener, boolean includeValues)
133     {
134         listenerSupport.add(new OsgiSafeCachedReferenceListener<V>(listener), includeValues);
135     }
136 
137     @Override
138     public void removeListener(@Nonnull CachedReferenceListener<V> listener)
139     {
140         listenerSupport.remove(new OsgiSafeCachedReferenceListener<V>(listener));
141     }
142 
143     @Nonnull
144     @Override
145     protected String getHazelcastMapName()
146     {
147         return hazelcastMap.getName();
148     }
149 
150     private static class OsgiSafeCachedReferenceListener<V> implements CachedReferenceListener<OsgiSafe<V>>
151     {
152         private final CachedReferenceListener<V> delegate;
153 
154         private OsgiSafeCachedReferenceListener(CachedReferenceListener<V> listener)
155         {
156             this.delegate = checkNotNull(listener, "listener");
157         }
158 
159         @Override
160         public void onEvict(@Nonnull CachedReferenceEvent<OsgiSafe<V>> event)
161         {
162             delegate.onEvict(new DefaultCachedReferenceEvent<V>(unwrap(event.getValue())));
163         }
164 
165         @Override
166         public void onSet(@Nonnull CachedReferenceEvent<OsgiSafe<V>> event)
167         {
168             delegate.onSet(new DefaultCachedReferenceEvent<V>(unwrap(event.getValue())));
169         }
170 
171         @Override
172         public void onReset(@Nonnull CachedReferenceEvent<OsgiSafe<V>> event)
173         {
174             delegate.onReset(new DefaultCachedReferenceEvent<V>(unwrap(event.getValue())));
175         }
176 
177         @Override
178         public boolean equals(Object o)
179         {
180             if (this == o)
181             {
182                 return true;
183             }
184             if (o == null || getClass() != o.getClass())
185             {
186                 return false;
187             }
188 
189             OsgiSafeCachedReferenceListener that = (OsgiSafeCachedReferenceListener) o;
190             return delegate.equals(that.delegate);
191         }
192 
193         @Override
194         public int hashCode()
195         {
196             return delegate.hashCode();
197         }
198     }
199 
200     @SuppressWarnings ("unchecked")
201     private static class HazelcastCachedReferenceListener<V> extends EntryAdapter<String, V>
202     {
203         private final CachedReferenceListenerSupport listenerSupport;
204 
205         private HazelcastCachedReferenceListener(final CachedReferenceListenerSupport listenerSupport)
206         {
207             this.listenerSupport = listenerSupport;
208         }
209 
210         @Override
211         public void entryAdded(EntryEvent<String, V> event)
212         {
213             listenerSupport.notifySet(event.getValue());
214         }
215 
216         @Override
217         public void entryRemoved(EntryEvent<String, V> event)
218         {
219             listenerSupport.notifyReset(event.getOldValue());
220         }
221 
222         @Override
223         public void entryUpdated(EntryEvent<String, V> event)
224         {
225             listenerSupport.notifySet(event.getValue());
226         }
227 
228         @Override
229         public void entryEvicted(EntryEvent<String, V> event)
230         {
231             listenerSupport.notifyEvict(event.getOldValue());
232         }
233     }
234 }