View Javadoc

1   /*
2    * RCache - A collection of simple reference-based cache implementations.
3    * Copyright (C) 2007  Rodrigo Ruiz
4    *
5    * This library is free software; you can redistribute it and/or
6    * modify it under the terms of the GNU Lesser General Public
7    * License as published by the Free Software Foundation; either
8    * version 2.1 of the License, or (at your option) any later version.
9    *
10   * This library is distributed in the hope that it will be useful,
11   * but WITHOUT ANY WARRANTY; without even the implied warranty of
12   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13   * Lesser General Public License for more details.
14   *
15   * You should have received a copy of the GNU Lesser General Public
16   * License along with this library; if not, write to the Free Software
17   * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA
18   *
19   * Alternatively, the contents of this file may be used under the terms
20   * of the Apache 2.0 license (the "Apache License"), in which case its
21   * provisions are applicable instead of those above. If you wish to allow use
22   * of your version of this file only* under the terms of the Apache License
23   * and not to allow others to use your version of this file under the LGPL,
24   * indicate your decision by* deleting the provisions above and replace them
25   * with the notice and other provisions required by the Apache License. If
26   * you do not delete the provisions above, a recipient may use your version of
27   * this file under either the LGPL or the Apache License.
28   */
29  package net.sourceforge.rcache;
30  
31  import java.lang.ref.ReferenceQueue;
32  import java.util.AbstractMap;
33  import java.util.Collection;
34  import java.util.HashMap;
35  import java.util.Hashtable;
36  import java.util.Map;
37  import java.util.Set;
38  import java.util.concurrent.ConcurrentHashMap;
39  import java.util.concurrent.ConcurrentMap;
40  
41  /**
42   * <p>Memory-Sensitive Cache base implementation.</p>
43   *
44   * <p>Thread-safety of this class will depend on the selected implementation
45   * for the internal map. If the convenience constructors are used, resulting
46   * instances will be thread-safe, unless a concurrency level of -1 (or less)
47   * is specified.</p>
48   *
49   * @author Rodrigo Ruiz
50   * @param <K> the type of keys maintained by this cache
51   * @param <V> the type of cached values
52   */
53  public abstract class BaseCache<K, V> extends AbstractMap<K, V>
54    implements Cache<K, V> {
55  
56    /**
57     * The map containing the cached values.
58     */
59    private final Map<K, KeyedReference<K, V>> cache;
60  
61    /**
62     * Queue used to collect unused keys.
63     */
64    private final ReferenceQueue<V> queue;
65  
66    /**
67     * <p>Creates an instance with custom capacity and concurrency level.</p>
68     *
69     * <p>The concurrency level is used as a hint to select the internal Map
70     * implementation:</p>
71     *
72     * <ul>
73     *   <li>&lt; 0 : Do not care about concurrency.
74     *                A {@link java.util.HashMap} will be used.</li>
75     *   <li>= 0    : No concurrent access allowed.
76     *                A {@link java.util.Hashtable} will be selected.</li>
77     *   <li>&gt; 0 : A {@link java.util.concurrent.ConcurrentHashMap} will be
78     *                created with the specified concurrency level.</li>
79     * </ul>
80     *
81     * @param initCapacity     The map initial capacity
82     * @param loadFactor       The map load factor
83     * @param concurrencyLevel The concurrency level.
84     */
85    public BaseCache(int initCapacity, float loadFactor, int concurrencyLevel) {
86      if (concurrencyLevel < 0) {
87        this.cache
88          = new Hashtable<K, KeyedReference<K, V>>(initCapacity, loadFactor);
89      } else if (concurrencyLevel == 0) {
90        this.cache
91          = new HashMap<K, KeyedReference<K, V>>(initCapacity, loadFactor);
92      } else {
93        this.cache = new ConcurrentHashMap<K, KeyedReference<K, V>>(
94          initCapacity, loadFactor, concurrencyLevel);
95      }
96  
97      this.queue = new ReferenceQueue<V>();
98    }
99  
100   /**
101    * <p>Advanced constructor.</p>
102    *
103    * With this constructor, the programmer can specify the Map to be used
104    * to hold the values. Useful for those cases in which the default
105    * implementations do not have the correct performance profile.
106    *
107    * @param map The map to use internally
108    */
109   public BaseCache(Map<K, KeyedReference<K, V>> map) {
110     this.cache = map;
111     this.queue = new ReferenceQueue<V>();
112   }
113 
114   /**
115    * {@inheritDoc}
116    */
117   @Override public final V get(Object key) {
118     KeyedReference<K, V> ref = (KeyedReference<K, V>)cache.get(key);
119     return (ref == null) ? null : ref.get();
120   }
121 
122   /**
123    * {@inheritDoc}
124    */
125   @Override public final V put(K key, V value) {
126     purge();
127 
128     KeyedReference<K, V> ref;
129     if (this.cache instanceof ConcurrentMap) {
130       ConcurrentMap<K, KeyedReference<K, V>> cmap
131         = (ConcurrentMap<K, KeyedReference<K, V>>)this.cache;
132       ref = cmap.putIfAbsent(key, createRef(key, value, queue));
133     } else {
134       ref = cache.get(key);
135       if (ref == null) {
136         ref = cache.put(key, createRef(key, value, queue));
137       }
138     }
139     return (ref == null) ? null : ref.get();
140   }
141 
142   /**
143    * {@inheritDoc}
144    */
145   @Override public final V remove(Object key) {
146     purge();
147     KeyedReference<K, V> ref = cache.remove(key);
148     return (ref == null) ? null : ref.get();
149   }
150 
151   /**
152    * {@inheritDoc}
153    */
154   @Override public final int size() {
155     purge();
156     return cache.size();
157   }
158 
159   /**
160    * {@inheritDoc}
161    */
162   @Override public final void clear() {
163     purge();
164     cache.clear();
165   }
166 
167   /**
168    * {@inheritDoc}
169    */
170   @Override public final Set<Entry<K, V>> entrySet() {
171     throw new UnsupportedOperationException();
172   }
173 
174   /**
175    * {@inheritDoc}
176    */
177   @Override public final Collection<V> values() {
178     throw new UnsupportedOperationException();
179   }
180 
181   /**
182    * {@inheritDoc}
183    *
184    * <p>The superclass implementation of this method relies on the
185    * {@link BaseCache#entrySet} method, which always throws an exception. This
186    * re-implementation removes the issue.</p>
187    */
188   @Override public final int hashCode() {
189     return System.identityHashCode(this);
190   }
191 
192   /**
193    * {@inheritDoc}
194    *
195    * <p>Cache instances are always different by nature, even if they contain
196    * exactly the same entries.</p>
197    */
198   @Override public final boolean equals(Object obj) {
199     return obj == this;
200   }
201 
202   /**
203    * Factory method for creating a new reference instance.
204    *
205    * @param key    The key
206    * @param value  The value
207    * @param rqueue A reference queue
208    * @return A reference instance containing the key and value
209    */
210   protected abstract KeyedReference<K, V> createRef(K key, V value,
211     ReferenceQueue<V> rqueue);
212 
213   /**
214    * <p>Removes keys for References that have been enqueued.</p>
215    *
216    * <p>This method is not public because applications should never need to
217    * manually purge the cache. However, it is left protected to allow invocation
218    * from tests.</p>
219    *
220    * <p>This method is synchronised because {@link java.lang.ref.ReferenceQueue}
221    * is not thread-safe.</p>
222    */
223   @SuppressWarnings("unchecked")
224   protected final synchronized void purge() {
225     KeyedReference<K, V> ref = (KeyedReference<K, V>)queue.poll();
226 
227     while (ref != null) {
228       K key = ref.getKey();
229       cache.remove(key);
230       ref = (KeyedReference<K, V>)queue.poll();
231     }
232   }
233 }