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
72 entry = lookup.get(key);
73 if (entry == null) {
74
75
76 entry = new LazyCacheEntry<K, E>(key, factory);
77 entries.add(entry);
78 lookup.put(key, entry);
79
80
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
105 }
106 }
107 entries.clear();
108 lookup.clear();
109 } finally {
110 lock.unlock();
111 }
112 }
113
114 }