1 | |
|
2 | |
|
3 | |
|
4 | |
|
5 | |
|
6 | |
|
7 | |
|
8 | |
|
9 | |
|
10 | |
|
11 | |
|
12 | |
|
13 | |
|
14 | |
|
15 | |
|
16 | |
package org.apache.commons.cache; |
17 | |
|
18 | |
import java.util.HashMap; |
19 | |
import java.util.ArrayList; |
20 | |
import java.util.NoSuchElementException; |
21 | |
import java.io.Serializable; |
22 | |
import java.io.ObjectInputStream; |
23 | |
import java.io.IOException; |
24 | |
import java.io.NotActiveException; |
25 | |
import org.apache.commons.cache.adt.Listable; |
26 | |
import org.apache.commons.cache.adt.WListableObject; |
27 | |
|
28 | |
|
29 | |
|
30 | |
|
31 | |
|
32 | |
|
33 | |
public class LRUEvictionPolicy implements EvictionPolicy, StorageListener, RetrievalListener, CachedObjectIterator { |
34 | 0 | transient protected HashMap _hash = new HashMap(); |
35 | 0 | transient protected WListableObject _leastRecent = null; |
36 | 0 | transient protected WListableObject _mostRecent = null; |
37 | 0 | transient protected WListableObject _current = null; |
38 | |
|
39 | 0 | protected boolean _canRemove = false; |
40 | 0 | public Cache _cache = null; |
41 | |
protected int _objsbetweennaps; |
42 | |
protected long _sleeptimemillis; |
43 | |
|
44 | |
public LRUEvictionPolicy() { |
45 | 0 | this(StaleObjectEvictor.DEFAULT_OBJECTS_BETWEEN_NAPS,StaleObjectEvictor.DEFAULT_SLEEP_TIME_MILLIS); |
46 | 0 | } |
47 | |
|
48 | 0 | public LRUEvictionPolicy(int objsbetweennaps, long sleeptimemillis) { |
49 | 0 | _objsbetweennaps = objsbetweennaps; |
50 | 0 | _sleeptimemillis = sleeptimemillis; |
51 | 0 | Thread t = new Thread(new StaleObjectEvictor(this,_objsbetweennaps,_sleeptimemillis)); |
52 | 0 | t.setDaemon(true); |
53 | 0 | t.start(); |
54 | 0 | } |
55 | |
|
56 | |
private void writeObject(java.io.ObjectOutputStream out) throws IOException { |
57 | 0 | out.defaultWriteObject(); |
58 | 0 | WListableObject cur = _leastRecent; |
59 | 0 | while(null != cur) { |
60 | 0 | out.writeObject(cur.getValue()); |
61 | 0 | cur =(WListableObject)(cur.getNext()); |
62 | |
} |
63 | 0 | out.writeObject(null); |
64 | 0 | } |
65 | |
|
66 | |
private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException { |
67 | |
try { |
68 | 0 | in.defaultReadObject(); |
69 | 0 | _hash = new HashMap(); |
70 | |
for(;;) { |
71 | 0 | Object obj = in.readObject(); |
72 | 0 | if(null == obj) { |
73 | 0 | break; |
74 | |
} else { |
75 | 0 | WListableObject temp = new WListableObject(obj,_mostRecent,null); |
76 | 0 | if(null == _leastRecent) { |
77 | 0 | _leastRecent = temp; |
78 | 0 | _current = temp; |
79 | |
} |
80 | 0 | if(null != _mostRecent) { |
81 | 0 | _mostRecent.setNext(temp); |
82 | |
} |
83 | 0 | _mostRecent = temp; |
84 | 0 | _hash.put(((CachedObjectInfo)obj).getKey(),_mostRecent); |
85 | |
} |
86 | 0 | } |
87 | |
|
88 | 0 | Thread t = new Thread(new StaleObjectEvictor(this,_objsbetweennaps,_sleeptimemillis)); |
89 | 0 | t.setDaemon(true); |
90 | 0 | t.start(); |
91 | 0 | } catch(NotActiveException e) { |
92 | 0 | e.printStackTrace(); |
93 | 0 | } |
94 | 0 | } |
95 | |
|
96 | |
protected synchronized void addToList(WListableObject cur) { |
97 | 0 | if(null == _mostRecent) { |
98 | 0 | _mostRecent = cur; |
99 | 0 | _leastRecent = cur; |
100 | |
} else { |
101 | 0 | _mostRecent.setNext(cur); |
102 | 0 | cur.setPrev(_mostRecent); |
103 | 0 | _mostRecent = cur; |
104 | |
} |
105 | 0 | } |
106 | |
|
107 | |
protected synchronized void removeFromList(WListableObject cur) { |
108 | |
|
109 | 0 | if(_current == cur) { |
110 | 0 | _current = (WListableObject)(_current.getPrev()); |
111 | 0 | _canRemove = false; |
112 | |
} |
113 | 0 | if(cur.hasPrev()) { |
114 | 0 | if(cur.hasNext()) { |
115 | 0 | cur.getPrev().setNext(cur.getNext()); |
116 | 0 | cur.getNext().setPrev(cur.getPrev()); |
117 | 0 | cur.setNext(null); |
118 | 0 | cur.setPrev(null); |
119 | |
} else { |
120 | |
|
121 | 0 | _mostRecent = (WListableObject)(cur.getPrev()); |
122 | 0 | cur.getPrev().setNext(null); |
123 | 0 | cur.setPrev(null); |
124 | |
} |
125 | |
} else { |
126 | |
|
127 | 0 | _leastRecent = (WListableObject)(cur.getNext()); |
128 | 0 | if(cur.hasNext()) { |
129 | 0 | cur.getNext().setPrev(null); |
130 | 0 | cur.setNext(null); |
131 | |
} else { |
132 | |
|
133 | |
|
134 | 0 | _leastRecent = null; |
135 | 0 | _mostRecent = null; |
136 | |
} |
137 | |
} |
138 | 0 | } |
139 | |
|
140 | |
|
141 | |
public synchronized boolean hasNext() { |
142 | 0 | return (null != _mostRecent); |
143 | |
} |
144 | |
|
145 | |
public synchronized CachedObjectInfo getNext() throws NoSuchElementException { |
146 | 0 | if(null != _current) { |
147 | 0 | _current = (WListableObject)(_current.getNext()); |
148 | |
} |
149 | 0 | if(null == _current) { _current = _leastRecent; } |
150 | 0 | if(null == _current) { |
151 | 0 | throw new NoSuchElementException(); |
152 | |
} else { |
153 | 0 | _canRemove = true; |
154 | 0 | return (CachedObjectInfo)(_current.getValue()); |
155 | |
} |
156 | |
} |
157 | |
|
158 | |
public synchronized Object next() throws NoSuchElementException { |
159 | 0 | return getNext(); |
160 | |
} |
161 | |
|
162 | |
public void remove() throws NoSuchElementException, IllegalStateException { |
163 | |
|
164 | 0 | synchronized(_cache) { |
165 | 0 | synchronized(this) { |
166 | 0 | if(null == _current) { |
167 | 0 | throw new IllegalStateException(); |
168 | |
} |
169 | 0 | if(_canRemove) { |
170 | 0 | _cache.clear( ((CachedObjectInfo)(_current.getValue())).getKey() ); |
171 | |
} else { |
172 | 0 | throw new IllegalStateException(); |
173 | |
} |
174 | 0 | } |
175 | 0 | } |
176 | 0 | } |
177 | |
|
178 | |
|
179 | |
|
180 | |
public void setCache(Cache c) { |
181 | 0 | if(null != _cache) { |
182 | 0 | Object mutex = _cache; |
183 | 0 | synchronized(mutex) { |
184 | 0 | synchronized(c) { |
185 | 0 | synchronized(this) { |
186 | 0 | unsetCache(); |
187 | 0 | _cache = c; |
188 | 0 | _cache.registerRetrievalListener(this); |
189 | 0 | _cache.registerStorageListener(this); |
190 | 0 | } |
191 | 0 | } |
192 | 0 | } |
193 | 0 | } else { |
194 | 0 | synchronized(c) { |
195 | 0 | synchronized(this) { |
196 | 0 | _cache = c; |
197 | 0 | _cache.registerRetrievalListener(this); |
198 | 0 | _cache.registerStorageListener(this); |
199 | 0 | } |
200 | 0 | } |
201 | |
} |
202 | 0 | } |
203 | |
|
204 | |
|
205 | |
public void unsetCache() { |
206 | 0 | if(null != _cache) { |
207 | 0 | Object mutex = _cache; |
208 | 0 | synchronized(mutex) { |
209 | 0 | synchronized(this) { |
210 | 0 | _cache.unregisterRetrievalListener(this); |
211 | 0 | _cache.unregisterStorageListener(this); |
212 | 0 | _cache = null; |
213 | 0 | } |
214 | 0 | } |
215 | |
} |
216 | 0 | } |
217 | |
|
218 | 0 | public void storeRequested(Serializable key, Serializable val, Long expiresAt, Long cost, Serializable group) { } |
219 | |
|
220 | 0 | public void notStored(Serializable key, Serializable val, Long expiresAt, Long cost, Serializable group) { } |
221 | |
|
222 | |
public synchronized void stored(Serializable key, Serializable val, Long expiresAt, Long cost, Serializable group) { |
223 | 0 | WListableObject cur = (WListableObject)(_hash.get(key)); |
224 | 0 | if(null == cur) { |
225 | 0 | cur = new WListableObject(new CachedObjectInfoImpl(key,expiresAt,cost)); |
226 | 0 | addToList(cur); |
227 | 0 | _hash.put(key,cur); |
228 | |
} else { |
229 | 0 | removeFromList(cur); |
230 | 0 | addToList(cur); |
231 | |
} |
232 | 0 | } |
233 | |
|
234 | |
public synchronized void cleared(Serializable key) { |
235 | 0 | WListableObject cur = (WListableObject)(_hash.get(key)); |
236 | 0 | if(cur != null) { |
237 | 0 | removeFromList(cur); |
238 | 0 | _hash.remove(key); |
239 | |
} |
240 | 0 | } |
241 | |
|
242 | |
public synchronized void cleared() { |
243 | 0 | _hash.clear(); |
244 | 0 | _mostRecent = null; |
245 | 0 | _leastRecent = null; |
246 | 0 | _current = null; |
247 | 0 | } |
248 | |
|
249 | |
public void retrieveRequested(Serializable key) { |
250 | 0 | } |
251 | |
|
252 | |
public synchronized void retrieved(Serializable key) { |
253 | 0 | WListableObject cur = (WListableObject)(_hash.get(key)); |
254 | 0 | if(null == cur) { |
255 | 0 | cur = new WListableObject(new CachedObjectInfoImpl(key,null,null)); |
256 | 0 | addToList(cur); |
257 | 0 | _hash.put(key,cur); |
258 | |
} else { |
259 | 0 | removeFromList(cur); |
260 | 0 | addToList(cur); |
261 | |
} |
262 | 0 | } |
263 | |
|
264 | |
public void notRetrieved(Serializable key) { |
265 | 0 | } |
266 | |
|
267 | |
public synchronized Serializable getEvictionCandidate() { |
268 | |
try { |
269 | 0 | return ((CachedObjectInfoImpl)(_leastRecent.getValue())).getKey(); |
270 | 0 | } catch(NullPointerException e) { |
271 | 0 | return null; |
272 | |
} |
273 | |
} |
274 | |
|
275 | |
public synchronized Serializable[] getEvictionCandidates(int n) { |
276 | 0 | ArrayList v = new ArrayList(n); |
277 | 0 | WListableObject c = _leastRecent; |
278 | 0 | while(n-- > 0) { |
279 | 0 | if(null != c) { |
280 | 0 | v.add(((CachedObjectInfoImpl)(c.getValue())).getKey()); |
281 | 0 | c = (WListableObject)(c.getNext()); |
282 | |
} else { |
283 | |
break; |
284 | |
} |
285 | |
} |
286 | 0 | return (Serializable[])(v.toArray(new Serializable[0])); |
287 | |
} |
288 | |
|
289 | |
public synchronized String toString() { |
290 | 0 | StringBuffer buf = new StringBuffer(); |
291 | 0 | WListableObject c = _leastRecent; |
292 | 0 | while(c != null) { |
293 | 0 | if(c.hasPrev()) { |
294 | 0 | buf.append(", "); |
295 | |
} |
296 | 0 | buf.append(c.getValue()); |
297 | 0 | c = (WListableObject)(c.getNext()); |
298 | |
} |
299 | 0 | return buf.toString(); |
300 | |
} |
301 | |
} |