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
30
31
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
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 }