View Javadoc

1   /***
2    * 
3    */
4   package de.cohesion.bssh.impl.util;
5   
6   import java.util.Comparator;
7   import java.util.HashMap;
8   import java.util.Map;
9   import java.util.SortedSet;
10  import java.util.TreeSet;
11  import java.util.concurrent.locks.Lock;
12  import java.util.concurrent.locks.ReentrantLock;
13  
14  import de.cohesion.bssh.impl.Factory;
15  
16  /***
17   * A cache implementation.
18   * <p>
19   * On a saturated cache miss the lowest element according to the given
20   * <tt>Comparator</tt> is replaced.
21   * 
22   * @author schulzs
23   */
24  public class Cache<K, E> {
25  
26  	public enum ReplacementStrategy implements Comparator<CacheEntry<?, ?>> {
27  
28  		LRU(new LRUStrategy());
29  
30  		private final Comparator<CacheEntry<?, ?>> c;
31  
32  		ReplacementStrategy(final Comparator<CacheEntry<?, ?>> c) {
33  			this.c = c;
34  		}
35  
36  		public int compare(final CacheEntry<?, ?> u, final CacheEntry<?, ?> v) {
37  			return c.compare(u, v);
38  		}
39  	}
40  
41  	private static class LRUStrategy implements Comparator<CacheEntry<?, ?>> {
42  		public int compare(final CacheEntry<?, ?> u, final CacheEntry<?, ?> v) {
43  			return Long.signum(u.getLastAccessTime() - v.getLastAccessTime());
44  		}
45  	}
46  
47  	private final Factory<K, E> factory;
48  
49  	private final SortedSet<CacheEntry<K, E>> entries;
50  
51  	private final Map<K, CacheEntry<K, E>> lookup;
52  
53  	private int capacity;
54  
55  	private final Lock lock;
56  
57  	public Cache(int capacity, final Factory<K, E> factory,
58  			final Comparator<? super CacheEntry<K, E>> sorter) {
59  		this.capacity = capacity;
60  		entries = new TreeSet<CacheEntry<K, E>>(sorter);
61  		lookup = new HashMap<K, CacheEntry<K, E>>();
62  		this.factory = factory;
63  		this.lock = new ReentrantLock();
64  	}
65  
66  	public E get(final K key) throws InstantiationException {
67  		CacheEntry<K, E> entry = null;
68  		lock.lock();
69  		try {
70  
71  			/* Check for cache hit. */
72  			entry = lookup.get(key);
73  			if (entry == null) {
74  
75  				/* Handle cache miss. */
76  				entry = new LazyCacheEntry<K, E>(key, factory);
77  				entries.add(entry);
78  				lookup.put(key, entry);
79  
80  				/* Ensure capacity constraint is satisfied. */
81  				if (entries.size() >= capacity) {
82  					CacheEntry<K, E> toBeReplaced = entries.first();
83  					entries.remove(toBeReplaced);
84  					lookup.remove(toBeReplaced.getKey());
85  					factory.destroy(entry.getValue());
86  				}
87  			}
88  
89  		} finally {
90  			lock.unlock();
91  		}
92  
93  		return entry.getValue();
94  
95  	}
96  
97  	public void invalidate() {
98  		lock.lock();
99  		try {
100 			for (CacheEntry<K, E> entry : entries) {
101 				try {
102 					factory.destroy(entry.getValue());
103 				} catch (InstantiationException ie) {
104 					/* Ignore. */
105 				}
106 			}
107 			entries.clear();
108 			lookup.clear();
109 		} finally {
110 			lock.unlock();
111 		}
112 	}
113 
114 }