001: package ri.cache;
002:
003: import java.util.*;
004: import java.util.concurrent.ConcurrentHashMap;
005: import java.util.concurrent.ConcurrentMap;
006: import javax.cache.*;
007: import ri.cache.eviction.EvictionStrategy;
008: import javax.cache.spi.CacheLoader;
009:
010: import ri.cache.eviction.LRUChainEvictionStrategy;
011: import ri.cache.loader.NullCacheLoader;
012:
013: /**
014: * BasicCache
015: *
016: * @author Brian Goetz
017: */
018: public class BasicCache<K, V> extends AbstractCache<K, V> implements
019: Cache<K, V> {
020:
021: private static final int DEFAULT_TTL = -1;
022: private static final int DEFAULT_CAPACITY = 100;
023:
024: protected final CacheLoader<K, V> loader;
025: protected final EvictionStrategy<K, V> evictionStrategy;
026: protected final ConcurrentMap<K, CacheEntry<K, V>> map = new ConcurrentHashMap<K, CacheEntry<K, V>>();
027: protected final long timeToLive;
028: protected final int capacity;
029: protected final ReferenceStatistics statistics = new ReferenceStatistics();
030: // protected final AsyncCacheWarmer warmer = new AsyncCacheWarmer(this);
031: protected Set<Map.Entry<K, V>> entrySet;
032:
033: public BasicCache(String name) {
034: this (name, DEFAULT_CAPACITY);
035: }
036:
037: public BasicCache(String name, int capacity) {
038: this (name, capacity, DEFAULT_TTL);
039: }
040:
041: public BasicCache(String name, CacheLoader<K, V> loader) {
042: this (name, DEFAULT_CAPACITY, DEFAULT_TTL,
043: new LRUChainEvictionStrategy<K, V>(), loader);
044: }
045:
046: public BasicCache(String name, int capacity, long timeToLive) {
047: this (name, capacity, timeToLive,
048: new LRUChainEvictionStrategy<K, V>(),
049: new NullCacheLoader<K, V>());
050: }
051:
052: public BasicCache(String name,
053: EvictionStrategy<K, V> evictionStrategy,
054: CacheLoader<K, V> loader) {
055: this (name, DEFAULT_CAPACITY, DEFAULT_TTL, evictionStrategy,
056: loader);
057: }
058:
059: public BasicCache(String name, int capacity, long timeToLive,
060: EvictionStrategy<K, V> evictionStrategy,
061: CacheLoader<K, V> loader) {
062: super (name);
063: this .loader = loader;
064: this .evictionStrategy = evictionStrategy;
065: this .timeToLive = timeToLive;
066: this .capacity = capacity;
067: }
068:
069: private static boolean isValid(CacheEntry ce) {
070: return ce != null && ce.isValid();
071: }
072:
073: protected V putEntry(K key, V value) {
074: if (map.size() >= capacity)
075: evict();
076: CacheEntry<K, V> ce = evictionStrategy.createEntry(key, value,
077: timeToLive);
078: CacheEntry<K, V> oldEntry = map.put(key, ce);
079: if (oldEntry != null)
080: evictionStrategy.discardEntry(oldEntry);
081: if (isValid(oldEntry))
082: return oldEntry.getValue();
083: return null;
084: }
085:
086: protected void loadEntry(K key, V value) {
087: putEntry(key, value);
088: listeners.onLoad(key);
089: }
090:
091: public V peek(K key) {
092: CacheEntry<K, V> ce = getCacheEntry(key);
093: return ce != null ? ce.getValue() : null;
094: }
095:
096: public V get(Object key) {
097: CacheEntry<K, V> ce = getCacheEntry((K) key);
098: if (ce != null)
099: return ce.getValue();
100:
101: V o = null;
102: try {
103: o = loader.load((K) key);
104: } catch (CacheException e) {
105: // do nothing -- will cause return of null
106: // @@@ inform an exception listener so it can be logged
107: }
108:
109: if (o != null)
110: loadEntry((K) key, o);
111:
112: return o;
113: }
114:
115: public Map<K, V> getAll(Collection<? extends K> keys)
116: throws CacheException {
117: Map<K, V> results = new HashMap<K, V>(keys.size());
118: List<K> toLoad = new ArrayList<K>();
119:
120: for (K key : keys) {
121: CacheEntry<K, V> ce = getCacheEntry(key);
122: if (ce != null)
123: results.put(ce.getKey(), ce.getValue());
124: else
125: toLoad.add(key);
126: }
127:
128: if (toLoad.size() > 0) {
129: Map<K, V> m = loader.loadAll(toLoad);
130: for (K key : m.keySet()) {
131: loadEntry(key, m.get(key));
132: }
133: results.putAll(m);
134: }
135:
136: return results;
137: }
138:
139: public void load(K key) throws CacheException {
140: // warmer.load(key);
141: // TODO: Implement me
142: System.out.println("TODO: implement me");
143: }
144:
145: public void loadAll(Collection<? extends K> keys)
146: throws CacheException {
147: //warmer.loadAll(keys);
148: // TODO: Implement me
149: System.out.println("TODO: implement me");
150: }
151:
152: public V remove(Object key) {
153: CacheEntry<K, V> ce = map.remove(key);
154: if (ce != null) {
155: evictionStrategy.discardEntry(ce);
156: }
157: if (isValid(ce)) {
158: listeners.onRemove((K) key);
159: return ce.getValue();
160: }
161: return null;
162: }
163:
164: public V put(K key, V value) {
165: V oldValue = putEntry(key, value);
166: listeners.onPut(key);
167: return oldValue;
168: }
169:
170: public CacheEntry<K, V> getCacheEntry(K key) {
171: CacheEntry<K, V> ce = map.get(key);
172:
173: if (isValid(ce)) {
174: evictionStrategy.touchEntry(ce);
175: statistics.incrementHits();
176: return ce;
177: }
178:
179: if (ce != null) {
180: evictionStrategy.discardEntry(ce);
181: }
182:
183: statistics.incrementMisses();
184: return null;
185: }
186:
187: public boolean containsKey(Object key) {
188: CacheEntry ce = getCacheEntry((K) key);
189: return ce != null;
190: }
191:
192: public Set<Map.Entry<K, V>> entrySet() {
193: if (entrySet == null) {
194: entrySet = new AbstractSet<Map.Entry<K, V>>() {
195: public Iterator iterator() {
196: // FIXME adam@bea.com 23-Jun-04 - Need to filter out
197: // expired values.
198: return map.values().iterator();
199: }
200:
201: public boolean contains(Object o) {
202: if (!(o instanceof Map.Entry))
203: return false;
204: Map.Entry entry = (Map.Entry) o;
205: return BasicCache.this .containsKey(entry.getKey());
206: }
207:
208: public boolean remove(Object o) {
209: if (!(o instanceof Map.Entry))
210: return false;
211: Map.Entry entry = (Map.Entry) o;
212: Object old = BasicCache.this .remove(entry.getKey());
213: return old != null;
214: }
215:
216: public int size() {
217: return BasicCache.this .size();
218: }
219:
220: public void clear() {
221: BasicCache.this .clear();
222: }
223: };
224: }
225: return entrySet;
226: }
227:
228: public void evict() {
229: Map<K, V> m = evictionStrategy.evict(this );
230: for (K key : m.keySet()) {
231: map.remove(key);
232: listeners.onEvict(key);
233: }
234: }
235:
236: public V put(K key, V value, long timeToLive) {
237: // TODO: Implement me
238: System.out.println("TODO: implement me");
239: return null;
240: }
241:
242: public int size() {
243: return map.size();
244: }
245:
246: public void clear() {
247: statistics.clearStatistics();
248: evictionStrategy.clear();
249: map.clear();
250: listeners.onClear();
251: }
252:
253: public CacheStatistics getStatistics() {
254: return statistics;
255: }
256:
257: public void shutdown() {
258: //warmer.shutdown();
259: // TODO: Implement me
260: System.out.println("TODO: implement me");
261: }
262:
263: public void clearStatistics() {
264: statistics.clearStatistics();
265: }
266:
267: public CacheStatistics getCacheStatistics() {
268: return statistics;
269: }
270:
271: // public V putIfAbsent(K k, V v) {
272: // }
273: //
274: // public boolean remove(Object key, Object value) {
275: // }
276: //
277: // public boolean replace(K k, V v, V v1) {
278: // }
279: //
280: // public V replace(K k, V v) {
281: // }
282: }
|