feisty meow concerns codebase 2.140
TimedOutLRUCache.java
Go to the documentation of this file.
1package org.gffs.cache;
2
3/*
4 * Copyright 2006 University of Virginia
5 *
6 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may
7 * obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions
13 * and limitations under the License.
14 */
15
16import java.util.ArrayList;
17import java.util.Date;
18import java.util.HashMap;
19import java.util.HashSet;
20import java.util.List;
21import java.util.Set;
22
23import org.apache.commons.logging.Log;
24import org.apache.commons.logging.LogFactory;
25
39public class TimedOutLRUCache<KeyType, DataType>
40{
41 static Log _logger = LogFactory.getLog(TimedOutLRUCache.class);
42
43 private HashMap<KeyType, RoleBasedCacheNode<KeyType, DataType>> _map;
44 private LRUList<KeyType, DataType> _lruList;
45 private TimeoutList<KeyType, DataType> _timeoutList;
46
47 private int _maxElements;
48 private long _defaultTimeoutMS;
49 private Thread _activeTimeoutThread = null;
50 private boolean _logCacheEjection = false;
51 public String _cacheName = null; // the name for this cache.
52
53 public TimedOutLRUCache(int maxElements, long defaultTimeoutMS, String cacheName)
54 {
55 if (maxElements < 1)
56 throw new IllegalArgumentException("\"maxElements\" must be greater than 0.");
57 _cacheName = cacheName;
58 if (_cacheName == null)
59 throw new IllegalArgumentException("must provide a non-null cache name");
60
61 _maxElements = maxElements;
62 _defaultTimeoutMS = defaultTimeoutMS;
63 _map = new HashMap<KeyType, RoleBasedCacheNode<KeyType, DataType>>(_maxElements);
64 _lruList = new LRUList<KeyType, DataType>();
65 _timeoutList = new TimeoutList<KeyType, DataType>();
66 }
67
71 public int size()
72 {
73 return _map.size();
74 }
75
79 public void setCacheEjectionLogging(boolean logRemovals)
80 {
81 _logCacheEjection = logRemovals;
82 }
83
84 public void activelyTimeoutElements(boolean activelyTimeout)
85 {
86 synchronized (_map) {
87 if (_activeTimeoutThread == null) {
88 if (activelyTimeout)
90 } else {
91 if (!activelyTimeout)
93 }
94 }
95 }
96
97 public String debugPrefix()
98 {
99 return _cacheName + ": ";
100 }
101
102 public void put(KeyType key, DataType data, long timeoutMS)
103 {
104 RoleBasedCacheNode<KeyType, DataType> newNode =
105 new RoleBasedCacheNode<KeyType, DataType>(key, data, new Date(System.currentTimeMillis() + timeoutMS));
106
107 synchronized (_map) {
108 RoleBasedCacheNode<KeyType, DataType> oldNode = _map.remove(key);
109 if (oldNode != null) {
110 _lruList.remove(oldNode);
111 _timeoutList.remove(oldNode);
112 }
113
114 if (_map.size() >= _maxElements)
115 clearStale();
116
117 while (_map.size() >= _maxElements) {
118 RoleBasedCacheNode<KeyType, DataType> node = _lruList.removeFirst();
119 if (_logCacheEjection && _logger.isDebugEnabled())
120 _logger.debug(debugPrefix() + "overloaded cache: removing cached item with key: " + node.getKey());
121 _timeoutList.remove(node);
122 _map.remove(node.getKey());
123 }
124
125 _map.put(key, newNode);
126 _lruList.insert(newNode);
127 _timeoutList.insert(newNode);
128
129 _map.notify();
130 }
131 }
132
133 public void put(KeyType key, DataType data)
134 {
135 put(key, data, _defaultTimeoutMS);
136 }
137
139 {
140 return _maxElements;
141 }
142
147 public boolean refresh(KeyType key)
148 {
149 synchronized (_map) {
150 RoleBasedCacheNode<KeyType, DataType> node = _map.get(key);
151 if (node == null)
152 return false;
153 _lruList.remove(node);
154 node.setInvalidationDate(_defaultTimeoutMS);
155 // move the node to the end of the LRU list, since we just accessed it.
156 _lruList.insert(node);
157 // also fix its position in the timeout list.
158 _timeoutList.remove(node);
159 _timeoutList.insert(node);
160 return true;
161 }
162 }
163
164 // hmmm: highly experimental memory analysis code here!
165 // private final long CHECK_INTERVAL = 1000 * 60; // one minute interval between deep checks currently.
166 private final long CHECK_INTERVAL = 1000 * 10;// hmmm: way too fast interval, being used for debugging.
167
168 private Date _nextDeepSizeCheck = new Date((new Date().getTime()) + CHECK_INTERVAL);
169
170 public DataType get(KeyType key)
171 {
172 Date now = new Date();
173
174 if (now.after(_nextDeepSizeCheck)) {
175
176 /*
177 * hmmm: size check code below, not right yet.
178 *
179 * would be nice to break that output into k, m, g, etc.
180 *
181 * trying the deep footprint on 'this' is giving unrealistic very small sizes.
182 *
183 * also the deep size check is dying with a stack overflow during the large rns directory test, so we cannot use it yet.
184 */
185 // if (_logger.isDebugEnabled()) {
186 // long sizeUsed = MemoryFootprint.getDeepFootprint(_map) + MemoryFootprint.getDeepFootprint(_lruList)
187 // + MemoryFootprint.getDeepFootprint(_timeoutList);
188 // _logger.debug(SizeOf.humanReadable(sizeUsed) + " consumed by "+ _cacheName);
189 // }
190
191 _nextDeepSizeCheck = new Date((new Date().getTime()) + CHECK_INTERVAL);
192 }
193
194 synchronized (_map) {
195 RoleBasedCacheNode<KeyType, DataType> node = _map.get(key);
196 if (node == null)
197 return null;
198 _lruList.remove(node);
199 if (node.getInvalidationDate().before(now)) {
200 // this entry has become stale.
201 if (_logCacheEjection && _logger.isDebugEnabled())
202 _logger.debug(debugPrefix() + "timed-out entry in get: removing cached item with key: " + node.getKey());
203 _map.remove(key);
204 _timeoutList.remove(node);
205 return null;
206 }
207 // move the node to the end of the LRU list, since we just accessed it.
208 _lruList.insert(node);
209 return node.getData();
210 }
211 }
212
213 public List<DataType> getAll()
214 {
215 ArrayList<DataType> toReturn = new ArrayList<DataType>();
216 synchronized (_map) {
217 for (KeyType key : _map.keySet()) {
218 toReturn.add(_map.get(key).getData());
219 }
220 }
221 return toReturn;
222 }
223
224 public List<DataType> getAllReferenced(List<KeyType> references)
225 {
226 ArrayList<DataType> toReturn = new ArrayList<DataType>();
227 synchronized (_map) {
228 for (KeyType key : references) {
230 if (_map.containsKey(key)) {
231 toReturn.add(_map.get(key).getData());
232 } else {
233 _logger.error(debugPrefix() + "failed to locate referenced object in cache: " + key);
234 }
235 }
236 }
237 return toReturn;
238 }
239
240 public void clearStale()
241 {
242 Date now = new Date();
243
244 synchronized (_map) {
245 while (true) {
246 RoleBasedCacheNode<KeyType, DataType> node = _timeoutList.peekFirst();
247 if (node == null)
248 break;
249
250 if (node.getInvalidationDate().compareTo(now) <= 0) {
251 if (_logCacheEjection && _logger.isDebugEnabled())
252 _logger.debug(debugPrefix() + "removing timed-out node: " + node.getKey());
253 _map.remove(node.getKey());
254 _timeoutList.removeFirst();
255 _lruList.remove(node);
256 } else {
257 break;
258 }
259 }
260 }
261 }
262
263 public void remove(KeyType key)
264 {
265 synchronized (_map) {
266 RoleBasedCacheNode<KeyType, DataType> node = _map.remove(key);
267 if (node != null) {
268 _lruList.remove(node);
269 _timeoutList.remove(node);
270 }
271 }
272 }
273
274 public Set<KeyType> keySet()
275 {
276 synchronized (_map) {
277 return new HashSet<KeyType>(_map.keySet());
278 }
279 }
280
281 public void clear()
282 {
283 synchronized (_map) {
284 _map.clear();
285 _lruList.clear();
286 _timeoutList.clear();
287 }
288 }
289
290 public void startActiveTimeout()
291 {
292 _activeTimeoutThread = new Thread(new ActiveTimeoutWorker(), "Active Cache Timeout Thread");
293 _activeTimeoutThread.setDaemon(true);
294 _activeTimeoutThread.start();
295 }
296
297 public void stopActiveTimeout()
298 {
299 Thread tmp = _activeTimeoutThread;
300 _activeTimeoutThread = null;
301 synchronized (_map) {
302 _map.notify();
303 }
304
305 try {
306 tmp.join();
307 } catch (InterruptedException cause) {
308 }
309 }
310
311 private class ActiveTimeoutWorker implements Runnable
312 {
313 public void run()
314 {
315 synchronized (_map) {
316 while (_activeTimeoutThread != null) {
317 try {
318 clearStale();
319 RoleBasedCacheNode<KeyType, DataType> firstNode = _timeoutList.peekFirst();
320 if (firstNode == null) {
321 _map.wait();
322 } else {
323 Date nextStale = firstNode.getInvalidationDate();
324 long timeout = nextStale.getTime() - System.currentTimeMillis();
325 if (timeout > 0) {
326 _map.wait(timeout);
327 }
328 }
329 } catch (InterruptedException ie) {
330 }
331 }
332 }
333 }
334 }
335}
void put(KeyType key, DataType data, long timeoutMS)
void activelyTimeoutElements(boolean activelyTimeout)
void put(KeyType key, DataType data)
List< DataType > getAllReferenced(List< KeyType > references)
void setCacheEjectionLogging(boolean logRemovals)
TimedOutLRUCache(int maxElements, long defaultTimeoutMS, String cacheName)