Coverage Report - org.simpleframework.xml.util.WeakCache
 
Classes in this File Line Coverage Branch Coverage Complexity
WeakCache
60%
9/15
0%
0/4
1.353
WeakCache$1
N/A
N/A
1.353
WeakCache$Segment
66%
4/6
N/A
1.353
WeakCache$SegmentList
86%
13/15
75%
3/4
1.353
 
 1  
 /*
 2  
  * WeakCache.java July 2006
 3  
  *
 4  
  * Copyright (C) 2006, Niall Gallagher <niallg@users.sf.net>
 5  
  *
 6  
  * Licensed under the Apache License, Version 2.0 (the "License");
 7  
  * you may not use this file except in compliance with the License.
 8  
  * You may obtain a copy of the License at
 9  
  *
 10  
  *     http://www.apache.org/licenses/LICENSE-2.0
 11  
  *
 12  
  * Unless required by applicable law or agreed to in writing, software
 13  
  * distributed under the License is distributed on an "AS IS" BASIS,
 14  
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 
 15  
  * implied. See the License for the specific language governing 
 16  
  * permissions and limitations under the License.
 17  
  */
 18  
 
 19  
 package org.simpleframework.xml.util;
 20  
 
 21  
 import java.util.ArrayList;
 22  
 import java.util.Iterator;
 23  
 import java.util.List;
 24  
 import java.util.WeakHashMap;
 25  
 
 26  
 /**
 27  
  * The <code>WeakCache</code> object is an implementation of a cache
 28  
  * that holds on to cached items only if the key remains in memory.
 29  
  * This is effectively like a concurrent hash map with weak keys, it
 30  
  * ensures that multiple threads can concurrently access weak hash
 31  
  * maps in a way that lowers contention for the locks used. 
 32  
  * 
 33  
  * @author Niall Gallagher
 34  
  */
 35  
 public class WeakCache<T> implements Cache<T> {
 36  
    
 37  
    /**
 38  
     * This is used to store a list of segments for the cache.
 39  
     */
 40  
    private SegmentList list;
 41  
    
 42  
    /**
 43  
     * Constructor for the <code>WeakCache</code> object. This is
 44  
     * used to create a cache that stores values in such a way that
 45  
     * when the key is garbage collected the value is removed from
 46  
     * the map. This is similar to the concurrent hash map.
 47  
     */
 48  
    public WeakCache() {
 49  521
       this(10);
 50  521
    }
 51  
    
 52  
    /**
 53  
     * Constructor for the <code>WeakCache</code> object. This is
 54  
     * used to create a cache that stores values in such a way that
 55  
     * when the key is garbage collected the value is removed from
 56  
     * the map. This is similar to the concurrent hash map.
 57  
     * 
 58  
     * @param size this is the number of segments within the cache
 59  
     */
 60  521
    public WeakCache(int size) {      
 61  521
       this.list = new SegmentList(size);
 62  521
    }
 63  
    
 64  
    public boolean isEmpty() {
 65  0
       for(Segment segment : list) {
 66  0
          if(!segment.isEmpty()) {
 67  0
             return false;
 68  
          }
 69  
       }
 70  0
       return true;
 71  
    }
 72  
    
 73  
    
 74  
    /**
 75  
     * This method is used to insert a key value mapping in to the
 76  
     * cache. The value can later be retrieved or removed from the
 77  
     * cache if desired. If the value associated with the key is 
 78  
     * null then nothing is stored within the cache.
 79  
     * 
 80  
     * @param key this is the key to cache the provided value to
 81  
     * @param value this is the value that is to be cached
 82  
     */
 83  
    public void cache(Object key, T value) {
 84  103465
       map(key).cache(key, value);
 85  103465
    }
 86  
    
 87  
    /**
 88  
     * This is used to exclusively take the value mapped to the 
 89  
     * specified key from the cache. Invoking this is effectively
 90  
     * removing the value from the cache.
 91  
     * 
 92  
     * @param key this is the key to acquire the cache value with
 93  
     * 
 94  
     * @return this returns the value mapped to the specified key 
 95  
     */
 96  
    public T take(Object key) {
 97  0
       return map(key).take(key);
 98  
    }
 99  
    
 100  
    /**
 101  
     * This method is used to get the value from the cache that is
 102  
     * mapped to the specified key. If there is no value mapped to
 103  
     * the specified key then this method will return a null.
 104  
     * 
 105  
     * @param key this is the key to acquire the cache value with
 106  
     * 
 107  
     * @return this returns the value mapped to the specified key 
 108  
     */
 109  
    public T fetch(Object key) {
 110  268128
       return map(key).fetch(key);
 111  
    }
 112  
    
 113  
    /**
 114  
     * This is used to determine whether the specified key exists
 115  
     * with in the cache. Typically this can be done using the 
 116  
     * fetch method, which will acquire the object. 
 117  
     * 
 118  
     * @param key this is the key to check within this segment
 119  
     * 
 120  
     * @return true if the specified key is within the cache
 121  
     */
 122  
    public boolean contains(Object key) {
 123  0
       return map(key).contains(key);
 124  
    }
 125  
    
 126  
    /**
 127  
     * This method is used to acquire a <code>Segment</code> using
 128  
     * the keys has code. This method effectively uses the hash to
 129  
     * find a specific segment within the fixed list of segments.
 130  
     * 
 131  
     * @param key this is the key used to acquire the segment
 132  
     * 
 133  
     * @return this returns the segment used to get acquire value
 134  
     */
 135  
    private Segment map(Object key) {
 136  371593
       return list.get(key);
 137  
    }   
 138  
    
 139  
    /**
 140  
     * This is used to maintain a list of segments. All segments that
 141  
     * are stored by this object can be acquired using a given key.
 142  
     * The keys hash is used to select the segment, this ensures that
 143  
     * all read and write operations with the same key result in the
 144  
     * same segment object within this list.
 145  
     * 
 146  
     * @author Niall Gallagher
 147  
     */
 148  
    private class SegmentList implements Iterable<Segment> {
 149  
       
 150  
       /**
 151  
        * The list of segment objects maintained by this object.
 152  
        */
 153  
       private List<Segment> list;
 154  
       
 155  
       /**
 156  
        * Represents the number of segments this object maintains.
 157  
        */
 158  
       private int size;
 159  
       
 160  
       /**
 161  
        * Constructor for the <code>SegmentList</code> object. This
 162  
        * is used to create a list of weak hash maps that can be
 163  
        * acquired using the hash code of a given key. 
 164  
        * 
 165  
        * @param size this is the number of hash maps to maintain
 166  
        */
 167  521
       public SegmentList(int size) {
 168  521
          this.list = new ArrayList<Segment>();
 169  521
          this.size = size;
 170  521
          this.create(size);         
 171  521
       }
 172  
       
 173  
       public Iterator<Segment> iterator() {
 174  0
          return list.iterator();
 175  
       }
 176  
       
 177  
       /**
 178  
        * This is used to acquire the segment using the given key.
 179  
        * The keys hash is used to determine the index within the 
 180  
        * list to acquire the segment, which is a synchronized weak        
 181  
        * hash map storing the key value pairs for a given hash.
 182  
        * 
 183  
        * @param key this is the key used to determine the segment
 184  
        * 
 185  
        * @return the segment that is stored at the resolved hash 
 186  
        */
 187  
       public Segment get(Object key) {
 188  371593
          int segment = segment(key);
 189  
          
 190  371593
          if(segment < size) {
 191  371593
             return list.get(segment);
 192  
          }
 193  0
          return null;
 194  
       }
 195  
       
 196  
       /**
 197  
        * Upon initialization the segment list is populated in such
 198  
        * a way that synchronization is not needed. Each segment is
 199  
        * created and stored in an increasing index within the list.
 200  
        * 
 201  
        * @param size this is the number of segments to be used
 202  
        */
 203  
       private void create(int size) {
 204  521
          int count = size;
 205  
          
 206  5731
          while(count-- > 0) {
 207  5210
             list.add(new Segment());
 208  
          }
 209  521
       }
 210  
       
 211  
       /**
 212  
        * This method performs the translation of the key hash code
 213  
        * to the segment index within the list. Translation is done
 214  
        * by acquiring the modulus of the hash and the list size.
 215  
        * 
 216  
        * @param key this is the key used to resolve the index
 217  
        * 
 218  
        * @return the index of the segment within the list 
 219  
        */
 220  
       private int segment(Object key) {
 221  371593
          return Math.abs(key.hashCode() % size);
 222  
       }
 223  
    }
 224  
    
 225  
    /**
 226  
     * The segment is effectively a synchronized weak hash map. If is
 227  
     * used to store the key value pairs in such a way that they are
 228  
     * kept only as long as the garbage collector does not collect 
 229  
     * the key. This ensures the cache does not cause memory issues. 
 230  
     * 
 231  
     * @author Niall Gallagher
 232  
     */
 233  10420
    private class Segment extends WeakHashMap<Object, T> {      
 234  
       
 235  
       /**
 236  
        * This method is used to insert a key value mapping in to the
 237  
        * cache. The value can later be retrieved or removed from the
 238  
        * cache if desired. If the value associated with the key is 
 239  
        * null then nothing is stored within the cache.
 240  
        * 
 241  
        * @param key this is the key to cache the provided value to
 242  
        * @param value this is the value that is to be cached
 243  
        */
 244  
       public synchronized void cache(Object key, T value) {
 245  103465
          put(key, value);
 246  103465
       }
 247  
       
 248  
       /**
 249  
        * This method is used to get the value from the cache that is
 250  
        * mapped to the specified key. If there is no value mapped to
 251  
        * the specified key then this method will return a null.
 252  
        * 
 253  
        * @param key this is the key to acquire the cache value with
 254  
        * 
 255  
        * @return this returns the value mapped to the specified key 
 256  
        */
 257  
       public synchronized T fetch(Object key) {
 258  268128
          return get(key);
 259  
       }
 260  
       
 261  
       /**
 262  
        * This is used to exclusively take the value mapped to the 
 263  
        * specified key from the cache. Invoking this is effectively
 264  
        * removing the value from the cache.
 265  
        * 
 266  
        * @param key this is the key to acquire the cache value with
 267  
        * 
 268  
        * @return this returns the value mapped to the specified key 
 269  
        */
 270  
       public synchronized T take(Object key) {
 271  0
          return remove(key);
 272  
       }
 273  
  
 274  
       /**
 275  
        * This is used to determine whether the specified key exists
 276  
        * with in the cache. Typically this can be done using the 
 277  
        * fetch method, which will acquire the object. 
 278  
        * 
 279  
        * @param key this is the key to check within this segment
 280  
        * 
 281  
        * @return true if the specified key is within the cache
 282  
        */
 283  
       public synchronized boolean contains(Object key) {
 284  0
          return containsKey(key);
 285  
       }
 286  
    }
 287  
 }