1 package org.apache.commons.jcs3.utils.struct;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 import java.util.AbstractMap;
23 import java.util.ArrayList;
24 import java.util.Collection;
25 import java.util.Map;
26 import java.util.Set;
27 import java.util.concurrent.ConcurrentHashMap;
28 import java.util.concurrent.locks.Lock;
29 import java.util.concurrent.locks.ReentrantLock;
30 import java.util.stream.Collectors;
31
32 import org.apache.commons.jcs3.engine.control.group.GroupAttrName;
33 import org.apache.commons.jcs3.engine.stats.StatElement;
34 import org.apache.commons.jcs3.engine.stats.Stats;
35 import org.apache.commons.jcs3.engine.stats.behavior.IStatElement;
36 import org.apache.commons.jcs3.engine.stats.behavior.IStats;
37 import org.apache.commons.jcs3.log.Log;
38 import org.apache.commons.jcs3.log.LogManager;
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55 public abstract class AbstractLRUMap<K, V>
56 implements Map<K, V>
57 {
58
59 private static final Log log = LogManager.getLog( AbstractLRUMap.class );
60
61
62 private final DoubleLinkedList<LRUElementDescriptor<K, V>> list;
63
64
65 private final Map<K, LRUElementDescriptor<K, V>> map;
66
67
68 private final Lock lock = new ReentrantLock();
69
70
71 private long hitCnt;
72
73
74 private long missCnt;
75
76
77 private long putCnt;
78
79
80
81
82
83 public AbstractLRUMap()
84 {
85 list = new DoubleLinkedList<>();
86
87
88
89 map = new ConcurrentHashMap<>();
90 }
91
92
93
94
95
96
97
98 @Override
99 public int size()
100 {
101 return map.size();
102 }
103
104
105
106
107
108
109 @Override
110 public void clear()
111 {
112 lock.lock();
113 try
114 {
115 map.clear();
116 list.removeAll();
117 }
118 finally
119 {
120 lock.unlock();
121 }
122 }
123
124
125
126
127
128
129 @Override
130 public boolean isEmpty()
131 {
132 return map.isEmpty();
133 }
134
135
136
137
138
139
140 @Override
141 public boolean containsKey( final Object key )
142 {
143 return map.containsKey( key );
144 }
145
146
147
148
149
150
151 @Override
152 public boolean containsValue( final Object value )
153 {
154 return map.containsValue( value );
155 }
156
157
158
159
160 @Override
161 public Collection<V> values()
162 {
163 return map.values().stream()
164 .map(LRUElementDescriptor::getPayload)
165 .collect(Collectors.toList());
166 }
167
168
169
170
171 @Override
172 public void putAll( final Map<? extends K, ? extends V> source )
173 {
174 if ( source != null )
175 {
176 source.forEach(this::put);
177 }
178 }
179
180
181
182
183
184 @Override
185 public V get( final Object key )
186 {
187 final V retVal;
188
189 log.debug( "getting item for key {0}", key );
190
191 final LRUElementDescriptor<K, V> me = map.get( key );
192
193 if ( me == null )
194 {
195 missCnt++;
196 retVal = null;
197 }
198 else
199 {
200 hitCnt++;
201 retVal = me.getPayload();
202 list.makeFirst( me );
203 }
204
205 if ( me == null )
206 {
207 log.debug( "LRUMap miss for {0}", key );
208 }
209 else
210 {
211 log.debug( "LRUMap hit for {0}", key );
212 }
213
214
215 return retVal;
216 }
217
218
219
220
221
222
223
224
225
226 public V getQuiet( final Object key )
227 {
228 V ce = null;
229 final LRUElementDescriptor<K, V> me = map.get( key );
230
231 if ( me != null )
232 {
233 ce = me.getPayload();
234 }
235
236 if ( me == null )
237 {
238 log.debug( "LRUMap quiet miss for {0}", key );
239 }
240 else
241 {
242 log.debug( "LRUMap quiet hit for {0}", key );
243 }
244
245 return ce;
246 }
247
248
249
250
251
252 @Override
253 public V remove( final Object key )
254 {
255 log.debug( "removing item for key: {0}", key );
256
257
258 lock.lock();
259 try
260 {
261 final LRUElementDescriptor<K, V> me = map.remove(key);
262
263 if (me != null)
264 {
265 list.remove(me);
266 return me.getPayload();
267 }
268 }
269 finally
270 {
271 lock.unlock();
272 }
273
274 return null;
275 }
276
277
278
279
280
281
282 @Override
283 public V put(final K key, final V value)
284 {
285 putCnt++;
286
287 LRUElementDescriptor<K, V> old = null;
288 final LRUElementDescriptor<K, V> me = new LRUElementDescriptor<>(key, value);
289
290 lock.lock();
291 try
292 {
293 list.addFirst( me );
294 old = map.put(key, me);
295
296
297 if ( old != null && key.equals(old.getKey()))
298 {
299 list.remove( old );
300 }
301 }
302 finally
303 {
304 lock.unlock();
305 }
306
307
308 if (shouldRemove())
309 {
310 log.debug( "In memory limit reached, removing least recently used." );
311
312
313
314
315 while (shouldRemove())
316 {
317 lock.lock();
318 try
319 {
320 final LRUElementDescriptor<K, V> last = list.getLast();
321 if (last == null) {
322 verifyCache();
323 throw new Error("update: last is null!");
324 }
325 processRemovedLRU(last.getKey(), last.getPayload());
326 if (map.remove(last.getKey()) == null)
327 {
328 log.warn("update: remove failed for key: {0}",
329 last::getKey);
330 verifyCache();
331 }
332 list.removeLast();
333 }
334 finally
335 {
336 lock.unlock();
337 }
338 }
339
340 log.debug( "update: After spool map size: {0}", map::size);
341 if ( map.size() != list.size() )
342 {
343 log.error("update: After spool, size mismatch: map.size() = {0}, "
344 + "linked list size = {1}",
345 map::size, list::size);
346 }
347 }
348
349 if ( old != null )
350 {
351 return old.getPayload();
352 }
353 return null;
354 }
355
356 protected abstract boolean shouldRemove();
357
358
359
360
361 @SuppressWarnings("unchecked")
362 public void dumpCacheEntries()
363 {
364 if (log.isTraceEnabled())
365 {
366 log.trace("dumpingCacheEntries");
367 for (LRUElementDescriptor<K, V> me = list.getFirst(); me != null; me = (LRUElementDescriptor<K, V>) me.next)
368 {
369 log.trace("dumpCacheEntries> key={0}, val={1}", me.getKey(), me.getPayload());
370 }
371 }
372 }
373
374
375
376
377 public void dumpMap()
378 {
379 if (log.isTraceEnabled())
380 {
381 log.trace("dumpingMap");
382 map.forEach((key, value) -> log.trace("dumpMap> key={0}, val={1}", key, value.getPayload()));
383 }
384 }
385
386
387
388
389
390 @SuppressWarnings("unchecked")
391 protected void verifyCache()
392 {
393 if ( !log.isTraceEnabled() )
394 {
395 return;
396 }
397
398 log.trace( "verifycache: mapContains {0} elements, linked list "
399 + "contains {1} elements", map.size(), list.size() );
400 log.trace( "verifycache: checking linked list by key" );
401 for (LRUElementDescriptor<K, V> li = list.getFirst(); li != null; li = (LRUElementDescriptor<K, V>) li.next )
402 {
403 final K key = li.getKey();
404 if ( !map.containsKey( key ) )
405 {
406 log.error( "verifycache: map does not contain key : {0}", li.getKey() );
407 log.error( "li.hashCode={0}", li.getKey().hashCode() );
408 log.error( "key class={0}", key.getClass() );
409 log.error( "key hashCode={0}", key.hashCode() );
410 log.error( "key toString={0}", key.toString() );
411 if ( key instanceof GroupAttrName )
412 {
413 final GroupAttrName<?> name = (GroupAttrName<?>) key;
414 log.error( "GroupID hashCode={0}", name.groupId.hashCode() );
415 log.error( "GroupID.class={0}", name.groupId.getClass() );
416 log.error( "AttrName hashCode={0}", name.attrName.hashCode() );
417 log.error( "AttrName.class={0}", name.attrName.getClass() );
418 }
419 dumpMap();
420 }
421 else if ( map.get( li.getKey() ) == null )
422 {
423 log.error( "verifycache: linked list retrieval returned null for key: {0}",
424 li.getKey() );
425 }
426 }
427
428 log.trace( "verifycache: checking linked list by value " );
429 for (LRUElementDescriptor<K, V> li3 = list.getFirst(); li3 != null; li3 = (LRUElementDescriptor<K, V>) li3.next )
430 {
431 if (!map.containsValue(li3))
432 {
433 log.error( "verifycache: map does not contain value : {0}", li3 );
434 dumpMap();
435 }
436 }
437
438 log.trace( "verifycache: checking via keysets!" );
439 map.keySet().stream()
440 .filter(key -> {
441 for (LRUElementDescriptor<K, V> li2 = list.getFirst(); li2 != null; li2 = (LRUElementDescriptor<K, V>) li2.next )
442 {
443 if ( key.equals( li2.getKey() ) )
444 {
445 return true;
446 }
447 }
448
449 log.error( "verifycache: key not found in list : {0}", key );
450 dumpCacheEntries();
451 if ( map.containsKey( key ) )
452 {
453 log.error( "verifycache: map contains key" );
454 }
455 else
456 {
457 log.error( "verifycache: map does NOT contain key, what the HECK!" );
458 }
459
460 return false;
461 })
462 .findFirst();
463 }
464
465
466
467
468
469
470
471
472 protected void processRemovedLRU(final K key, final V value )
473 {
474 log.debug( "Removing key: [{0}] from LRUMap store, value = [{1}]", key, value );
475 log.debug( "LRUMap store size: \"{0}\".", this.size() );
476 }
477
478
479
480
481 public IStats getStatistics()
482 {
483 final IStats stats = new Stats();
484 stats.setTypeName( "LRUMap" );
485
486 final ArrayList<IStatElement<?>> elems = new ArrayList<>();
487
488 elems.add(new StatElement<>( "List Size", Integer.valueOf(list.size()) ) );
489 elems.add(new StatElement<>( "Map Size", Integer.valueOf(map.size()) ) );
490 elems.add(new StatElement<>( "Put Count", Long.valueOf(putCnt) ) );
491 elems.add(new StatElement<>( "Hit Count", Long.valueOf(hitCnt) ) );
492 elems.add(new StatElement<>( "Miss Count", Long.valueOf(missCnt) ) );
493
494 stats.setStatElements( elems );
495
496 return stats;
497 }
498
499
500
501
502
503
504
505
506
507
508
509 @Override
510 public Set<Map.Entry<K, V>> entrySet()
511 {
512 lock.lock();
513 try
514 {
515 return map.entrySet().stream()
516 .map(entry -> new AbstractMap.SimpleEntry<>(
517 entry.getKey(), entry.getValue().getPayload()))
518 .collect(Collectors.toSet());
519 }
520 finally
521 {
522 lock.unlock();
523 }
524 }
525
526
527
528
529 @Override
530 public Set<K> keySet()
531 {
532 return map.values().stream()
533 .map(LRUElementDescriptor::getKey)
534 .collect(Collectors.toSet());
535 }
536 }