Coverage Report - net.sourceforge.rcache.BaseCache
 
Classes in this File Line Coverage Branch Coverage Complexity
BaseCache
90%
38/42
100%
18/18
0
 
 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  21
   public BaseCache(int initCapacity, float loadFactor, int concurrencyLevel) {
 86  21
     if (concurrencyLevel < 0) {
 87  2
       this.cache
 88  
         = new Hashtable<K, KeyedReference<K, V>>(initCapacity, loadFactor);
 89  19
     } else if (concurrencyLevel == 0) {
 90  2
       this.cache
 91  
         = new HashMap<K, KeyedReference<K, V>>(initCapacity, loadFactor);
 92  
     } else {
 93  17
       this.cache = new ConcurrentHashMap<K, KeyedReference<K, V>>(
 94  
         initCapacity, loadFactor, concurrencyLevel);
 95  
     }
 96  
 
 97  21
     this.queue = new ReferenceQueue<V>();
 98  21
   }
 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  0
   public BaseCache(Map<K, KeyedReference<K, V>> map) {
 110  0
     this.cache = map;
 111  0
     this.queue = new ReferenceQueue<V>();
 112  0
   }
 113  
 
 114  
   /**
 115  
    * {@inheritDoc}
 116  
    */
 117  
   @Override public final V get(Object key) {
 118  54
     KeyedReference<K, V> ref = (KeyedReference<K, V>)cache.get(key);
 119  54
     return (ref == null) ? null : ref.get();
 120  
   }
 121  
 
 122  
   /**
 123  
    * {@inheritDoc}
 124  
    */
 125  
   @Override public final V put(K key, V value) {
 126  40
     purge();
 127  
 
 128  
     KeyedReference<K, V> ref;
 129  40
     if (this.cache instanceof ConcurrentMap) {
 130  28
       ConcurrentMap<K, KeyedReference<K, V>> cmap
 131  
         = (ConcurrentMap<K, KeyedReference<K, V>>)this.cache;
 132  28
       ref = cmap.putIfAbsent(key, createRef(key, value, queue));
 133  28
     } else {
 134  12
       ref = cache.get(key);
 135  12
       if (ref == null) {
 136  8
         ref = cache.put(key, createRef(key, value, queue));
 137  
       }
 138  
     }
 139  40
     return (ref == null) ? null : ref.get();
 140  
   }
 141  
 
 142  
   /**
 143  
    * {@inheritDoc}
 144  
    */
 145  
   @Override public final V remove(Object key) {
 146  16
     purge();
 147  16
     KeyedReference<K, V> ref = cache.remove(key);
 148  16
     return (ref == null) ? null : ref.get();
 149  
   }
 150  
 
 151  
   /**
 152  
    * {@inheritDoc}
 153  
    */
 154  
   @Override public final int size() {
 155  8
     purge();
 156  8
     return cache.size();
 157  
   }
 158  
 
 159  
   /**
 160  
    * {@inheritDoc}
 161  
    */
 162  
   @Override public final void clear() {
 163  13
     purge();
 164  13
     cache.clear();
 165  13
   }
 166  
 
 167  
   /**
 168  
    * {@inheritDoc}
 169  
    */
 170  
   @Override public final Set<Entry<K, V>> entrySet() {
 171  2
     throw new UnsupportedOperationException();
 172  
   }
 173  
 
 174  
   /**
 175  
    * {@inheritDoc}
 176  
    */
 177  
   @Override public final Collection<V> values() {
 178  2
     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  4
     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  4
     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  77
     KeyedReference<K, V> ref = (KeyedReference<K, V>)queue.poll();
 226  
 
 227  81
     while (ref != null) {
 228  4
       K key = ref.getKey();
 229  4
       cache.remove(key);
 230  4
       ref = (KeyedReference<K, V>)queue.poll();
 231  4
     }
 232  77
   }
 233  
 }