1 package org.apache.commons.jcs.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.Iterator;
24 import java.util.LinkedHashSet;
25 import java.util.List;
26 import java.util.Map;
27 import java.util.Set;
28 import java.util.concurrent.ConcurrentHashMap;
29 import java.util.concurrent.ConcurrentMap;
30
31 import org.apache.commons.jcs.engine.CacheConstants;
32 import org.apache.commons.jcs.engine.behavior.ICacheElement;
33 import org.apache.commons.jcs.engine.control.CompositeCache;
34 import org.apache.commons.jcs.engine.control.group.GroupAttrName;
35 import org.apache.commons.jcs.engine.memory.util.MemoryElementDescriptor;
36 import org.apache.commons.jcs.engine.stats.StatElement;
37 import org.apache.commons.jcs.engine.stats.behavior.IStatElement;
38 import org.apache.commons.jcs.engine.stats.behavior.IStats;
39 import org.apache.commons.jcs.utils.struct.DoubleLinkedList;
40 import org.apache.commons.logging.Log;
41 import org.apache.commons.logging.LogFactory;
42
43
44
45
46
47
48
49
50
51 public abstract class AbstractDoubleLinkedListMemoryCache<K, V> extends AbstractMemoryCache<K, V>
52 {
53
54 private static final Log log = LogFactory.getLog(AbstractDoubleLinkedListMemoryCache.class);
55
56
57 protected DoubleLinkedList<MemoryElementDescriptor<K, V>> list;
58
59
60
61
62
63
64
65 @Override
66 public void initialize(CompositeCache<K, V> hub)
67 {
68 super.initialize(hub);
69 list = new DoubleLinkedList<MemoryElementDescriptor<K, V>>();
70 log.info("initialized MemoryCache for " + getCacheName());
71 }
72
73
74
75
76
77
78
79
80
81
82 @Override
83 public ConcurrentMap<K, MemoryElementDescriptor<K, V>> createMap()
84 {
85 return new ConcurrentHashMap<K, MemoryElementDescriptor<K, V>>();
86 }
87
88
89
90
91
92
93
94
95
96
97
98 @Override
99 public final void update(ICacheElement<K, V> ce) throws IOException
100 {
101 putCnt.incrementAndGet();
102
103 lock.lock();
104 try
105 {
106 MemoryElementDescriptor<K, V> newNode = adjustListForUpdate(ce);
107
108
109 final K key = newNode.getCacheElement().getKey();
110 MemoryElementDescriptor<K, V> oldNode = map.put(key, newNode);
111
112
113 if (oldNode != null && key.equals(oldNode.getCacheElement().getKey()))
114 {
115 list.remove(oldNode);
116 }
117 }
118 finally
119 {
120 lock.unlock();
121 }
122
123
124 spoolIfNeeded();
125 }
126
127
128
129
130
131
132
133
134
135 protected abstract MemoryElementDescriptor<K, V> adjustListForUpdate(ICacheElement<K, V> ce) throws IOException;
136
137
138
139
140
141
142
143 private void spoolIfNeeded() throws Error
144 {
145 int size = map.size();
146
147
148 if (size <= this.getCacheAttributes().getMaxObjects())
149 {
150 return;
151 }
152
153 if (log.isDebugEnabled())
154 {
155 log.debug("In memory limit reached, spooling");
156 }
157
158
159 int chunkSizeCorrected = Math.min(size, chunkSize);
160
161 if (log.isDebugEnabled())
162 {
163 log.debug("About to spool to disk cache, map size: " + size + ", max objects: "
164 + this.getCacheAttributes().getMaxObjects() + ", maximum items to spool: " + chunkSizeCorrected);
165 }
166
167
168
169
170 lock.lock();
171
172 try
173 {
174 for (int i = 0; i < chunkSizeCorrected; i++)
175 {
176 ICacheElement<K, V> lastElement = spoolLastElement();
177 if (lastElement == null)
178 {
179 break;
180 }
181 }
182
183
184
185 if (log.isDebugEnabled() && map.size() != list.size())
186 {
187 log.debug("update: After spool, size mismatch: map.size() = " + map.size() + ", linked list size = " + list.size());
188 }
189 }
190 finally
191 {
192 lock.unlock();
193 }
194
195 if (log.isDebugEnabled())
196 {
197 log.debug("update: After spool map size: " + map.size() + " linked list size = " + list.size());
198 }
199 }
200
201
202
203
204
205
206
207
208
209
210 @Override
211 public final ICacheElement<K, V> get(K key) throws IOException
212 {
213 ICacheElement<K, V> ce = null;
214
215 if (log.isDebugEnabled())
216 {
217 log.debug(getCacheName() + ": getting item for key " + key);
218 }
219
220 MemoryElementDescriptor<K, V> me = map.get(key);
221
222 if (me != null)
223 {
224 hitCnt.incrementAndGet();
225
226 lock.lock();
227 try
228 {
229 ce = me.getCacheElement();
230
231 adjustListForGet(me);
232 }
233 finally
234 {
235 lock.unlock();
236 }
237
238 if (log.isDebugEnabled())
239 {
240 log.debug(getCacheName() + ": LRUMemoryCache hit for " + key);
241 }
242 }
243 else
244 {
245 missCnt.incrementAndGet();
246
247 if (log.isDebugEnabled())
248 {
249 log.debug(getCacheName() + ": LRUMemoryCache miss for " + key);
250 }
251 }
252
253 if (log.isDebugEnabled())
254 {
255 verifyCache();
256 }
257
258 return ce;
259 }
260
261
262
263
264
265
266
267 protected abstract void adjustListForGet(MemoryElementDescriptor<K, V> me);
268
269
270
271
272
273
274
275
276
277
278
279
280 @Override
281 public int freeElements(int numberToFree) throws IOException
282 {
283 int freed = 0;
284
285 lock.lock();
286
287 try
288 {
289 for (; freed < numberToFree; freed++)
290 {
291 ICacheElement<K, V> element = spoolLastElement();
292 if (element == null)
293 {
294 break;
295 }
296 }
297 }
298 finally
299 {
300 lock.unlock();
301 }
302
303 return freed;
304 }
305
306
307
308
309
310
311
312
313 private ICacheElement<K, V> spoolLastElement() throws Error
314 {
315 ICacheElement<K, V> toSpool = null;
316
317 final MemoryElementDescriptor<K, V> last = list.getLast();
318 if (last != null)
319 {
320 toSpool = last.getCacheElement();
321 if (toSpool != null)
322 {
323 getCompositeCache().spoolToDisk(toSpool);
324 if (map.remove(toSpool.getKey()) == null)
325 {
326 log.warn("update: remove failed for key: " + toSpool.getKey());
327
328 if (log.isDebugEnabled())
329 {
330 verifyCache();
331 }
332 }
333 }
334 else
335 {
336 throw new Error("update: last.ce is null!");
337 }
338
339 list.remove(last);
340 }
341
342 return toSpool;
343 }
344
345
346
347
348
349
350
351
352
353
354
355 @Override
356 public boolean remove(K key) throws IOException
357 {
358 if (log.isDebugEnabled())
359 {
360 log.debug("removing item for key: " + key);
361 }
362
363 boolean removed = false;
364
365
366 if (key instanceof String && ((String) key).endsWith(CacheConstants.NAME_COMPONENT_DELIMITER))
367 {
368
369 for (Iterator<Map.Entry<K, MemoryElementDescriptor<K, V>>> itr = map.entrySet().iterator(); itr.hasNext();)
370 {
371 Map.Entry<K, MemoryElementDescriptor<K, V>> entry = itr.next();
372 K k = entry.getKey();
373
374 if (k instanceof String && ((String) k).startsWith(key.toString()))
375 {
376 lock.lock();
377 try
378 {
379 list.remove(entry.getValue());
380 itr.remove();
381 removed = true;
382 }
383 finally
384 {
385 lock.unlock();
386 }
387 }
388 }
389 }
390 else if (key instanceof GroupAttrName && ((GroupAttrName<?>) key).attrName == null)
391 {
392
393 for (Iterator<Map.Entry<K, MemoryElementDescriptor<K, V>>> itr = map.entrySet().iterator(); itr.hasNext();)
394 {
395 Map.Entry<K, MemoryElementDescriptor<K, V>> entry = itr.next();
396 K k = entry.getKey();
397
398 if (k instanceof GroupAttrName && ((GroupAttrName<?>) k).groupId.equals(((GroupAttrName<?>) key).groupId))
399 {
400 lock.lock();
401 try
402 {
403 list.remove(entry.getValue());
404 itr.remove();
405 removed = true;
406 }
407 finally
408 {
409 lock.unlock();
410 }
411 }
412 }
413 }
414 else
415 {
416
417 lock.lock();
418 try
419 {
420 MemoryElementDescriptor<K, V> me = map.remove(key);
421 if (me != null)
422 {
423 list.remove(me);
424 removed = true;
425 }
426 }
427 finally
428 {
429 lock.unlock();
430 }
431 }
432
433 return removed;
434 }
435
436
437
438
439
440
441
442
443 @Override
444 public void removeAll() throws IOException
445 {
446 lock.lock();
447 try
448 {
449 list.removeAll();
450 map.clear();
451 }
452 finally
453 {
454 lock.unlock();
455 }
456 }
457
458
459
460
461
462
463
464
465
466
467 protected MemoryElementDescriptor<K, V> addFirst(ICacheElement<K, V> ce)
468 {
469 lock.lock();
470 try
471 {
472 MemoryElementDescriptor<K, V> me = new MemoryElementDescriptor<K, V>(ce);
473 list.addFirst(me);
474 if ( log.isDebugEnabled() )
475 {
476 verifyCache(ce.getKey());
477 }
478 return me;
479 }
480 finally
481 {
482 lock.unlock();
483 }
484 }
485
486
487
488
489
490
491
492
493
494 protected MemoryElementDescriptor<K, V> addLast(ICacheElement<K, V> ce)
495 {
496 lock.lock();
497 try
498 {
499 MemoryElementDescriptor<K, V> me = new MemoryElementDescriptor<K, V>(ce);
500 list.addLast(me);
501 if ( log.isDebugEnabled() )
502 {
503 verifyCache(ce.getKey());
504 }
505 return me;
506 }
507 finally
508 {
509 lock.unlock();
510 }
511 }
512
513
514
515
516
517
518 @SuppressWarnings("unchecked")
519
520 private void dumpCacheEntries()
521 {
522 log.debug("dumpingCacheEntries");
523 for (MemoryElementDescriptor<K, V> me = list.getFirst(); me != null; me = (MemoryElementDescriptor<K, V>) me.next)
524 {
525 log.debug("dumpCacheEntries> key=" + me.getCacheElement().getKey() + ", val=" + me.getCacheElement().getVal());
526 }
527 }
528
529
530
531
532
533 @SuppressWarnings("unchecked")
534
535 private void verifyCache()
536 {
537 boolean found = false;
538 log.debug("verifycache[" + getCacheName() + "]: mapContains " + map.size() + " elements, linked list contains "
539 + list.size() + " elements");
540 log.debug("verifycache: checking linked list by key ");
541 for (MemoryElementDescriptor<K, V> li = list.getFirst(); li != null; li = (MemoryElementDescriptor<K, V>) li.next)
542 {
543 K key = li.getCacheElement().getKey();
544 if (!map.containsKey(key))
545 {
546 log.error("verifycache[" + getCacheName() + "]: map does not contain key : " + key);
547 log.error("key class=" + key.getClass());
548 log.error("key hashcode=" + key.hashCode());
549 log.error("key toString=" + key.toString());
550 if (key instanceof GroupAttrName)
551 {
552 GroupAttrName<?> name = (GroupAttrName<?>) key;
553 log.error("GroupID hashcode=" + name.groupId.hashCode());
554 log.error("GroupID.class=" + name.groupId.getClass());
555 log.error("AttrName hashcode=" + name.attrName.hashCode());
556 log.error("AttrName.class=" + name.attrName.getClass());
557 }
558 dumpMap();
559 }
560 else if (map.get(key) == null)
561 {
562 log.error("verifycache[" + getCacheName() + "]: linked list retrieval returned null for key: " + key);
563 }
564 }
565
566 log.debug("verifycache: checking linked list by value ");
567 for (MemoryElementDescriptor<K, V> li3 = list.getFirst(); li3 != null; li3 = (MemoryElementDescriptor<K, V>) li3.next)
568 {
569 if (map.containsValue(li3) == false)
570 {
571 log.error("verifycache[" + getCacheName() + "]: map does not contain value : " + li3);
572 dumpMap();
573 }
574 }
575
576 log.debug("verifycache: checking via keysets!");
577 for (Object val : map.keySet())
578 {
579 found = false;
580
581 for (MemoryElementDescriptor<K, V> li2 = list.getFirst(); li2 != null; li2 = (MemoryElementDescriptor<K, V>) li2.next)
582 {
583 if (val.equals(li2.getCacheElement().getKey()))
584 {
585 found = true;
586 break;
587 }
588 }
589 if (!found)
590 {
591 log.error("verifycache[" + getCacheName() + "]: key not found in list : " + val);
592 dumpCacheEntries();
593 if (map.containsKey(val))
594 {
595 log.error("verifycache: map contains key");
596 }
597 else
598 {
599 log.error("verifycache: map does NOT contain key, what the HECK!");
600 }
601 }
602 }
603 }
604
605
606
607
608
609
610
611 @SuppressWarnings("unchecked")
612
613 private void verifyCache(K key)
614 {
615 boolean found = false;
616
617
618 for (MemoryElementDescriptor<K, V> li = list.getFirst(); li != null; li = (MemoryElementDescriptor<K, V>) li.next)
619 {
620 if (li.getCacheElement().getKey() == key)
621 {
622 found = true;
623 log.debug("verifycache(key) key match: " + key);
624 break;
625 }
626 }
627 if (!found)
628 {
629 log.error("verifycache(key)[" + getCacheName() + "], couldn't find key! : " + key);
630 }
631 }
632
633
634
635
636
637
638 @Override
639 public Set<K> getKeySet()
640 {
641 return new LinkedHashSet<K>(map.keySet());
642 }
643
644
645
646
647
648
649
650
651 @Override
652 public IStats getStatistics()
653 {
654 IStats stats = super.getStatistics();
655 stats.setTypeName( "Memory Cache");
656
657 List<IStatElement<?>> elems = stats.getStatElements();
658
659 elems.add(new StatElement<Integer>("List Size", Integer.valueOf(list.size())));
660
661 return stats;
662 }
663 }