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