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