1/*2 * Copyright 2000-2001,2004 The Apache Software Foundation.3 * 4 * Licensed under the Apache License, Version 2.0 (the "License");5 * you may not use this file except in compliance with the License.6 * You may obtain a copy of the License at7 * 8 * http://www.apache.org/licenses/LICENSE-2.09 * 10 * Unless required by applicable law or agreed to in writing, software11 * distributed under the License is distributed on an "AS IS" BASIS,12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.13 * See the License for the specific language governing permissions and14 * limitations under the License.15 */1617packageorg.apache.jetspeed.cache;
1819import java.util.Collections;
20import java.util.Date;
21import java.util.Map;
22import java.util.HashMap;
23import java.util.List;
24import java.util.LinkedList;
25import java.util.Iterator;
26import java.io.File;
2728// Jetspeed logging classes29import org.apache.jetspeed.services.logging.JetspeedLogFactoryService;
30import org.apache.jetspeed.services.logging.JetspeedLogger;
3132/***33 * FileCache keeps a cache of files up-to-date with a most simple eviction policy.34 * The eviction policy will keep n items in the cache, and then start evicting35 * the items ordered-by least used first. The cache runs a thread to check for36 * both evictions and refreshes.37 *38 * @author David S. Taylor <a href="mailto:taylor@apache.org">David Sean Taylor</a>39 * @version $Id: FileCache.java,v 1.5 2004/03/25 16:27:41 jford Exp $40 */4142publicclassFileCache implements java.util.Comparator
43 {
44protectedlong scanRate = 300; // every 5 minutes45protectedint maxSize = 100; // maximum of 100 items46protected List listeners = new LinkedList();
4748private FileCacheScanner scanner = null;
49private Map cache = null;
5051/***52 * Static initialization of the logger for this class53 */54privatestaticfinalJetspeedLogger logger = JetspeedLogFactoryService.getLogger(FileCache.class.getName());
5556/***57 * Default constructor. Use default values for scanReate and maxSize58 *59 */60publicFileCache()
61 {
62 cache = new HashMap();
63this.scanner = new FileCacheScanner();
64this.scanner.setDaemon(true);
65 }
6667/***68 * Set scanRate and maxSize69 *70 * @param scanRate how often in seconds to refresh and evict from the cache71 * @param maxSize the maximum allowed size of the cache before eviction starts72 */73publicFileCache(long scanRate,
74int maxSize)
75 {
76 cache = new HashMap();
7778this.scanRate = scanRate;
79this.maxSize = maxSize;
80this.scanner = new FileCacheScanner();
81this.scanner.setDaemon(true);
82 }
8384/***85 * Set all parameters on the cache86 *87 * @param initialCapacity the initial size of the cache as passed to HashMap88 * @param loadFactor how full the hash table is allowed to get before increasing89 * @param scanRate how often in seconds to refresh and evict from the cache90 * @param maxSize the maximum allowed size of the cache before eviction starts91 */92publicFileCache(int initialCapacity,
93int loadFactor,
94long scanRate,
95int maxSize)
96 {
97 cache = new HashMap(initialCapacity, loadFactor);
9899this.scanRate = scanRate;
100this.maxSize = maxSize;
101this.scanner = new FileCacheScanner();
102this.scanner.setDaemon(true);
103 }
104105/***106 * Set the new refresh scan rate on managed files.107 *108 * @param scanRate the new scan rate in seconds109 */110publicvoid setScanRate(long scanRate)
111 {
112this.scanRate= scanRate;
113 }
114115/***116 * Get the refresh scan rate 117 *118 * @return the current refresh scan rate in seconds119 */120publiclong getScanRate()
121 {
122return scanRate;
123 }
124125/***126 * Set the new maximum size of the cache 127 *128 * @param maxSize the maximum size of the cache129 */130publicvoid setMaxSize(int maxSize)
131 {
132this.maxSize = maxSize;
133 }
134135/***136 * Get the maximum size of the cache 137 *138 * @return the current maximum size of the cache139 */140publicint getMaxSize()
141 {
142return maxSize;
143 }
144145/***146 * Gets an entry from the cache given a key147 *148 * @param key the key to look up the entry by149 * @return the entry150 */151publicFileCacheEntry get(String key)
152 {
153return (FileCacheEntry) cache.get(key);
154 }
155156/***157 * Gets an entry from the cache given a key158 *159 * @param key the key to look up the entry by160 * @return the entry161 */162public Object getDocument(String key)
163 {
164FileCacheEntry entry = (FileCacheEntry) cache.get(key);
165if (entry != null)
166 {
167return entry.getDocument();
168 }
169returnnull;
170 }
171172/***173 * Puts a file entry in the file cache174 *175 * @param file The file to be put in the cache176 * @param document the cached document177 */178publicvoid put(File file, Object document)
179 throws java.io.IOException
180 {
181FileCacheEntry entry = newFileCacheEntry(file, document);
182 cache.put(file.getCanonicalPath(), entry);
183 }
184185/***186 * Puts a file entry in the file cache187 *188 * @param path the full path name of the file189 * @param document the cached document190 */191publicvoid put(String key, Object document)
192 throws java.io.IOException
193 {
194 File file = new File(key);
195FileCacheEntry entry = newFileCacheEntry(file, document);
196 cache.put(file.getCanonicalPath(), entry);
197 }
198199/***200 * Removes a file entry from the file cache201 *202 * @param key the full path name of the file203 * @return the entry removed204 */205public Object remove(String key)
206 {
207return cache.remove(key);
208 }
209210211/***212 * Add a File Cache Event Listener 213 *214 * @param listener the event listener215 */216publicvoid addListener(FileCacheEventListener listener)
217 {
218 listeners.add(listener);
219 }
220221/***222 * Start the file Scanner running at the current scan rate.223 *224 */225publicvoid startFileScanner()
226 {
227try228 {
229230this.scanner.start();
231 }
232catch (java.lang.IllegalThreadStateException e)
233 {
234 logger.error("Exception starting scanner", e);
235 }
236 }
237238/***239 * Stop the file Scanner 240 *241 */242publicvoid stopFileScanner()
243 {
244this.scanner.setStopping(true);
245 }
246247/***248 * Evicts entries based on last accessed time stamp249 *250 */251protectedvoid evict()
252 {
253synchronized (cache)
254 {
255if (this.getMaxSize() >= cache.size())
256 {
257return;
258 }
259260 List list = new LinkedList(cache.values());
261 Collections.sort(list, this);
262263int count = 0;
264int limit = cache.size() - this.getMaxSize();
265266for (Iterator it = list.iterator(); it.hasNext(); )
267 {
268if (count >= limit)
269 {
270break;
271 }
272273FileCacheEntry entry = (FileCacheEntry) it.next();
274 String key = null;
275try276 {
277 key = entry.getFile().getCanonicalPath();
278 }
279catch (java.io.IOException e)
280 {
281 logger.error("Exception getting file path: ", e);
282 }
283// notify that eviction will soon take place284for (Iterator lit = this.listeners.iterator(); lit.hasNext(); )
285 {
286FileCacheEventListener listener =
287 (FileCacheEventListener) lit.next();
288 listener.evict(entry);
289 }
290 cache.remove(key);
291292 count++;
293 }
294 }
295 }
296297/***298 * Comparator function for sorting by last accessed during eviction299 *300 */301publicint compare(Object o1, Object o2)
302 {
303FileCacheEntry e1 = (FileCacheEntry)o1;
304FileCacheEntry e2 = (FileCacheEntry)o2;
305if (e1.getLastAccessed() < e2.getLastAccessed())
306 {
307return -1;
308 }
309elseif (e1.getLastAccessed() == e2.getLastAccessed())
310 {
311return 0;
312 }
313return 1;
314 }
315316/***317 * inner class that runs as a thread to scan the cache for updates or evictions318 *319 */320protectedclass FileCacheScanner extends Thread
321 {
322privateboolean stopping = false;
323324publicvoid setStopping(boolean flag)
325 {
326this.stopping = flag;
327 }
328329/***330 * Run the file scanner thread331 *332 */333publicvoid run()
334 {
335boolean done = false;
336337try338 {
339while(!done)
340 {
341try342 {
343int count = 0;
344synchronized(FileCache.this)
345 {
346for (Iterator it = FileCache.this.cache.values().iterator(); it.hasNext(); )
347 {
348 FileCacheEntry entry = (FileCacheEntry) it.next();
349 Date modified = new Date(entry.getFile().lastModified());
350351if (modified.after(entry.getLastModified()))
352 {
353for (Iterator lit = FileCache.this.listeners.iterator(); lit.hasNext(); )
354 {
355 FileCacheEventListener listener =
356 (FileCacheEventListener) lit.next();
357 listener.refresh(entry);
358 entry.setLastModified(modified);
359 }
360 }
361 count++;
362 }
363 }
364if (count > FileCache.this.getMaxSize())
365 {
366 FileCache.this.evict();
367 }
368 }
369catch (Exception e)
370 {
371 logger.error("FileCache Scanner: Error in iteration...", e);
372 }
373374 sleep(FileCache.this.getScanRate() * 1000);
375376if (this.stopping)
377 {
378this.stopping = false;
379 done = true;
380 }
381 }
382 }
383catch (InterruptedException e)
384 {
385 logger.error("FileCacheScanner: recieved interruption, exiting.", e);
386 }
387 }
388 } // end inner class: FileCacheScanner389390391/***392 * get an iterator over the cache values393 *394 * @return iterator over the cache values395 */396public Iterator getIterator()
397 {
398return cache.values().iterator();
399 }
400401/***402 * get the size of the cache403 *404 * @return the size of the cache405 */406publicint getSize()
407 {
408return cache.size();
409 }
410 }
411