1 package org.apache.commons.jcs3.engine.memory;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 import java.io.IOException;
23 import java.util.ArrayList;
24 import java.util.HashMap;
25 import java.util.LinkedHashSet;
26 import java.util.Map;
27 import java.util.Objects;
28 import java.util.Set;
29 import java.util.concurrent.atomic.AtomicLong;
30 import java.util.concurrent.locks.Lock;
31 import java.util.concurrent.locks.ReentrantLock;
32 import java.util.stream.Collectors;
33
34 import org.apache.commons.jcs3.engine.behavior.ICache;
35 import org.apache.commons.jcs3.engine.behavior.ICacheElement;
36 import org.apache.commons.jcs3.engine.behavior.ICompositeCacheAttributes;
37 import org.apache.commons.jcs3.engine.control.CompositeCache;
38 import org.apache.commons.jcs3.engine.control.group.GroupAttrName;
39 import org.apache.commons.jcs3.engine.control.group.GroupId;
40 import org.apache.commons.jcs3.engine.memory.behavior.IMemoryCache;
41 import org.apache.commons.jcs3.engine.memory.util.MemoryElementDescriptor;
42 import org.apache.commons.jcs3.engine.stats.StatElement;
43 import org.apache.commons.jcs3.engine.stats.Stats;
44 import org.apache.commons.jcs3.engine.stats.behavior.IStatElement;
45 import org.apache.commons.jcs3.engine.stats.behavior.IStats;
46 import org.apache.commons.jcs3.log.Log;
47 import org.apache.commons.jcs3.log.LogManager;
48
49
50
51
52 public abstract class AbstractMemoryCache<K, V>
53 implements IMemoryCache<K, V>
54 {
55
56 private static final Log log = LogManager.getLog( AbstractMemoryCache.class );
57
58
59 private ICompositeCacheAttributes cacheAttributes;
60
61
62 private CompositeCache<K, V> cache;
63
64
65 protected int chunkSize;
66
67 protected final Lock lock = new ReentrantLock();
68
69
70 protected Map<K, MemoryElementDescriptor<K, V>> map;
71
72
73 protected AtomicLong hitCnt;
74
75
76 protected AtomicLong missCnt;
77
78
79 protected AtomicLong putCnt;
80
81
82
83
84
85
86 @Override
87 public void initialize( final CompositeCache<K, V> hub )
88 {
89 hitCnt = new AtomicLong();
90 missCnt = new AtomicLong();
91 putCnt = new AtomicLong();
92
93 this.cacheAttributes = hub.getCacheAttributes();
94 this.chunkSize = cacheAttributes.getSpoolChunkSize();
95 this.cache = hub;
96
97 this.map = createMap();
98 }
99
100
101
102
103
104
105
106 public abstract Map<K, MemoryElementDescriptor<K, V>> createMap();
107
108
109
110
111
112
113
114
115
116 @Override
117 public Map<K, ICacheElement<K, V>> getMultiple(final Set<K> keys)
118 throws IOException
119 {
120 if (keys != null)
121 {
122 return keys.stream()
123 .map(key -> {
124 try
125 {
126 return get(key);
127 }
128 catch (final IOException e)
129 {
130 return null;
131 }
132 })
133 .filter(Objects::nonNull)
134 .collect(Collectors.toMap(
135 ICacheElement::getKey,
136 element -> element));
137 }
138
139 return new HashMap<>();
140 }
141
142
143
144
145
146
147
148
149
150 @Override
151 public ICacheElement<K, V> getQuiet( final K key )
152 throws IOException
153 {
154 ICacheElement<K, V> ce = null;
155
156 final MemoryElementDescriptor<K, V> me = map.get( key );
157 if ( me != null )
158 {
159 log.debug( "{0}: MemoryCache quiet hit for {1}",
160 this::getCacheName, () -> key );
161
162 ce = me.getCacheElement();
163 }
164 else
165 {
166 log.debug( "{0}: MemoryCache quiet miss for {1}",
167 this::getCacheName, () -> key );
168 }
169
170 return ce;
171 }
172
173
174
175
176
177
178
179 @Override
180 public abstract void update( ICacheElement<K, V> ce )
181 throws IOException;
182
183
184
185
186
187
188 @Override
189 public void removeAll() throws IOException
190 {
191 lock.lock();
192 try
193 {
194 lockedRemoveAll();
195 map.clear();
196 }
197 finally
198 {
199 lock.unlock();
200 }
201 }
202
203
204
205
206
207 protected abstract void lockedRemoveAll();
208
209
210
211
212
213
214 @Override
215 public void dispose()
216 throws IOException
217 {
218 removeAll();
219 hitCnt.set(0);
220 missCnt.set(0);
221 putCnt.set(0);
222 log.info( "Memory Cache dispose called." );
223 }
224
225
226
227
228 @Override
229 public IStats getStatistics()
230 {
231 final IStats stats = new Stats();
232 stats.setTypeName( "Abstract Memory Cache" );
233
234 final ArrayList<IStatElement<?>> elems = new ArrayList<>();
235 stats.setStatElements(elems);
236
237 elems.add(new StatElement<>("Put Count", putCnt));
238 elems.add(new StatElement<>("Hit Count", hitCnt));
239 elems.add(new StatElement<>("Miss Count", missCnt));
240 elems.add(new StatElement<>( "Map Size", Integer.valueOf(getSize()) ) );
241
242 return stats;
243 }
244
245
246
247
248
249
250 @Override
251 public int getSize()
252 {
253 return this.map.size();
254 }
255
256
257
258
259
260
261 public String getCacheName()
262 {
263 final String attributeCacheName = this.cacheAttributes.getCacheName();
264 if(attributeCacheName != null)
265 {
266 return attributeCacheName;
267 }
268 return cache.getCacheName();
269 }
270
271
272
273
274
275
276 @Override
277 public void waterfal( final ICacheElement<K, V> ce )
278 {
279 this.cache.spoolToDisk( ce );
280 }
281
282
283
284
285
286 public void dumpMap()
287 {
288 if (log.isTraceEnabled())
289 {
290 log.trace("dumpingMap");
291 map.forEach((key, value) ->
292 log.trace("dumpMap> key={0}, val={1}",key, key,
293 value.getCacheElement().getVal()));
294 }
295 }
296
297
298
299
300
301
302 @Override
303 public ICompositeCacheAttributes getCacheAttributes()
304 {
305 return this.cacheAttributes;
306 }
307
308
309
310
311
312
313 @Override
314 public void setCacheAttributes( final ICompositeCacheAttributes cattr )
315 {
316 this.cacheAttributes = cattr;
317 }
318
319
320
321
322
323
324 @Override
325 public CompositeCache<K, V> getCompositeCache()
326 {
327 return this.cache;
328 }
329
330
331
332
333
334
335 protected boolean removeByGroup(final K key)
336 {
337 final GroupId groupId = ((GroupAttrName<?>) key).groupId;
338
339
340 return map.entrySet().removeIf(entry -> {
341 final K k = entry.getKey();
342
343 if (k instanceof GroupAttrName && ((GroupAttrName<?>) k).groupId.equals(groupId))
344 {
345 lock.lock();
346 try
347 {
348 lockedRemoveElement(entry.getValue());
349 return true;
350 }
351 finally
352 {
353 lock.unlock();
354 }
355 }
356
357 return false;
358 });
359 }
360
361
362
363
364
365
366
367 protected boolean removeByHierarchy(final K key)
368 {
369 final String keyString = key.toString();
370
371
372 return map.entrySet().removeIf(entry -> {
373 final K k = entry.getKey();
374
375 if (k instanceof String && ((String) k).startsWith(keyString))
376 {
377 lock.lock();
378 try
379 {
380 lockedRemoveElement(entry.getValue());
381 return true;
382 }
383 finally
384 {
385 lock.unlock();
386 }
387 }
388
389 return false;
390 });
391 }
392
393
394
395
396
397
398
399 protected abstract void lockedRemoveElement(MemoryElementDescriptor<K, V> me);
400
401
402
403
404
405
406
407
408
409
410
411 @Override
412 public boolean remove(final K key) throws IOException
413 {
414 log.debug("removing item for key: {0}", key);
415
416 boolean removed = false;
417
418
419 if (key instanceof String && ((String) key).endsWith(ICache.NAME_COMPONENT_DELIMITER))
420 {
421 removed = removeByHierarchy(key);
422 }
423 else if (key instanceof GroupAttrName && ((GroupAttrName<?>) key).attrName == null)
424 {
425 removed = removeByGroup(key);
426 }
427 else
428 {
429
430 lock.lock();
431 try
432 {
433 final MemoryElementDescriptor<K, V> me = map.remove(key);
434 if (me != null)
435 {
436 lockedRemoveElement(me);
437 removed = true;
438 }
439 }
440 finally
441 {
442 lock.unlock();
443 }
444 }
445
446 return removed;
447 }
448
449
450
451
452
453
454 @Override
455 public Set<K> getKeySet()
456 {
457 return new LinkedHashSet<>(map.keySet());
458 }
459
460
461
462
463
464
465
466
467
468 @Override
469 public ICacheElement<K, V> get(final K key) throws IOException
470 {
471 ICacheElement<K, V> ce = null;
472
473 log.debug("{0}: getting item for key {1}", this::getCacheName,
474 () -> key);
475
476 final MemoryElementDescriptor<K, V> me = map.get(key);
477
478 if (me != null)
479 {
480 hitCnt.incrementAndGet();
481 ce = me.getCacheElement();
482
483 lock.lock();
484 try
485 {
486 lockedGetElement(me);
487 }
488 finally
489 {
490 lock.unlock();
491 }
492
493 log.debug("{0}: MemoryCache hit for {1}", this::getCacheName,
494 () -> key);
495 }
496 else
497 {
498 missCnt.incrementAndGet();
499
500 log.debug("{0}: MemoryCache miss for {1}", this::getCacheName,
501 () -> key);
502 }
503
504 return ce;
505 }
506
507
508
509
510
511
512
513 protected abstract void lockedGetElement(MemoryElementDescriptor<K, V> me);
514 }