1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32 package org.spf4j.concurrent;
33
34 import com.google.common.cache.CacheLoader;
35 import com.google.common.cache.CacheStats;
36 import com.google.common.cache.LoadingCache;
37 import com.google.common.collect.ImmutableMap;
38 import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
39 import java.util.Collection;
40 import java.util.Comparator;
41 import java.util.Map;
42 import java.util.Set;
43 import java.util.concurrent.Callable;
44 import java.util.concurrent.ConcurrentHashMap;
45 import java.util.concurrent.ConcurrentMap;
46 import java.util.concurrent.ConcurrentSkipListMap;
47 import java.util.concurrent.ExecutionException;
48 import javax.annotation.Nullable;
49 import javax.annotation.ParametersAreNonnullByDefault;
50 import org.spf4j.base.Callables;
51 import org.spf4j.base.UncheckedExecutionException;
52
53
54
55
56
57
58
59
60
61
62
63
64
65 @ParametersAreNonnullByDefault
66 public final class UnboundedLoadingCache<K, V> implements LoadingCache<K, V> {
67
68 private final ConcurrentMap<K, Callable<? extends V>> map;
69
70 private final CacheLoader<K, V> loader;
71
72 public UnboundedLoadingCache(final int initialSize, final CacheLoader<K, V> loader) {
73 this(initialSize, 8, loader);
74 }
75
76 public UnboundedLoadingCache(final int initialSize, final int concurrency, final CacheLoader<K, V> loader) {
77 this.map = new ConcurrentHashMap<>(
78 initialSize, 0.75f, concurrency);
79 this.loader = loader;
80 }
81
82
83
84
85
86
87 public UnboundedLoadingCache(final Comparator<? super K> comparator, final CacheLoader<K, V> loader) {
88 this.map = new ConcurrentSkipListMap<>(comparator);
89 this.loader = loader;
90 }
91
92
93 @Override
94 public V get(final K key) throws ExecutionException {
95 Callable<? extends V> existingValHolder = map.get(key);
96 if (existingValHolder == null) {
97 Callable<? extends V> newHolder = Callables.memorized(() -> loader.load(key));
98 existingValHolder = map.putIfAbsent(key, newHolder);
99 if (existingValHolder == null) {
100 existingValHolder = newHolder;
101 }
102 }
103 try {
104 return existingValHolder.call();
105 } catch (Exception ex) {
106 throw new ExecutionException(ex);
107 }
108
109 }
110
111 @Override
112 public V getUnchecked(final K key) {
113 try {
114 return get(key);
115 } catch (ExecutionException ex) {
116 throw new UncheckedExecutionException(ex);
117 }
118 }
119
120 @Override
121 @SuppressFBWarnings("RV_RETURN_VALUE_IGNORED")
122 public ImmutableMap<K, V> getAll(final Iterable<? extends K> keys) throws ExecutionException {
123 ImmutableMap.Builder<K, V> builder = ImmutableMap.builder();
124 for (K key : keys) {
125 builder.put(key, get(key));
126 }
127 return builder.build();
128 }
129
130 @Override
131 @SuppressFBWarnings("NP_PARAMETER_MUST_BE_NONNULL_BUT_MARKED_AS_NULLABLE")
132 public V apply(final K key) {
133 return getUnchecked(key);
134 }
135
136 @Override
137 public void refresh(final K key) {
138 Callable<? extends V> newHolder = Callables.memorized(new Callable<V>() {
139 @Override
140 public V call() throws Exception {
141 return loader.load(key);
142 }
143 });
144 map.put(key, newHolder);
145 }
146
147 @Override
148 public ConcurrentMap<K, V> asMap() {
149 return new MapView();
150 }
151
152 @Override
153 @Nullable
154 public V getIfPresent(final Object key) {
155 Callable<? extends V> existingValHolder = map.get(key);
156 if (existingValHolder != null) {
157 try {
158 return existingValHolder.call();
159 } catch (Exception ex) {
160 throw new UncheckedExecutionException(ex);
161 }
162 } else {
163 return null;
164 }
165 }
166
167 @Override
168 public V get(final K key, final Callable<? extends V> valueLoader) throws ExecutionException {
169 Callable<? extends V> existingValHolder = map.get(key);
170 if (existingValHolder == null) {
171 Callable<? extends V> newHolder = Callables.memorized(valueLoader);
172 existingValHolder = map.putIfAbsent(key, newHolder);
173 if (existingValHolder == null) {
174 existingValHolder = newHolder;
175 }
176 }
177 try {
178 return existingValHolder.call();
179 } catch (Exception ex) {
180 throw new ExecutionException(ex);
181 }
182 }
183
184 @Override
185 @SuppressFBWarnings("RV_RETURN_VALUE_IGNORED")
186 public ImmutableMap<K, V> getAllPresent(final Iterable<?> keys) {
187 ImmutableMap.Builder<K, V> builder = ImmutableMap.builder();
188 for (K key : (Iterable<K>) keys) {
189 V val = getIfPresent(key);
190 if (val != null) {
191 builder.put(key, val);
192 }
193 }
194 return builder.build();
195 }
196
197 @Override
198 public void put(final K key, final V value) {
199 map.put(key, Callables.constant(value));
200 }
201
202 @Override
203 public void putAll(final Map<? extends K, ? extends V> m) {
204 for (Map.Entry<? extends K, ? extends V> entry : m.entrySet()) {
205 put(entry.getKey(), entry.getValue());
206 }
207 }
208
209 @Override
210 public void invalidate(final Object key) {
211 map.remove(key);
212 }
213
214 @Override
215 public void invalidateAll(final Iterable<?> keys) {
216 for (K key : (Iterable<K>) keys) {
217 invalidate(key);
218 }
219 }
220
221 @Override
222 public void invalidateAll() {
223 map.clear();
224 }
225
226 @Override
227 public long size() {
228 return map.size();
229 }
230
231 @Override
232 public CacheStats stats() {
233 throw new UnsupportedOperationException("Not supported");
234 }
235
236 @Override
237 @SuppressFBWarnings("NM_CONFUSING")
238 public void cleanUp() {
239 map.clear();
240 }
241
242 public Set<K> getKeysLoaded() {
243 return map.keySet();
244 }
245
246 @Override
247 public String toString() {
248 return "UnboundedLoadingCache{" + "map=" + map + ", loader=" + loader + '}';
249 }
250
251 private class MapView implements ConcurrentMap<K, V> {
252
253 @Override
254 public V putIfAbsent(final K key, final V value) {
255 Callable<? extends V> result = map.putIfAbsent(key, Callables.constant(value));
256 try {
257 return result == null ? null : result.call();
258 } catch (Exception ex) {
259 throw new UncheckedExecutionException(ex);
260 }
261 }
262
263 @Override
264 public boolean remove(final Object key, final Object value) {
265 throw new UnsupportedOperationException();
266 }
267
268 @Override
269 public boolean replace(final K key, final V oldValue, final V newValue) {
270 throw new UnsupportedOperationException();
271 }
272
273 @Override
274 public V replace(final K key, final V value) {
275 throw new UnsupportedOperationException();
276 }
277
278 @Override
279 public int size() {
280 return map.size();
281 }
282
283 @Override
284 public boolean isEmpty() {
285 return map.isEmpty();
286 }
287
288 @Override
289 public boolean containsKey(final Object key) {
290 return map.containsKey(key);
291 }
292
293 @Override
294 public boolean containsValue(final Object value) {
295 throw new UnsupportedOperationException();
296 }
297
298 @Override
299 public V get(final Object key) {
300 Callable<? extends V> result = map.get(key);
301 try {
302 return result == null ? null : result.call();
303 } catch (Exception ex) {
304 throw new UncheckedExecutionException(ex);
305 }
306 }
307
308 @Override
309 public V put(final K key, final V value) {
310 Callable<? extends V> result = map.put(key, Callables.constant(value));
311 try {
312 return result == null ? null : result.call();
313 } catch (Exception ex) {
314 throw new UncheckedExecutionException(ex);
315 }
316 }
317
318 @Override
319 public V remove(final Object key) {
320 Callable<? extends V> result = map.remove(key);
321 try {
322 return result == null ? null : result.call();
323 } catch (Exception ex) {
324 throw new UncheckedExecutionException(ex);
325 }
326 }
327
328 @Override
329 public void putAll(final Map<? extends K, ? extends V> m) {
330 for (Map.Entry<? extends K, ? extends V> entry : m.entrySet()) {
331 map.put(entry.getKey(), entry::getValue);
332 }
333 }
334
335 @Override
336 public void clear() {
337 map.clear();
338 }
339
340 @Override
341 public Set<K> keySet() {
342 return map.keySet();
343 }
344
345 @Override
346 public Collection<V> values() {
347 throw new UnsupportedOperationException();
348 }
349
350 @Override
351 public Set<Map.Entry<K, V>> entrySet() {
352 throw new UnsupportedOperationException();
353 }
354 }
355
356 }