1 /**********************************************************************
2 * SoftKeyedObjectCache.java
3 * created on 12.03.2005 by netseeker
4 * $Source: /cvsroot/ejoe/EJOE/src/de/netseeker/ejoe/cache/SoftKeyedObjectCache.java,v $
5 * $Date: 2006/02/04 14:16:21 $
6 * $Revision: 1.3 $
7 *********************************************************************/
8 package de.netseeker.ejoe.cache;
9
10 import java.lang.ref.Reference;
11 import java.lang.ref.ReferenceQueue;
12 import java.lang.ref.SoftReference;
13 import java.util.Iterator;
14 import java.util.LinkedHashMap;
15 import java.util.Map;
16
17 /***
18 * A keyed value cache implementation that uses soft references to store cached
19 * values. Once a value got garbage collected by the JVM, the associated
20 * key-value pair will be removed by the next put() or get() operation.
21 *
22 * @author netseeker
23 */
24 public final class SoftKeyedObjectCache
25 {
26 private final ReferenceQueue _refQueue = new ReferenceQueue();
27
28 private int _size = 10000;
29
30 private final Map _cache;
31
32 private final Map _references;
33
34 /***
35 * Create a new cache which will store max. 10000 objects. If the cache
36 * reaches max. size it will ditch some of the oldest key-value pairs by
37 * each put operation
38 */
39 public SoftKeyedObjectCache()
40 {
41 this._cache = new LinkedHashMap(this._size);
42 this._references = new LinkedHashMap(this._size);
43 }
44
45 /***
46 * Create a new cache of the given size. If the cache reaches max. size it
47 * will ditch some of the oldest key-value pairs by each put operation
48 *
49 * @param size
50 * max. amount of objects to hold in this cache
51 */
52 public SoftKeyedObjectCache(int size)
53 {
54 this._size = size;
55 this._cache = new LinkedHashMap(size);
56 this._references = new LinkedHashMap(size);
57 }
58
59 /***
60 * Adds a new entry to this cache under the given key
61 *
62 * @param key
63 * identifier under which the given object will be found in this
64 * cache
65 * @param obj
66 * object to cache
67 */
68 public void put(Object key, Object obj)
69 {
70 clean();
71
72 if (this._cache.size() >= this._size)
73 {
74
75
76 Iterator it = this._cache.keySet().iterator();
77 int count = 0;
78 do
79 {
80 remove(it.next());
81 count++;
82 }
83 while (count < 5 && it.hasNext());
84 }
85
86 SoftReference ref = new SoftReference(obj, this._refQueue);
87 this._cache.put(key, ref);
88 this._references.put(ref, key);
89 }
90
91 /***
92 * Returns an entry from this cache
93 *
94 * @param key
95 * @return object that is cached under the given key or null if either no
96 * object is cached under the given key, or an object was cached
97 * under the given key but is already garbage collected
98 */
99 public Object get(Object key)
100 {
101 clean();
102 Object value = null;
103 SoftReference ref = (SoftReference) this._cache.get(key);
104 if (ref != null)
105 {
106 value = ref.get();
107 }
108 return value;
109 }
110
111 /***
112 * Removes the given key and it's associated value from this cache
113 *
114 * @param key
115 * @return the removed value or null
116 */
117 public Object remove(Object key)
118 {
119 clean();
120 Object obj = null;
121
122 if (key != null)
123 {
124 SoftReference ref = (SoftReference) this._cache.get(key);
125 if (ref != null)
126 {
127 obj = ref.get();
128 this._references.remove(ref);
129 }
130 this._cache.remove(key);
131 }
132
133 return obj;
134 }
135
136 /***
137 * garbage collects all dereferenced objects in this cache
138 */
139 private void clean()
140 {
141 Reference ref;
142
143 while ((ref = this._refQueue.poll()) != null)
144 {
145 Object key = this._references.get(ref);
146 if (key != null)
147 {
148 this._cache.remove(key);
149 }
150
151 this._references.remove(ref);
152 }
153 }
154 }