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.Objects;
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
28
29
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
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(Objects.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 @Override
144 protected String getHazelcastMapName()
145 {
146 return hazelcastMap.getName();
147 }
148
149 private static class OsgiSafeCachedReferenceListener<V> implements CachedReferenceListener<OsgiSafe<V>>
150 {
151 private final CachedReferenceListener<V> delegate;
152
153 private OsgiSafeCachedReferenceListener(CachedReferenceListener<V> listener)
154 {
155 this.delegate = checkNotNull(listener, "listener");
156 }
157
158 @Override
159 public void onEvict(@Nonnull CachedReferenceEvent<OsgiSafe<V>> event)
160 {
161 delegate.onEvict(new DefaultCachedReferenceEvent<V>(unwrap(event.getValue())));
162 }
163
164 @Override
165 public void onSet(@Nonnull CachedReferenceEvent<OsgiSafe<V>> event)
166 {
167 delegate.onSet(new DefaultCachedReferenceEvent<V>(unwrap(event.getValue())));
168 }
169
170 @Override
171 public void onReset(@Nonnull CachedReferenceEvent<OsgiSafe<V>> event)
172 {
173 delegate.onReset(new DefaultCachedReferenceEvent<V>(unwrap(event.getValue())));
174 }
175
176 @Override
177 public boolean equals(Object o)
178 {
179 if (this == o)
180 {
181 return true;
182 }
183 if (o == null || getClass() != o.getClass())
184 {
185 return false;
186 }
187
188 OsgiSafeCachedReferenceListener that = (OsgiSafeCachedReferenceListener) o;
189 return delegate.equals(that.delegate);
190 }
191
192 @Override
193 public int hashCode()
194 {
195 return delegate.hashCode();
196 }
197 }
198
199 @SuppressWarnings ("unchecked")
200 private static class HazelcastCachedReferenceListener<V> extends EntryAdapter<String, V>
201 {
202 private final CachedReferenceListenerSupport listenerSupport;
203
204 private HazelcastCachedReferenceListener(final CachedReferenceListenerSupport listenerSupport)
205 {
206 this.listenerSupport = listenerSupport;
207 }
208
209 @Override
210 public void entryAdded(EntryEvent<String, V> event)
211 {
212 listenerSupport.notifySet(event.getValue());
213 }
214
215 @Override
216 public void entryRemoved(EntryEvent<String, V> event)
217 {
218 listenerSupport.notifyReset(event.getOldValue());
219 }
220
221 @Override
222 public void entryUpdated(EntryEvent<String, V> event)
223 {
224 listenerSupport.notifySet(event.getValue());
225 }
226
227 @Override
228 public void entryEvicted(EntryEvent<String, V> event)
229 {
230 listenerSupport.notifyEvict(event.getOldValue());
231 }
232 }
233 }