%line | %branch | |||||||||
---|---|---|---|---|---|---|---|---|---|---|
org.apache.jetspeed.cache.file.FileCache$FileCacheScanner |
|
|
1 | /* |
|
2 | * Licensed to the Apache Software Foundation (ASF) under one or more |
|
3 | * contributor license agreements. See the NOTICE file distributed with |
|
4 | * this work for additional information regarding copyright ownership. |
|
5 | * The ASF licenses this file to You under the Apache License, Version 2.0 |
|
6 | * (the "License"); you may not use this file except in compliance with |
|
7 | * the License. You may 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 |
|
12 | * distributed under the License is distributed on an "AS IS" BASIS, |
|
13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|
14 | * See the License for the specific language governing permissions and |
|
15 | * limitations under the License. |
|
16 | */ |
|
17 | ||
18 | package org.apache.jetspeed.cache.file; |
|
19 | ||
20 | import java.util.Collection; |
|
21 | import java.util.Collections; |
|
22 | import java.util.Date; |
|
23 | import java.util.Map; |
|
24 | import java.util.HashMap; |
|
25 | import java.util.List; |
|
26 | import java.util.LinkedList; |
|
27 | import java.util.Iterator; |
|
28 | import java.io.File; |
|
29 | import java.io.FileNotFoundException; |
|
30 | ||
31 | import org.apache.commons.logging.Log; |
|
32 | import org.apache.commons.logging.LogFactory; |
|
33 | ||
34 | /** |
|
35 | * FileCache keeps a cache of files up-to-date with a most simple eviction policy. |
|
36 | * The eviction policy will keep n items in the cache, and then start evicting |
|
37 | * the items ordered-by least used first. The cache runs a thread to check for |
|
38 | * both evictions and refreshes. |
|
39 | * |
|
40 | * @author David S. Taylor <a href="mailto:taylor@apache.org">David Sean Taylor</a> |
|
41 | * @version $Id: FileCache.java 516448 2007-03-09 16:25:47Z ate $ |
|
42 | */ |
|
43 | ||
44 | public class FileCache implements java.util.Comparator |
|
45 | { |
|
46 | protected long scanRate = 300; // every 5 minutes |
|
47 | protected int maxSize = 100; // maximum of 100 items |
|
48 | protected List listeners = new LinkedList(); |
|
49 | ||
50 | private FileCacheScanner scanner = null; |
|
51 | private Map cache = null; |
|
52 | ||
53 | private final static Log log = LogFactory.getLog(FileCache.class); |
|
54 | ||
55 | /** |
|
56 | * Default constructor. Use default values for scanReate and maxSize |
|
57 | * |
|
58 | */ |
|
59 | public FileCache() |
|
60 | { |
|
61 | cache = Collections.synchronizedMap(new HashMap()); |
|
62 | this.scanner = new FileCacheScanner(); |
|
63 | this.scanner.setDaemon(true); |
|
64 | } |
|
65 | ||
66 | /** |
|
67 | * Set scanRate and maxSize |
|
68 | * |
|
69 | * @param scanRate how often in seconds to refresh and evict from the cache |
|
70 | * @param maxSize the maximum allowed size of the cache before eviction starts |
|
71 | */ |
|
72 | public FileCache(long scanRate, |
|
73 | int maxSize) |
|
74 | { |
|
75 | ||
76 | cache = Collections.synchronizedMap(new HashMap()); |
|
77 | ||
78 | this.scanRate = scanRate; |
|
79 | this.maxSize = maxSize; |
|
80 | this.scanner = new FileCacheScanner(); |
|
81 | this.scanner.setDaemon(true); |
|
82 | } |
|
83 | ||
84 | /** |
|
85 | * Set all parameters on the cache |
|
86 | * |
|
87 | * @param initialCapacity the initial size of the cache as passed to HashMap |
|
88 | * @param loadFactor how full the hash table is allowed to get before increasing |
|
89 | * @param scanRate how often in seconds to refresh and evict from the cache |
|
90 | * @param maxSize the maximum allowed size of the cache before eviction starts |
|
91 | */ |
|
92 | public FileCache(int initialCapacity, |
|
93 | int loadFactor, |
|
94 | long scanRate, |
|
95 | int maxSize) |
|
96 | { |
|
97 | cache = Collections.synchronizedMap(new HashMap(initialCapacity, loadFactor)); |
|
98 | ||
99 | this.scanRate = scanRate; |
|
100 | this.maxSize = maxSize; |
|
101 | this.scanner = new FileCacheScanner(); |
|
102 | this.scanner.setDaemon(true); |
|
103 | } |
|
104 | ||
105 | /** |
|
106 | * Set the new refresh scan rate on managed files. |
|
107 | * |
|
108 | * @param scanRate the new scan rate in seconds |
|
109 | */ |
|
110 | public void setScanRate(long scanRate) |
|
111 | { |
|
112 | this.scanRate= scanRate; |
|
113 | } |
|
114 | ||
115 | /** |
|
116 | * Get the refresh scan rate |
|
117 | * |
|
118 | * @return the current refresh scan rate in seconds |
|
119 | */ |
|
120 | public long getScanRate() |
|
121 | { |
|
122 | return scanRate; |
|
123 | } |
|
124 | ||
125 | /** |
|
126 | * Set the new maximum size of the cache |
|
127 | * |
|
128 | * @param maxSize the maximum size of the cache |
|
129 | */ |
|
130 | public void setMaxSize(int maxSize) |
|
131 | { |
|
132 | this.maxSize = maxSize; |
|
133 | } |
|
134 | ||
135 | /** |
|
136 | * Get the maximum size of the cache |
|
137 | * |
|
138 | * @return the current maximum size of the cache |
|
139 | */ |
|
140 | public int getMaxSize() |
|
141 | { |
|
142 | return maxSize; |
|
143 | } |
|
144 | ||
145 | /** |
|
146 | * Gets an entry from the cache given a key |
|
147 | * |
|
148 | * @param key the key to look up the entry by |
|
149 | * @return the entry |
|
150 | */ |
|
151 | public FileCacheEntry get(String key) |
|
152 | { |
|
153 | return (FileCacheEntry) cache.get(key); |
|
154 | } |
|
155 | ||
156 | /** |
|
157 | * Gets an entry from the cache given a key |
|
158 | * |
|
159 | * @param key the key to look up the entry by |
|
160 | * @return the entry |
|
161 | */ |
|
162 | public Object getDocument(String key) |
|
163 | { |
|
164 | FileCacheEntry entry = (FileCacheEntry) cache.get(key); |
|
165 | if (entry != null) |
|
166 | { |
|
167 | return entry.getDocument(); |
|
168 | } |
|
169 | return null; |
|
170 | } |
|
171 | ||
172 | /** |
|
173 | * Puts a file entry in the file cache |
|
174 | * |
|
175 | * @param file The file to be put in the cache |
|
176 | * @param document the cached document |
|
177 | */ |
|
178 | public void put(File file, Object document) |
|
179 | throws java.io.IOException |
|
180 | { |
|
181 | if(!file.exists()) |
|
182 | { |
|
183 | throw new FileNotFoundException("File to cache: "+file.getAbsolutePath()+" does not exist."); |
|
184 | } |
|
185 | FileCacheEntry entry = new FileCacheEntryImpl(file, document); |
|
186 | cache.put(file.getCanonicalPath(), entry); |
|
187 | } |
|
188 | ||
189 | /** |
|
190 | * Puts a file entry in the file cache |
|
191 | * |
|
192 | * @param path the full path name of the file |
|
193 | * @param document the cached document |
|
194 | */ |
|
195 | public void put(String key, Object document, File rootFile) |
|
196 | throws java.io.IOException |
|
197 | { |
|
198 | File file = new File(rootFile, key); |
|
199 | if(!file.exists()) |
|
200 | { |
|
201 | throw new FileNotFoundException("File to cache: "+file.getAbsolutePath()+" does not exist."); |
|
202 | } |
|
203 | FileCacheEntry entry = new FileCacheEntryImpl(file, document); |
|
204 | cache.put(key, entry); |
|
205 | } |
|
206 | ||
207 | /** |
|
208 | * Removes a file entry from the file cache |
|
209 | * |
|
210 | * @param key the full path name of the file |
|
211 | * @return the entry removed |
|
212 | */ |
|
213 | public Object remove(String key) |
|
214 | { |
|
215 | return cache.remove(key); |
|
216 | } |
|
217 | ||
218 | ||
219 | /** |
|
220 | * Add a File Cache Event Listener |
|
221 | * |
|
222 | * @param listener the event listener |
|
223 | */ |
|
224 | public void addListener(FileCacheEventListener listener) |
|
225 | { |
|
226 | listeners.add(listener); |
|
227 | } |
|
228 | ||
229 | /** |
|
230 | * Start the file Scanner running at the current scan rate. |
|
231 | * |
|
232 | */ |
|
233 | public void startFileScanner() |
|
234 | { |
|
235 | try |
|
236 | { |
|
237 | ||
238 | this.scanner.start(); |
|
239 | } |
|
240 | catch (java.lang.IllegalThreadStateException e) |
|
241 | { |
|
242 | log.error("Exception starting scanner", e); |
|
243 | } |
|
244 | } |
|
245 | ||
246 | /** |
|
247 | * Stop the file Scanner |
|
248 | * |
|
249 | */ |
|
250 | public void stopFileScanner() |
|
251 | { |
|
252 | this.scanner.setStopping(true); |
|
253 | } |
|
254 | ||
255 | /** |
|
256 | * Evicts entries based on last accessed time stamp |
|
257 | * |
|
258 | */ |
|
259 | protected void evict() |
|
260 | { |
|
261 | synchronized (cache) |
|
262 | { |
|
263 | if (this.getMaxSize() >= cache.size()) |
|
264 | { |
|
265 | return; |
|
266 | } |
|
267 | ||
268 | List list = new LinkedList(cache.values()); |
|
269 | Collections.sort(list, this); |
|
270 | ||
271 | int count = 0; |
|
272 | int limit = cache.size() - this.getMaxSize(); |
|
273 | ||
274 | for (Iterator it = list.iterator(); it.hasNext(); ) |
|
275 | { |
|
276 | if (count >= limit) |
|
277 | { |
|
278 | break; |
|
279 | } |
|
280 | ||
281 | FileCacheEntry entry = (FileCacheEntry) it.next(); |
|
282 | String key = null; |
|
283 | try |
|
284 | { |
|
285 | key = entry.getFile().getCanonicalPath(); |
|
286 | } |
|
287 | catch (java.io.IOException e) |
|
288 | { |
|
289 | log.error("Exception getting file path: ", e); |
|
290 | } |
|
291 | // notify that eviction will soon take place |
|
292 | for (Iterator lit = this.listeners.iterator(); lit.hasNext(); ) |
|
293 | { |
|
294 | FileCacheEventListener listener = |
|
295 | (FileCacheEventListener) lit.next(); |
|
296 | try |
|
297 | { |
|
298 | listener.evict(entry); |
|
299 | } |
|
300 | catch (Exception e1) |
|
301 | { |
|
302 | log.warn("Unable to evict cache entry. "+e1.toString(), e1); |
|
303 | } |
|
304 | } |
|
305 | cache.remove(key); |
|
306 | ||
307 | count++; |
|
308 | } |
|
309 | } |
|
310 | } |
|
311 | ||
312 | /** |
|
313 | * Evicts all entries |
|
314 | * |
|
315 | */ |
|
316 | public void evictAll() |
|
317 | { |
|
318 | synchronized (cache) |
|
319 | { |
|
320 | // evict all cache entries |
|
321 | List list = new LinkedList(cache.values()); |
|
322 | for (Iterator it = list.iterator(); it.hasNext(); ) |
|
323 | { |
|
324 | // evict cache entry |
|
325 | FileCacheEntry entry = (FileCacheEntry) it.next(); |
|
326 | // notify that eviction will soon take place |
|
327 | for (Iterator lit = this.listeners.iterator(); lit.hasNext(); ) |
|
328 | { |
|
329 | FileCacheEventListener listener = |
|
330 | (FileCacheEventListener) lit.next(); |
|
331 | try |
|
332 | { |
|
333 | listener.evict(entry); |
|
334 | } |
|
335 | catch (Exception e1) |
|
336 | { |
|
337 | log.warn("Unable to evict cache entry. "+e1.toString(), e1); |
|
338 | } |
|
339 | } |
|
340 | // remove from cache by key |
|
341 | String key = null; |
|
342 | try |
|
343 | { |
|
344 | key = entry.getFile().getCanonicalPath(); |
|
345 | } |
|
346 | catch (java.io.IOException e) |
|
347 | { |
|
348 | log.error("Exception getting file path: ", e); |
|
349 | } |
|
350 | cache.remove(key); |
|
351 | } |
|
352 | } |
|
353 | } |
|
354 | ||
355 | /** |
|
356 | * Comparator function for sorting by last accessed during eviction |
|
357 | * |
|
358 | */ |
|
359 | public int compare(Object o1, Object o2) |
|
360 | { |
|
361 | FileCacheEntry e1 = (FileCacheEntry)o1; |
|
362 | FileCacheEntry e2 = (FileCacheEntry)o2; |
|
363 | if (e1.getLastAccessed() < e2.getLastAccessed()) |
|
364 | { |
|
365 | return -1; |
|
366 | } |
|
367 | else if (e1.getLastAccessed() == e2.getLastAccessed()) |
|
368 | { |
|
369 | return 0; |
|
370 | } |
|
371 | return 1; |
|
372 | } |
|
373 | ||
374 | /** |
|
375 | * inner class that runs as a thread to scan the cache for updates or evictions |
|
376 | * |
|
377 | */ |
|
378 | 0 | protected class FileCacheScanner extends Thread |
379 | { |
|
380 | 0 | private boolean stopping = false; |
381 | ||
382 | public void setStopping(boolean flag) |
|
383 | { |
|
384 | 0 | this.stopping = flag; |
385 | 0 | } |
386 | ||
387 | /** |
|
388 | * Run the file scanner thread |
|
389 | * |
|
390 | */ |
|
391 | public void run() |
|
392 | { |
|
393 | 0 | boolean done = false; |
394 | ||
395 | try |
|
396 | { |
|
397 | 0 | while(!done) |
398 | { |
|
399 | try |
|
400 | { |
|
401 | 0 | int count = 0; |
402 | 0 | Collection values = Collections.synchronizedCollection(FileCache.this.cache.values()); |
403 | 0 | synchronized (values) |
404 | { |
|
405 | 0 | for (Iterator it = values.iterator(); it.hasNext(); ) |
406 | { |
|
407 | 0 | FileCacheEntry entry = (FileCacheEntry) it.next(); |
408 | 0 | Date modified = new Date(entry.getFile().lastModified()); |
409 | ||
410 | 0 | if (modclass="keyword">ified.after(entry.getLastModclass="keyword">ified())) |
411 | { |
|
412 | 0 | for (Iterator lit = FileCache.this.listeners.iterator(); lit.hasNext(); ) |
413 | { |
|
414 | 0 | FileCacheEventListener listener = |
415 | (FileCacheEventListener) lit.next(); |
|
416 | try |
|
417 | { |
|
418 | 0 | listener.refresh(entry); |
419 | } |
|
420 | 0 | catch (Exception e1) |
421 | { |
|
422 | 0 | log.warn("Unable to refresh cached document: "+e1.toString(), e1); |
423 | 0 | } |
424 | 0 | entry.setLastModified(modified); |
425 | 0 | } |
426 | } |
|
427 | 0 | count++; |
428 | 0 | } |
429 | 0 | } |
430 | 0 | if (count > FileCache.this.getMaxSize()) |
431 | { |
|
432 | 0 | FileCache.this.evict(); |
433 | } |
|
434 | } |
|
435 | 0 | catch (Exception e) |
436 | { |
|
437 | 0 | log.error("FileCache Scanner: Error in iteration...", e); |
438 | 0 | } |
439 | ||
440 | 0 | sleep(FileCache.this.getScanRate() * 1000); |
441 | ||
442 | 0 | if (this.stopping) |
443 | { |
|
444 | 0 | this.stopping = false; |
445 | 0 | done = true; |
446 | } |
|
447 | } |
|
448 | } |
|
449 | 0 | catch (InterruptedException e) |
450 | { |
|
451 | 0 | log.error("FileCacheScanner: recieved interruption, exiting.", e); |
452 | 0 | } |
453 | 0 | } |
454 | } // end inner class: FileCacheScanner |
|
455 | ||
456 | ||
457 | /** |
|
458 | * get an iterator over the cache values |
|
459 | * |
|
460 | * @return iterator over the cache values |
|
461 | */ |
|
462 | public Iterator getIterator() |
|
463 | { |
|
464 | return cache.values().iterator(); |
|
465 | } |
|
466 | ||
467 | /** |
|
468 | * get the size of the cache |
|
469 | * |
|
470 | * @return the size of the cache |
|
471 | */ |
|
472 | public int getSize() |
|
473 | { |
|
474 | return cache.size(); |
|
475 | } |
|
476 | } |
|
477 |
This report is generated by jcoverage, Maven and Maven JCoverage Plugin. |