Docjar: A Java Source and Docuemnt Enginecom.*    java.*    javax.*    org.*    all    new    plug-in

Quick Search    Search Deep

Source code: com/opensymphony/oscache/base/algorithm/AbstractConcurrentReadCache.java


1   /*
2    * Copyright (c) 2002-2003 by OpenSymphony
3    * All rights reserved.
4    */
5   /*
6           File: AbstractConcurrentReadCache
7   
8           Written by Doug Lea. Adapted from JDK1.2 HashMap.java and Hashtable.java
9           which carries the following copyright:
10  
11                   * Copyright 1997 by Sun Microsystems, Inc.,
12                   * 901 San Antonio Road, Palo Alto, California, 94303, U.S.A.
13                   * All rights reserved.
14                   *
15                   * This software is the confidential and proprietary information
16                   * of Sun Microsystems, Inc. ("Confidential Information").  You
17                   * shall not disclose such Confidential Information and shall use
18                   * it only in accordance with the terms of the license agreement
19                   * you entered into with Sun.
20  
21          This class is a modified version of ConcurrentReaderHashMap, which was written
22          by Doug Lea (http://gee.cs.oswego.edu/dl/). The modifications where done
23          by Pyxis Technologies. This is a base class for the OSCache module of the
24          openSymphony project (www.opensymphony.com).
25  
26          History:
27          Date       Who                What
28          28oct1999  dl               Created
29          14dec1999  dl               jmm snapshot
30          19apr2000  dl               use barrierLock
31          12jan2001  dl               public release
32          Oct2001    abergevin@pyxis-tech.com
33                                                                  Integrated persistence and outer algorithm support
34  */
35  package com.opensymphony.oscache.base.algorithm;
36  
37  
38  /** OpenSymphony BEGIN */
39  import com.opensymphony.oscache.base.CacheEntry;
40  import com.opensymphony.oscache.base.persistence.CachePersistenceException;
41  import com.opensymphony.oscache.base.persistence.PersistenceListener;
42  
43  import org.apache.commons.logging.Log;
44  import org.apache.commons.logging.LogFactory;
45  
46  import java.io.IOException;
47  import java.io.Serializable;
48  
49  import java.util.*;
50  
51  /**
52   * A version of Hashtable that supports mostly-concurrent reading, but exclusive writing.
53   * Because reads are not limited to periods
54   * without writes, a concurrent reader policy is weaker than a classic
55   * reader/writer policy, but is generally faster and allows more
56   * concurrency. This class is a good choice especially for tables that
57   * are mainly created by one thread during the start-up phase of a
58   * program, and from then on, are mainly read (with perhaps occasional
59   * additions or removals) in many threads.  If you also need concurrency
60   * among writes, consider instead using ConcurrentHashMap.
61   * <p>
62   *
63   * Successful retrievals using get(key) and containsKey(key) usually
64   * run without locking. Unsuccessful ones (i.e., when the key is not
65   * present) do involve brief synchronization (locking).  Also, the
66   * size and isEmpty methods are always synchronized.
67   *
68   * <p> Because retrieval operations can ordinarily overlap with
69   * writing operations (i.e., put, remove, and their derivatives),
70   * retrievals can only be guaranteed to return the results of the most
71   * recently <em>completed</em> operations holding upon their
72   * onset. Retrieval operations may or may not return results
73   * reflecting in-progress writing operations.  However, the retrieval
74   * operations do always return consistent results -- either those
75   * holding before any single modification or after it, but never a
76   * nonsense result.  For aggregate operations such as putAll and
77   * clear, concurrent reads may reflect insertion or removal of only
78   * some entries. In those rare contexts in which you use a hash table
79   * to synchronize operations across threads (for example, to prevent
80   * reads until after clears), you should either encase operations
81   * in synchronized blocks, or instead use java.util.Hashtable.
82   *
83   * <p>
84   *
85   * This class also supports optional guaranteed
86   * exclusive reads, simply by surrounding a call within a synchronized
87   * block, as in <br>
88   * <code>AbstractConcurrentReadCache t; ... Object v; <br>
89   * synchronized(t) { v = t.get(k); } </code> <br>
90   *
91   * But this is not usually necessary in practice. For
92   * example, it is generally inefficient to write:
93   *
94   * <pre>
95   *   AbstractConcurrentReadCache t; ...            // Inefficient version
96   *   Object key; ...
97   *   Object value; ...
98   *   synchronized(t) {
99   *     if (!t.containsKey(key))
100  *       t.put(key, value);
101  *       // other code if not previously present
102  *     }
103  *     else {
104  *       // other code if it was previously present
105  *     }
106  *   }
107  *</pre>
108  * Instead, just take advantage of the fact that put returns
109  * null if the key was not previously present:
110  * <pre>
111  *   AbstractConcurrentReadCache t; ...                // Use this instead
112  *   Object key; ...
113  *   Object value; ...
114  *   Object oldValue = t.put(key, value);
115  *   if (oldValue == null) {
116  *     // other code if not previously present
117  *   }
118  *   else {
119  *     // other code if it was previously present
120  *   }
121  *</pre>
122  * <p>
123  *
124  * Iterators and Enumerations (i.e., those returned by
125  * keySet().iterator(), entrySet().iterator(), values().iterator(),
126  * keys(), and elements()) return elements reflecting the state of the
127  * hash table at some point at or since the creation of the
128  * iterator/enumeration.  They will return at most one instance of
129  * each element (via next()/nextElement()), but might or might not
130  * reflect puts and removes that have been processed since they were
131  * created.  They do <em>not</em> throw ConcurrentModificationException.
132  * However, these iterators are designed to be used by only one
133  * thread at a time. Sharing an iterator across multiple threads may
134  * lead to unpredictable results if the table is being concurrently
135  * modified.  Again, you can ensure interference-free iteration by
136  * enclosing the iteration in a synchronized block.  <p>
137  *
138  * This class may be used as a direct replacement for any use of
139  * java.util.Hashtable that does not depend on readers being blocked
140  * during updates. Like Hashtable but unlike java.util.HashMap,
141  * this class does NOT allow <tt>null</tt> to be used as a key or
142  * value.  This class is also typically faster than ConcurrentHashMap
143  * when there is usually only one thread updating the table, but
144  * possibly many retrieving values from it.
145  * <p>
146  *
147  * Implementation note: A slightly faster implementation of
148  * this class will be possible once planned Java Memory Model
149  * revisions are in place.
150  *
151  * <p>[<a href="http://gee.cs.oswego.edu/dl/classes/EDU/oswego/cs/dl/util/concurrent/intro.html"> Introduction to this package. </a>]
152  **/
153 public abstract class AbstractConcurrentReadCache extends AbstractMap implements Map, Cloneable, Serializable {
154     /**
155      * The default initial number of table slots for this table (32).
156      * Used when not otherwise specified in constructor.
157      **/
158     public static int DEFAULT_INITIAL_CAPACITY = 32;
159 
160     /**
161      * The minimum capacity.
162      * Used if a lower value is implicitly specified
163      * by either of the constructors with arguments.
164      * MUST be a power of two.
165      */
166     private static final int MINIMUM_CAPACITY = 4;
167 
168     /**
169      * The maximum capacity.
170      * Used if a higher value is implicitly specified
171      * by either of the constructors with arguments.
172      * MUST be a power of two <= 1<<30.
173      */
174     private static final int MAXIMUM_CAPACITY = 1 << 30;
175 
176     /**
177      * The default load factor for this table.
178      * Used when not otherwise specified in constructor, the default is 0.75f.
179      **/
180     public static final float DEFAULT_LOAD_FACTOR = 0.75f;
181 
182     //OpenSymphony BEGIN (pretty long!)
183     protected static final String NULL = "_nul!~";
184     protected static Log log = LogFactory.getLog(AbstractConcurrentReadCache.class);
185 
186     /*
187       The basic strategy is an optimistic-style scheme based on
188       the guarantee that the hash table and its lists are always
189       kept in a consistent enough state to be read without locking:
190 
191       * Read operations first proceed without locking, by traversing the
192          apparently correct list of the apparently correct bin. If an
193          entry is found, but not invalidated (value field null), it is
194          returned. If not found, operations must recheck (after a memory
195          barrier) to make sure they are using both the right list and
196          the right table (which can change under resizes). If
197          invalidated, reads must acquire main update lock to wait out
198          the update, and then re-traverse.
199 
200       * All list additions are at the front of each bin, making it easy
201          to check changes, and also fast to traverse.  Entry next
202          pointers are never assigned. Remove() builds new nodes when
203          necessary to preserve this.
204 
205       * Remove() (also clear()) invalidates removed nodes to alert read
206          operations that they must wait out the full modifications.
207 
208     */
209 
210     /**
211      * Lock used only for its memory effects. We use a Boolean
212      * because it is serializable, and we create a new one because
213      * we need a unique object for each cache instance.
214      **/
215     protected final Boolean barrierLock = new Boolean(true);
216 
217     /**
218      * field written to only to guarantee lock ordering.
219      **/
220     protected transient Object lastWrite;
221 
222     /**
223      * The hash table data.
224      */
225     protected transient Entry[] table;
226 
227     /**
228      * The total number of mappings in the hash table.
229      */
230     protected transient int count;
231 
232     /**
233      * Persistence listener.
234      */
235     protected PersistenceListener persistenceListener = null;
236 
237     /**
238      * Use memory cache or not.
239      */
240     protected boolean memoryCaching = true;
241 
242     /**
243      * Use unlimited disk caching.
244      */
245     protected boolean unlimitedDiskCache = false;
246 
247     /**
248      * The load factor for the hash table.
249      *
250      * @serial
251      */
252     protected float loadFactor;
253 
254     /**
255      * Default cache capacity (number of entries).
256      */
257     protected final int DEFAULT_MAX_ENTRIES = 100;
258 
259     /**
260      * Max number of element in cache when considered unlimited.
261      */
262     protected final int UNLIMITED = 2147483646;
263     protected transient Collection values = null;
264 
265     /**
266      * A HashMap containing the group information.
267      * Each entry uses the group name as the key, and holds a
268      * <code>Set</code> of containing keys of all
269      * the cache entries that belong to that particular group.
270      */
271     protected HashMap groups = new HashMap();
272     protected transient Set entrySet = null;
273 
274     // Views
275     protected transient Set keySet = null;
276 
277     /**
278      * Cache capacity (number of entries).
279      */
280     protected int maxEntries = DEFAULT_MAX_ENTRIES;
281 
282     /**
283      * The table is rehashed when its size exceeds this threshold.
284      * (The value of this field is always (int)(capacity * loadFactor).)
285      *
286      * @serial
287      */
288     protected int threshold;
289 
290     /**
291      * Use overflow persistence caching.
292      */
293     private boolean overflowPersistence = false;
294 
295     /**
296      * Constructs a new, empty map with the specified initial capacity and the specified load factor.
297      *
298      * @param initialCapacity the initial capacity
299      *  The actual initial capacity is rounded to the nearest power of two.
300      * @param loadFactor  the load factor of the AbstractConcurrentReadCache
301      * @throws IllegalArgumentException  if the initial maximum number
302      *               of elements is less
303      *               than zero, or if the load factor is nonpositive.
304      */
305     public AbstractConcurrentReadCache(int initialCapacity, float loadFactor) {
306         if (loadFactor <= 0) {
307             throw new IllegalArgumentException("Illegal Load factor: " + loadFactor);
308         }
309 
310         this.loadFactor = loadFactor;
311 
312         int cap = p2capacity(initialCapacity);
313         table = new Entry[cap];
314         threshold = (int) (cap * loadFactor);
315     }
316 
317     /**
318      * Constructs a new, empty map with the specified initial capacity and default load factor.
319      *
320      * @param   initialCapacity   the initial capacity of the
321      *                            AbstractConcurrentReadCache.
322      * @throws    IllegalArgumentException if the initial maximum number
323      *              of elements is less
324      *              than zero.
325      */
326     public AbstractConcurrentReadCache(int initialCapacity) {
327         this(initialCapacity, DEFAULT_LOAD_FACTOR);
328     }
329 
330     /**
331      * Constructs a new, empty map with a default initial capacity and load factor.
332      */
333     public AbstractConcurrentReadCache() {
334         this(DEFAULT_INITIAL_CAPACITY, DEFAULT_LOAD_FACTOR);
335     }
336 
337     /**
338      * Constructs a new map with the same mappings as the given map.
339      * The map is created with a capacity of twice the number of mappings in
340      * the given map or 11 (whichever is greater), and a default load factor.
341      */
342     public AbstractConcurrentReadCache(Map t) {
343         this(Math.max(2 * t.size(), 11), DEFAULT_LOAD_FACTOR);
344         putAll(t);
345     }
346 
347     /**
348      * Returns <tt>true</tt> if this map contains no key-value mappings.
349      *
350      * @return <tt>true</tt> if this map contains no key-value mappings.
351      */
352     public synchronized boolean isEmpty() {
353         return count == 0;
354     }
355 
356     /**
357      * Returns a set of the cache keys that reside in a particular group.
358      *
359      * @param   groupName The name of the group to retrieve.
360      * @return  a set containing all of the keys of cache entries that belong
361      * to this group, or <code>null</code> if the group was not found.
362      * @exception  NullPointerException if the groupName is <code>null</code>.
363      */
364     public Set getGroup(String groupName) {
365         if (log.isDebugEnabled()) {
366             log.debug("getGroup called (group=" + groupName + ")");
367         }
368 
369         Set groupEntries = null;
370 
371         if (memoryCaching && (groups != null)) {
372             groupEntries = (Set) getGroupForReading(groupName);
373         }
374 
375         if (groupEntries == null) {
376             // Not in the map, try the persistence layer
377             groupEntries = persistRetrieveGroup(groupName);
378         }
379 
380         return groupEntries;
381     }
382 
383     /**
384      * Set the cache capacity
385      */
386     public void setMaxEntries(int newLimit) {
387         if (newLimit > 0) {
388             maxEntries = newLimit;
389 
390             synchronized (this) { // because remove() isn't synchronized
391 
392                 while (size() > maxEntries) {
393                     remove(removeItem(), false);
394                 }
395             }
396         } else {
397             // Capacity must be at least 1
398             throw new IllegalArgumentException("Cache maximum number of entries must be at least 1");
399         }
400     }
401 
402     /**
403      * Retrieve the cache capacity (number of entries).
404      */
405     public int getMaxEntries() {
406         return maxEntries;
407     }
408 
409     /**
410      * Sets the memory caching flag.
411      */
412     public void setMemoryCaching(boolean memoryCaching) {
413         this.memoryCaching = memoryCaching;
414     }
415 
416     /**
417      * Check if memory caching is used.
418      */
419     public boolean isMemoryCaching() {
420         return memoryCaching;
421     }
422 
423     /**
424      * Set the persistence listener to use.
425      */
426     public void setPersistenceListener(PersistenceListener listener) {
427         this.persistenceListener = listener;
428     }
429 
430     /**
431      * Get the persistence listener.
432      */
433     public PersistenceListener getPersistenceListener() {
434         return persistenceListener;
435     }
436 
437     /**
438      * Sets the unlimited disk caching flag.
439      */
440     public void setUnlimitedDiskCache(boolean unlimitedDiskCache) {
441         this.unlimitedDiskCache = unlimitedDiskCache;
442     }
443 
444     /**
445      * Check if we use unlimited disk cache.
446      */
447     public boolean isUnlimitedDiskCache() {
448         return unlimitedDiskCache;
449     }
450 
451     /**
452      * Check if we use overflowPersistence
453      *
454      * @return Returns the overflowPersistence.
455      */
456     public boolean isOverflowPersistence() {
457         return this.overflowPersistence;
458     }
459 
460     /**
461      * Sets the overflowPersistence flag
462      *
463      * @param overflowPersistence The overflowPersistence to set.
464      */
465     public void setOverflowPersistence(boolean overflowPersistence) {
466         this.overflowPersistence = overflowPersistence;
467     }
468 
469     /**
470      * Return the number of slots in this table.
471      **/
472     public synchronized int capacity() {
473         return table.length;
474     }
475 
476     /**
477      * Removes all mappings from this map.
478      */
479     public synchronized void clear() {
480         Entry[] tab = table;
481 
482         for (int i = 0; i < tab.length; ++i) {
483             // must invalidate all to force concurrent get's to wait and then retry
484             for (Entry e = tab[i]; e != null; e = e.next) {
485                 e.value = null;
486 
487                 /** OpenSymphony BEGIN */
488                 itemRemoved(e.key);
489 
490                 /** OpenSymphony END */
491             }
492 
493             tab[i] = null;
494         }
495 
496         // Clean out the entire disk cache
497         persistClear();
498 
499         count = 0;
500         recordModification(tab);
501     }
502 
503     /**
504      * Returns a shallow copy of this.
505      * <tt>AbstractConcurrentReadCache</tt> instance: the keys and
506      * values themselves are not cloned.
507      *
508      * @return a shallow copy of this map.
509      */
510     public synchronized Object clone() {
511         try {
512             AbstractConcurrentReadCache t = (AbstractConcurrentReadCache) super.clone();
513             t.keySet = null;
514             t.entrySet = null;
515             t.values = null;
516 
517             Entry[] tab = table;
518             t.table = new Entry[tab.length];
519 
520             Entry[] ttab = t.table;
521 
522             for (int i = 0; i < tab.length; ++i) {
523                 Entry first = tab[i];
524 
525                 if (first != null) {
526                     ttab[i] = (Entry) (first.clone());
527                 }
528             }
529 
530             return t;
531         } catch (CloneNotSupportedException e) {
532             // this shouldn't happen, since we are Cloneable
533             throw new InternalError();
534         }
535     }
536 
537     /**
538      * Tests if some key maps into the specified value in this table.
539      * This operation is more expensive than the <code>containsKey</code>
540      * method.<p>
541      *
542      * Note that this method is identical in functionality to containsValue,
543      * (which is part of the Map interface in the collections framework).
544      *
545      * @param      value   a value to search for.
546      * @return     <code>true</code> if and only if some key maps to the
547      *             <code>value</code> argument in this table as
548      *             determined by the <tt>equals</tt> method;
549      *             <code>false</code> otherwise.
550      * @exception  NullPointerException  if the value is <code>null</code>.
551      * @see        #containsKey(Object)
552      * @see        #containsValue(Object)
553      * @see           Map
554      */
555     public boolean contains(Object value) {
556         return containsValue(value);
557     }
558 
559     /**
560      * Tests if the specified object is a key in this table.
561      *
562      * @param   key   possible key.
563      * @return  <code>true</code> if and only if the specified object
564      *          is a key in this table, as determined by the
565      *          <tt>equals</tt> method; <code>false</code> otherwise.
566      * @exception  NullPointerException  if the key is
567      *               <code>null</code>.
568      * @see     #contains(Object)
569      */
570     public boolean containsKey(Object key) {
571         return get(key) != null;
572 
573         /** OpenSymphony BEGIN */
574 
575         // TODO: Also check the persistence?
576 
577         /** OpenSymphony END */
578     }
579 
580     /**
581      * Returns <tt>true</tt> if this map maps one or more keys to the
582      * specified value. Note: This method requires a full internal
583      * traversal of the hash table, and so is much slower than
584      * method <tt>containsKey</tt>.
585      *
586      * @param value value whose presence in this map is to be tested.
587      * @return <tt>true</tt> if this map maps one or more keys to the
588      * specified value.
589      * @exception  NullPointerException  if the value is <code>null</code>.
590      */
591     public boolean containsValue(Object value) {
592         if (value == null) {
593             throw new NullPointerException();
594         }
595 
596         Entry[] tab = getTableForReading();
597 
598         for (int i = 0; i < tab.length; ++i) {
599             for (Entry e = tab[i]; e != null; e = e.next) {
600                 Object v = e.value;
601 
602                 if ((v != null) && value.equals(v)) {
603                     return true;
604                 }
605             }
606         }
607 
608         return false;
609     }
610 
611     /**
612      * Returns an enumeration of the values in this table.
613      * Use the Enumeration methods on the returned object to fetch the elements
614      * sequentially.
615      *
616      * @return  an enumeration of the values in this table.
617      * @see     java.util.Enumeration
618      * @see     #keys()
619      * @see        #values()
620      * @see        Map
621      */
622     public Enumeration elements() {
623         return new ValueIterator();
624     }
625 
626     /**
627      * Returns a collection view of the mappings contained in this map.
628      * Each element in the returned collection is a <tt>Map.Entry</tt>.  The
629      * collection is backed by the map, so changes to the map are reflected in
630      * the collection, and vice-versa.  The collection supports element
631      * removal, which removes the corresponding mapping from the map, via the
632      * <tt>Iterator.remove</tt>, <tt>Collection.remove</tt>,
633      * <tt>removeAll</tt>, <tt>retainAll</tt>, and <tt>clear</tt> operations.
634      * It does not support the <tt>add</tt> or <tt>addAll</tt> operations.
635      *
636      * @return a collection view of the mappings contained in this map.
637      */
638     public Set entrySet() {
639         Set es = entrySet;
640 
641         if (es != null) {
642             return es;
643         } else {
644             return entrySet = new AbstractSet() {
645                         public Iterator iterator() {
646                             return new HashIterator();
647                         }
648 
649                         public boolean contains(Object o) {
650                             if (!(o instanceof Map.Entry)) {
651                                 return false;
652                             }
653 
654                             Map.Entry entry = (Map.Entry) o;
655                             Object key = entry.getKey();
656                             Object v = AbstractConcurrentReadCache.this.get(key);
657 
658                             return (v != null) && v.equals(entry.getValue());
659                         }
660 
661                         public boolean remove(Object o) {
662                             if (!(o instanceof Map.Entry)) {
663                                 return false;
664                             }
665 
666                             return AbstractConcurrentReadCache.this.findAndRemoveEntry((Map.Entry) o);
667                         }
668 
669                         public int size() {
670                             return AbstractConcurrentReadCache.this.size();
671                         }
672 
673                         public void clear() {
674                             AbstractConcurrentReadCache.this.clear();
675                         }
676                     };
677         }
678     }
679 
680     /**
681      * Returns the value to which the specified key is mapped in this table.
682      *
683      * @param   key   a key in the table.
684      * @return  the value to which the key is mapped in this table;
685      *          <code>null</code> if the key is not mapped to any value in
686      *          this table.
687      * @exception  NullPointerException  if the key is
688      *               <code>null</code>.
689      * @see     #put(Object, Object)
690      */
691     public Object get(Object key) {
692         if (log.isDebugEnabled()) {
693             log.debug("get called (key=" + key + ")");
694         }
695 
696         // throw null pointer exception if key null
697         int hash = hash(key);
698 
699         /*
700            Start off at the apparently correct bin.  If entry is found, we
701            need to check after a barrier anyway.  If not found, we need a
702            barrier to check if we are actually in right bin. So either
703            way, we encounter only one barrier unless we need to retry.
704            And we only need to fully synchronize if there have been
705            concurrent modifications.
706         */
707         Entry[] tab = table;
708         int index = hash & (tab.length - 1);
709         Entry first = tab[index];
710         Entry e = first;
711 
712         for (;;) {
713             if (e == null) {
714                 // If key apparently not there, check to
715                 // make sure this was a valid read
716                 tab = getTableForReading();
717 
718                 if (first == tab[index]) {
719                     /** OpenSymphony BEGIN */
720 
721                     /* Previous code
722                     return null;*/
723 
724                     // Not in the table, try persistence
725                     Object value = persistRetrieve(key);
726 
727                     if (value != null) {
728                         // Update the map, but don't persist the data
729                         put(key, value, false);
730                     }
731 
732                     return value;
733 
734                     /** OpenSymphony END */
735                 } else {
736                     // Wrong list -- must restart traversal at new first
737                     e = first = tab[index = hash & (tab.length - 1)];
738                 }
739             }
740             // checking for pointer equality first wins in most applications
741             else if ((key == e.key) || ((e.hash == hash) && key.equals(e.key))) {
742                 Object value = e.value;
743 
744                 if (value != null) {
745                     /** OpenSymphony BEGIN */
746 
747                     /* Previous code
748                     return value;*/
749                     if (NULL.equals(value)) {
750                         // Memory cache disable, use disk
751                         value = persistRetrieve(e.key);
752 
753                         if (value != null) {
754                             itemRetrieved(key);
755                         }
756 
757                         return value; // fix [CACHE-13]
758                     } else {
759                         itemRetrieved(key);
760 
761                         return value;
762                     }
763 
764                     /** OpenSymphony END */
765                 }
766 
767                 // Entry was invalidated during deletion. But it could
768                 // have been re-inserted, so we must retraverse.
769                 // To avoid useless contention, get lock to wait out modifications
770                 // before retraversing.
771                 synchronized (this) {
772                     tab = table;
773                 }
774 
775                 e = first = tab[index = hash & (tab.length - 1)];
776             } else {
777                 e = e.next;
778             }
779         }
780     }
781 
782     /**
783      * Returns a set view of the keys contained in this map.
784      * The set is backed by the map, so changes to the map are reflected in the set, and
785      * vice-versa.  The set supports element removal, which removes the
786      * corresponding mapping from this map, via the <tt>Iterator.remove</tt>,
787      * <tt>Set.remove</tt>, <tt>removeAll</tt>, <tt>retainAll</tt>, and
788      * <tt>clear</tt> operations.  It does not support the <tt>add</tt> or
789      * <tt>addAll</tt> operations.
790      *
791      * @return a set view of the keys contained in this map.
792      */
793     public Set keySet() {
794         Set ks = keySet;
795 
796         if (ks != null) {
797             return ks;
798         } else {
799             return keySet = new AbstractSet() {
800                         public Iterator iterator() {
801                             return new KeyIterator();
802                         }
803 
804                         public int size() {
805                             return AbstractConcurrentReadCache.this.size();
806                         }
807 
808                         public boolean contains(Object o) {
809                             return AbstractConcurrentReadCache.this.containsKey(o);
810                         }
811 
812                         public boolean remove(Object o) {
813                             return AbstractConcurrentReadCache.this.remove(o) != null;
814                         }
815 
816                         public void clear() {
817                             AbstractConcurrentReadCache.this.clear();
818                         }
819                     };
820         }
821     }
822 
823     /**
824      * Returns an enumeration of the keys in this table.
825      *
826      * @return  an enumeration of the keys in this table.
827      * @see     Enumeration
828      * @see     #elements()
829      * @see        #keySet()
830      * @see        Map
831      */
832     public Enumeration keys() {
833         return new KeyIterator();
834     }
835 
836     /**
837      * Return the load factor
838      **/
839     public float loadFactor() {
840         return loadFactor;
841     }
842 
843     /**
844      * Maps the specified <code>key</code> to the specified <code>value</code> in this table.
845      * Neither the key nor the
846      * value can be <code>null</code>. <p>
847      *
848      * The value can be retrieved by calling the <code>get</code> method
849      * with a key that is equal to the original key.
850      *
851      * @param      key     the table key.
852      * @param      value   the value.
853      * @return     the previous value of the specified key in this table,
854      *             or <code>null</code> if it did not have one.
855      * @exception  NullPointerException  if the key or value is
856      *               <code>null</code>.
857      * @see     Object#equals(Object)
858      * @see     #get(Object)
859      */
860     /** OpenSymphony BEGIN */
861     public Object put(Object key, Object value) {
862         // Call the internal put using persistance
863         return put(key, value, true);
864     }
865 
866     /**
867      * Copies all of the mappings from the specified map to this one.
868      *
869      * These mappings replace any mappings that this map had for any of the
870      * keys currently in the specified Map.
871      *
872      * @param t Mappings to be stored in this map.
873      */
874     public synchronized void putAll(Map t) {
875         for (Iterator it = t.entrySet().iterator(); it.hasNext();) {
876             Map.Entry entry = (Map.Entry) it.next();
877             Object key = entry.getKey();
878             Object value = entry.getValue();
879             put(key, value);
880         }
881     }
882 
883     /**
884      * Removes the key (and its corresponding value) from this table.
885      * This method does nothing if the key is not in the table.
886      *
887      * @param   key   the key that needs to be removed.
888      * @return  the value to which the key had been mapped in this table,
889      *          or <code>null</code> if the key did not have a mapping.
890      * @exception  NullPointerException  if the key is
891      *               <code>null</code>.
892      */
893     /** OpenSymphony BEGIN */
894     public Object remove(Object key) {
895         return remove(key, true);
896     }
897 
898     /**
899      * Returns the total number of cache entries held in this map.
900      *
901      * @return the number of key-value mappings in this map.
902      */
903     public synchronized int size() {
904         return count;
905     }
906 
907     /**
908      * Returns a collection view of the values contained in this map.
909      * The collection is backed by the map, so changes to the map are reflected in
910      * the collection, and vice-versa.  The collection supports element
911      * removal, which removes the corresponding mapping from this map, via the
912      * <tt>Iterator.remove</tt>, <tt>Collection.remove</tt>,
913      * <tt>removeAll</tt>, <tt>retainAll</tt>, and <tt>clear</tt> operations.
914      * It does not support the <tt>add</tt> or <tt>addAll</tt> operations.
915      *
916      * @return a collection view of the values contained in this map.
917      */
918     public Collection values() {
919         Collection vs = values;
920 
921         if (vs != null) {
922             return vs;
923         } else {
924             return values = new AbstractCollection() {
925                         public Iterator iterator() {
926                             return new ValueIterator();
927                         }
928 
929                         public int size() {
930                             return AbstractConcurrentReadCache.this.size();
931                         }
932 
933                         public boolean contains(Object o) {
934                             return AbstractConcurrentReadCache.this.containsValue(o);
935                         }
936 
937                         public void clear() {
938                             AbstractConcurrentReadCache.this.clear();
939                         }
940                     };
941         }
942     }
943 
944     /**
945      * Get ref to group.
946      * CACHE-127 Synchronized copying of the group entry set since
947      * the new HashSet(Collection c) constructor uses the iterator.
948      * This may slow things down but it is better than a
949      * ConcurrentModificationException.  We might have to revisit the
950      * code if performance is too adversely impacted.
951      **/
952     protected synchronized final Set getGroupForReading(String groupName) {
953         Set group = (Set) getGroupsForReading().get(groupName);
954 
955         if (group == null) {
956             return null;
957         }
958 
959         return new HashSet(group);
960     }
961 
962     /**
963      * Get ref to groups.
964      * The reference and the cells it
965      * accesses will be at least as fresh as from last
966      * use of barrierLock
967      **/
968     protected final Map getGroupsForReading() {
969         synchronized (barrierLock) {
970             return groups;
971         }
972     }
973 
974     /**
975      * Get ref to table; the reference and the cells it
976      * accesses will be at least as fresh as from last
977      * use of barrierLock
978      **/
979     protected final Entry[] getTableForReading() {
980         synchronized (barrierLock) {
981             return table;
982         }
983     }
984 
985     /**
986      * Force a memory synchronization that will cause
987      * all readers to see table. Call only when already
988      * holding main synch lock.
989      **/
990     protected final void recordModification(Object x) {
991         synchronized (barrierLock) {
992             lastWrite = x;
993         }
994     }
995 
996     /**
997      * Helper method for entrySet remove.
998      **/
999     protected synchronized boolean findAndRemoveEntry(Map.Entry entry) {
1000        Object key = entry.getKey();
1001        Object v = get(key);
1002
1003        if ((v != null) && v.equals(entry.getValue())) {
1004            remove(key);
1005
1006            return true;
1007        } else {
1008            return false;
1009        }
1010    }
1011
1012    /**
1013     * Remove an object from the persistence.
1014     * @param key The key of the object to remove
1015     */
1016    protected void persistRemove(Object key) {
1017        if (log.isDebugEnabled()) {
1018            log.debug("PersistRemove called (key=" + key + ")");
1019        }
1020
1021        if (persistenceListener != null) {
1022            try {
1023                persistenceListener.remove((String) key);
1024            } catch (CachePersistenceException e) {
1025                log.error("[oscache] Exception removing cache entry with key '" + key + "' from persistence", e);
1026            }
1027        }
1028    }
1029
1030    /**
1031     * Removes a cache group using the persistence listener.
1032     * @param groupName The name of the group to remove
1033     */
1034    protected void persistRemoveGroup(String groupName) {
1035        if (log.isDebugEnabled()) {
1036            log.debug("persistRemoveGroup called (groupName=" + groupName + ")");
1037        }
1038
1039        if (persistenceListener != null) {
1040            try {
1041                persistenceListener.removeGroup(groupName);
1042            } catch (CachePersistenceException e) {
1043                log.error("[oscache] Exception removing group " + groupName, e);
1044            }
1045        }
1046    }
1047
1048    /**
1049     * Retrieve an object from the persistence listener.
1050     * @param key The key of the object to retrieve
1051     */
1052    protected Object persistRetrieve(Object key) {
1053        if (log.isDebugEnabled()) {
1054            log.debug("persistRetrieve called (key=" + key + ")");
1055        }
1056
1057        Object entry = null;
1058
1059        if (persistenceListener != null) {
1060            try {
1061                entry = persistenceListener.retrieve((String) key);
1062            } catch (CachePersistenceException e) {
1063                /**
1064                 * It is normal that we get an exception occasionally.
1065                 * It happens when the item is invalidated (written or removed)
1066                 * during read. The logic is constructed so that read is retried.
1067                 */
1068            }
1069        }
1070
1071        return entry;
1072    }
1073
1074    /**
1075     * Retrieves a cache group using the persistence listener.
1076     * @param groupName The name of the group to retrieve
1077     */
1078    protected Set persistRetrieveGroup(String groupName) {
1079        if (log.isDebugEnabled()) {
1080            log.debug("persistRetrieveGroup called (groupName=" + groupName + ")");
1081        }
1082
1083        if (persistenceListener != null) {
1084            try {
1085                return persistenceListener.retrieveGroup(groupName);
1086            } catch (CachePersistenceException e) {
1087                log.error("[oscache] Exception retrieving group " + groupName, e);
1088            }
1089        }
1090
1091        return null;
1092    }
1093
1094    /**
1095     * Store an object in the cache using the persistence listener.
1096     * @param key The object key
1097     * @param obj The object to store
1098     */
1099    protected void persistStore(Object key, Object obj) {
1100        if (log.isDebugEnabled()) {
1101            log.debug("persistStore called (key=" + key + ")");
1102        }
1103
1104        if (persistenceListener != null) {
1105            try {
1106                persistenceListener.store((String) key, obj);
1107            } catch (CachePersistenceException e) {
1108                log.error("[oscache] Exception persisting " + key, e);
1109            }
1110        }
1111    }
1112
1113    /**
1114     * Creates or Updates a cache group using the persistence listener.
1115     * @param groupName The name of the group to update
1116     * @param group The entries for the group
1117     */
1118    protected void persistStoreGroup(String groupName, Set group) {
1119        if (log.isDebugEnabled()) {
1120            log.debug("persistStoreGroup called (groupName=" + groupName + ")");
1121        }
1122
1123        if (persistenceListener != null) {
1124            try {
1125                if ((group == null) || group.isEmpty()) {
1126                    persistenceListener.removeGroup(groupName);
1127                } else {
1128                    persistenceListener.storeGroup(groupName, group);
1129                }
1130            } catch (CachePersistenceException e) {
1131                log.error("[oscache] Exception persisting group " + groupName, e);
1132            }
1133        }
1134    }
1135
1136    /**
1137     * Removes the entire cache from persistent storage.
1138     */
1139    protected void persistClear() {
1140        if (log.isDebugEnabled()) {
1141            log.debug("persistClear called");
1142            ;
1143        }
1144
1145        if (persistenceListener != null) {
1146            try {
1147                persistenceListener.clear();
1148            } catch (CachePersistenceException e) {
1149                log.error("[oscache] Exception clearing persistent cache", e);
1150            }
1151        }
1152    }
1153
1154    /**
1155     * Notify the underlying implementation that an item was put in the cache.
1156     *
1157     * @param key The cache key of the item that was put.
1158     */
1159    protected abstract void itemPut(Object key);
1160
1161    /**
1162     * Notify any underlying algorithm that an item has been retrieved from the cache.
1163     *
1164     * @param key The cache key of the item that was retrieved.
1165     */
1166    protected abstract void itemRetrieved(Object key);
1167
1168    /**
1169     * Notify the underlying implementation that an item was removed from the cache.
1170     *
1171     * @param key The cache key of the item that was removed.
1172     */
1173    protected abstract void itemRemoved(Object key);
1174
1175    /**
1176     * The cache has reached its cacpacity and an item needs to be removed.
1177     * (typically according to an algorithm such as LRU or FIFO).
1178     *
1179     * @return The key of whichever item was removed.
1180     */
1181    protected abstract Object removeItem();
1182
1183    /**
1184     * Reconstitute the <tt>AbstractConcurrentReadCache</tt>.
1185     * instance from a stream (i.e.,
1186     * deserialize it).
1187     */
1188    private synchronized void readObject(java.io.ObjectInputStream s) throws IOException, ClassNotFoundException {
1189        // Read in the threshold, loadfactor, and any hidden stuff
1190        s.defaultReadObject();
1191
1192        // Read in number of buckets and allocate the bucket array;
1193        int numBuckets = s.readInt();
1194        table = new Entry[numBuckets];
1195
1196        // Read in size (number of Mappings)
1197        int size = s.readInt();
1198
1199        // Read the keys and values, and put the mappings in the table
1200        for (int i = 0; i < size; i++) {
1201            Object key = s.readObject();
1202            Object value = s.readObject();
1203            put(key, value);
1204        }
1205    }
1206
1207    /**
1208     * Rehashes the contents of this map into a new table with a larger capacity.
1209     * This method is called automatically when the
1210     * number of keys in this map exceeds its capacity and load factor.
1211     */
1212    protected void rehash() {
1213        Entry[] oldMap = table;
1214        int oldCapacity = oldMap.length;
1215
1216        if (oldCapacity >= MAXIMUM_CAPACITY) {
1217            return;
1218        }
1219
1220        int newCapacity = oldCapacity << 1;
1221        Entry[] newMap = new Entry[newCapacity];
1222        threshold = (int) (newCapacity * loadFactor);
1223
1224        /*
1225          We need to guarantee that any existing reads of oldMap can
1226          proceed. So we cannot yet null out each oldMap bin.
1227
1228          Because we are using power-of-two expansion, the elements
1229          from each bin must either stay at same index, or move
1230          to oldCapacity+index. We also minimize new node creation by
1231          catching cases where old nodes can be reused because their
1232          .next fields won't change. (This is checked only for sequences
1233          of one and two. It is not worth checking longer ones.)
1234        */
1235        for (int i = 0; i < oldCapacity; ++i) {
1236            Entry l = null;
1237            Entry h = null;
1238            Entry e = oldMap[i];
1239
1240            while (e != null) {
1241                int hash = e.hash;
1242                Entry next = e.next;
1243
1244                if ((hash & oldCapacity) == 0) {
1245                    // stays at newMap[i]
1246                    if (l == null) {
1247                        // try to reuse node
1248                        if ((next == null) || ((next.next == null) && ((next.hash & oldCapacity) == 0))) {
1249                            l = e;
1250
1251                            break;
1252                        }
1253                    }
1254
1255                    l = new Entry(hash, e.key, e.value, l);
1256                } else {
1257                    // moves to newMap[oldCapacity+i]
1258                    if (h == null) {
1259                        if ((next == null) || ((next.next == null) && ((next.hash & oldCapacity) != 0))) {
1260                            h = e;
1261
1262                            break;
1263                        }
1264                    }
1265
1266                    h = new Entry(hash, e.key, e.value, h);
1267                }
1268
1269                e = next;
1270            }
1271
1272            newMap[i] = l;
1273            newMap[oldCapacity + i] = h;
1274        }
1275
1276        table = newMap;
1277        recordModification(newMap);
1278    }
1279
1280    /**
1281     * Continuation of put(), called only when synch lock is
1282     * held and interference has been detected.
1283     **/
1284    /** OpenSymphony BEGIN */
1285
1286    /* Previous code
1287    protected Object sput(Object key, Object value, int hash) {*/
1288    protected Object sput(Object key, Object value, int hash, boolean persist) {
1289        /** OpenSymphony END */
1290        Entry[] tab = table;
1291        int index = hash & (tab.length - 1);
1292        Entry first = tab[index];
1293        Entry e = first;
1294
1295        for (;;) {
1296            if (e == null) {
1297                /** OpenSymphony BEGIN */
1298
1299                // Previous code
1300                //      Entry newEntry = new Entry(hash, key, value, first);
1301                Entry newEntry;
1302
1303                if (memoryCaching) {
1304                    newEntry = new Entry(hash, key, value, first);
1305                } else {
1306                    newEntry = new Entry(hash, key, NULL, first);
1307                }
1308
1309                itemPut(key);
1310
1311                // Persist if required
1312                if (persist && !overflowPersistence) {
1313                    persistStore(key, value);
1314                }
1315
1316                // If we have a CacheEntry, update the group lookups
1317                if (value instanceof CacheEntry) {
1318                    updateGroups(null, (CacheEntry) value, persist);
1319                }
1320
1321                /**        OpenSymphony END */
1322                tab[index] = newEntry;
1323
1324                if (++count >= threshold) {
1325                    rehash();
1326                } else {
1327                    recordModification(newEntry);
1328                }
1329
1330                return null;
1331            } else if ((key == e.key) || ((e.hash == hash) && key.equals(e.key))) {
1332                Object oldValue = e.value;
1333
1334                /** OpenSymphony BEGIN */
1335
1336                /* Previous code
1337                e.value = value; */
1338                if (memoryCaching) {
1339                    e.value = value;
1340                }
1341
1342                // Persist if required
1343                if (persist && overflowPersistence) {
1344                    persistRemove(key);
1345                } else if (persist) {
1346                    persistStore(key, value);
1347                }
1348
1349                updateGroups(oldValue, value, persist);
1350
1351                itemPut(key);
1352
1353                /** OpenSymphony END */
1354                return oldValue;
1355            } else {
1356                e = e.next;
1357            }
1358        }
1359    }
1360
1361    /**
1362     * Continuation of remove(), called only when synch lock is
1363     * held and interference has been detected.
1364     **/
1365    /** OpenSymphony BEGIN */
1366
1367    /* Previous code
1368    protected Object sremove(Object key, int hash) { */
1369    protected Object sremove(Object key, int hash, boolean invokeAlgorithm) {
1370        /** OpenSymphony END */
1371        Entry[] tab = table;
1372        int index = hash & (tab.length - 1);
1373        Entry first = tab[index];
1374        Entry e = first;
1375
1376        for (;;) {
1377            if (e == null) {
1378                return null;
1379            } else if ((key == e.key) || ((e.hash == hash) && key.equals(e.key))) {
1380                Object oldValue = e.value;
1381                e.value = null;
1382                count--;
1383
1384                /** OpenSymphony BEGIN */
1385                if (!unlimitedDiskCache && !overflowPersistence) {
1386                    persistRemove(e.key);
1387                }
1388
1389                if (overflowPersistence && ((size() + 1) >= maxEntries)) {
1390                    persistStore(key, oldValue);
1391                }
1392
1393                if (invokeAlgorithm) {
1394                    itemRemoved(key);
1395                }
1396
1397                /** OpenSymphony END */
1398                Entry head = e.next;
1399
1400                for (Entry p = first; p != e; p = p.next) {
1401                    head = new Entry(p.hash, p.key, p.value, head);
1402                }
1403
1404                tab[index] = head;
1405                recordModification(head);
1406
1407                return oldValue;
1408            } else {
1409                e = e.next;
1410            }
1411        }
1412    }
1413
1414    /**
1415     * Save the state of the <tt>AbstractConcurrentReadCache</tt> instance to a stream.
1416     * (i.e., serialize it).
1417     *
1418     * @serialData The <i>capacity</i> of the
1419     * AbstractConcurrentReadCache (the length of the
1420     * bucket array) is emitted (int), followed  by the
1421     * <i>size</i> of the AbstractConcurrentReadCache (the number of key-value
1422     * mappings), followed by the key (Object) and value (Object)
1423     * for each key-value mapping represented by the AbstractConcurrentReadCache
1424     * The key-value mappings are emitted in no particular order.
1425     */
1426    private synchronized void writeObject(java.io.ObjectOutputStream s) throws IOException {
1427        // Write out the threshold, loadfactor, and any hidden stuff
1428        s.defaultWriteObject();
1429
1430        // Write out number of buckets
1431        s.writeInt(table.length);
1432
1433        // Write out size (number of Mappings)
1434        s.writeInt(count);
1435
1436        // Write out keys and values (alternating)
1437        for (int index = table.length - 1; index >= 0; index--) {
1438            Entry entry = table[index];
1439
1440            while (entry != null) {
1441                s.writeObject(entry.key);
1442                s.writeObject(entry.value);
1443                entry = entry.next;
1444            }
1445        }
1446    }
1447
1448    /**
1449     * Return hash code for Object x.
1450     * Since we are using power-of-two
1451     * tables, it is worth the effort to improve hashcode via
1452     * the same multiplicative scheme as used in IdentityHashMap.
1453     */
1454    private static int hash(Object x) {
1455        int h = x.hashCode();
1456
1457        // Multiply by 127 (quickly, via shifts), and mix in some high
1458        // bits to help guard against bunching of codes that are
1459        // consecutive or equally spaced.
1460        return ((h << 7) - h + (h >>> 9) + (h >>> 17));
1461    }
1462
1463    /**
1464     * Add this cache key to the groups specified groups.
1465     * We have to treat the
1466     * memory and disk group mappings seperately so they remain valid for their
1467     * corresponding memory/disk caches. (eg if mem is limited to 100 entries
1468     * and disk is unlimited, the group mappings will be different).
1469     *
1470     * @param key The cache key that we are ading to the groups.
1471     * @param newGroups the set of groups we want to add this cache entry to.
1472     * @param persist A flag to indicate whether the keys should be added to
1473     * the persistent cache layer.
1474     */
1475    private void addGroupMappings(