KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > caucho > vfs > CachePath


1 /*
2  * Copyright (c) 1998-2006 Caucho Technology -- all rights reserved
3  *
4  * This file is part of Resin(R) Open Source
5  *
6  * Each copy or derived work must preserve the copyright notice and this
7  * notice unmodified.
8  *
9  * Resin Open Source is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation; either version 2 of the License, or
12  * (at your option) any later version.
13  *
14  * Resin Open Source is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, or any warranty
17  * of NON-INFRINGEMENT. See the GNU General Public License for more
18  * details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with Resin Open Source; if not, write to the
22  * Free SoftwareFoundation, Inc.
23  * 59 Temple Place, Suite 330
24  * Boston, MA 02111-1307 USA
25  *
26  * @author Scott Ferguson
27  */

28
29 package com.caucho.vfs;
30
31 import com.caucho.util.CacheListener;
32 import com.caucho.util.LruCache;
33
34 import java.io.IOException JavaDoc;
35 import java.io.OutputStream JavaDoc;
36 import java.util.Map JavaDoc;
37
38 /**
39  * A cache wrapper over a path. All files are automatically cached.
40  * Once the cache is created, you use the file just like any other Path:
41  *
42  * <code><pre>
43  * Path root = new CachePath(Vfs.lookup("test/cacheable"));
44  *
45  * ReadStream is = root.lookup("test.file").openRead();
46  * ...
47  * is.close();
48  * </pre></code>
49  *
50  * <p>The cache size is governed by two limits: memory size and number of
51  * entries. The size of the disk is considered infinite, and it limited
52  * only by the number of entries.
53  *
54  * <p>The cache entries are stored in an LRU with a capacity of the number
55  * of entries. If loading a new file pushes the memory size over the limit,
56  * the old files are dropped from the memory cache.
57  */

58 public class CachePath extends FilesystemPath {
59   private final static int BLOCK_SIZE = 1024;
60   
61   private Path _backingRoot;
62   private LruCache<String JavaDoc,Cache> _cache;
63   private long _maxSize;
64   private long _maxEntrySize;
65   private long _size;
66   
67   private Path _file;
68   private Cache _item;
69   private boolean _removeOnRelease;
70
71   // statistics
72
private long _readTotalCount;
73   private long _readHitCount;
74
75   /**
76    * Creates a new cache.
77    *
78    * @param root underlying backing store.
79    * @param entries the number of entries in the cache.
80    * @param capacity total size in bytes allowed in the memory cache.
81    */

82   public CachePath(Path root, int entries, long capacity)
83   {
84     super(null, "/", "/");
85     _root = this;
86
87     _backingRoot = root.createRoot();
88     _cache = new LruCache<String JavaDoc,Cache>(entries);
89     _maxSize = capacity;
90     _maxEntrySize = _maxSize / 64;
91     _maxEntrySize += BLOCK_SIZE - 1;
92     _maxEntrySize -= _maxEntrySize % BLOCK_SIZE;
93     
94     if (_maxEntrySize > 1024 * 1024)
95       _maxEntrySize = 1024 * 1024;
96
97     clear();
98   }
99
100   /**
101    * Internal creation of a path for the vfs.
102    *
103    * @param root the root path (i.e. the cache root)
104    * @param userPath the path the application used in the lookup
105    * @param path canonical path
106    */

107   protected CachePath(FilesystemPath root, String JavaDoc userPath, String JavaDoc path)
108   {
109     super(root, userPath, path);
110   }
111
112   /**
113    * If set, files bumped from the cache are removed from the underlying
114    * filesystem.
115    *
116    * @param remove if true, remove the underlying file on a release.
117    */

118   public void setRemoveOnRelease(boolean remove)
119   {
120     ((CachePath) _root)._removeOnRelease = remove;
121   }
122
123   /**
124    * Looks up the cache entry for a given path.
125    *
126    * @param userPath the string given by the user in lookup()
127    * @param attributes the user's initialization attributes
128    * @param path the canonical path name
129    *
130    * @return a cache path entry
131    */

132   public Path fsWalk(String JavaDoc userPath,
133             Map JavaDoc<String JavaDoc,Object JavaDoc> attributes,
134             String JavaDoc path)
135   {
136     return new CachePath(_root, userPath, path);
137   }
138
139   /**
140    * The scheme for the path is cache:
141    */

142   public String JavaDoc getScheme()
143   {
144     return "cache";
145   }
146
147   /**
148    * Clear the memory cache.
149    */

150   public void clear()
151   {
152     CachePath cRoot = (CachePath) _root;
153     
154     synchronized (cRoot) {
155       cRoot._cache.clear();
156       cRoot._size = 0;
157     }
158   }
159
160   /**
161    * Returns true if the underlying file exists.
162    */

163   public boolean exists()
164   {
165     return getFile().exists();
166   }
167
168   public boolean isDirectory()
169   {
170     return getFile().isDirectory();
171   }
172
173   public boolean isFile()
174   {
175     return getFile().isFile();
176   }
177
178   public long getLength()
179   {
180     return getFile().getLength();
181   }
182
183   public long getLastModified()
184   {
185     return getFile().getLastModified();
186   }
187
188   public void setLastModified(long time)
189   {
190     getFile().setLastModified(time);
191   }
192
193   public boolean canRead()
194   {
195     return getFile().canRead();
196   }
197
198   public boolean canWrite()
199   {
200     return getFile().canWrite();
201   }
202   
203   public String JavaDoc []list() throws IOException JavaDoc
204   {
205     return getFile().list();
206   }
207   
208   public boolean mkdir()
209     throws IOException JavaDoc
210   {
211     return getFile().mkdir();
212   }
213   
214   public boolean mkdirs()
215     throws IOException JavaDoc
216   {
217     return getFile().mkdirs();
218   }
219   
220   public boolean remove()
221     throws IOException JavaDoc
222   {
223     if (getFile().remove()) {
224       CachePath root = (CachePath) _root;
225       root._cache.remove(getPath());
226
227       return true;
228     }
229     else
230       return false;
231   }
232   
233   public boolean renameTo(Path path)
234     throws IOException JavaDoc
235   {
236     if (getFile().renameTo(path)) {
237       CachePath root = (CachePath) _root;
238       root._cache.remove(getPath());
239
240       return true;
241     }
242     else
243       return false;
244   }
245
246   /**
247    * Write the contents of the cache item to the output stream.
248    *
249    * @param os destination output stream.
250    */

251   public void writeToStream(OutputStream JavaDoc os) throws IOException JavaDoc
252   {
253     CachePath root = (CachePath) _root;
254     LruCache<String JavaDoc,Cache> cache = root._cache;
255
256     String JavaDoc path = getPath();
257     
258     if (_item == null)
259       _item = cache.get(path);
260
261     if (_item != null) {
262       TempBuffer head = _item.getHead();
263
264       if (head != null) {
265         for (; head != null; head = head.getNext())
266           os.write(head.getBuffer(), 0, head.getLength());
267         return;
268       }
269     }
270
271     Path file = getFile();
272     long length = file.getLength();
273
274     if (length <= root._maxEntrySize && length > 0) {
275       ReadStream is = file.openRead();
276       TempBuffer head;
277
278       try {
279         head = copyFromStream(is, os, length);
280       } finally {
281         is.close();
282       }
283
284       _item = new Cache(root, file, head);
285
286       synchronized (root) {
287         long size = _item.getSize();
288         root._size += _item.getSize();
289         cache.put(path, _item);
290       }
291
292       int i = 16;
293       while (root._maxSize < root._size && i-- > 0) {
294         root._cache.removeTail();
295       }
296     }
297     else
298       getFile().writeToStream(os);
299   }
300
301   /**
302    * Opens a read stream to the path. When the file is in the memory
303    * cache, the read stream returns the memory. Otherwise it reads from
304    * the backing file.
305    */

306   public StreamImpl openReadImpl() throws IOException JavaDoc
307   {
308     CachePath root = (CachePath) _root;
309     LruCache<String JavaDoc,Cache> cache = root._cache;
310
311     String JavaDoc path = getPath();
312     Cache item = cache.get(path);
313     
314     root._readTotalCount++;
315     if (item != null) {
316       root._readHitCount++;
317       TempReadStream rs = new TempReadStream(item.getHead());
318       rs.setFreeWhenDone(false);
319       return rs;
320     }
321     
322     Path file = getFile();
323     long length = file.getLength();
324
325     if (length <= root._maxEntrySize && length > 0) {
326       try {
327         ReadStream is = file.openRead();
328     TempBuffer head = null;
329     try {
330       head = copyFromStream(is, null, length);
331     } finally {
332       is.close();
333     }
334
335         item = new Cache((CachePath) root, file, head);
336       
337         synchronized (root) {
338           root._size += item.getSize();
339           root._cache.put(path, item);
340         }
341       
342         while (root._size > root._maxSize) {
343           root._cache.removeTail();
344         }
345
346         TempReadStream rs = new TempReadStream(head);
347         rs.setFreeWhenDone(false);
348         return rs;
349       } catch (IOException JavaDoc e) {
350       }
351     }
352     
353     return file.openReadImpl();
354   }
355
356   private TempBuffer copyFromStream(ReadStream is, OutputStream JavaDoc os,
357                                     long length)
358     throws IOException JavaDoc
359   {
360     TempBuffer head = new TempBuffer(BLOCK_SIZE);
361     TempBuffer tail = head;
362     int len;
363
364     while ((len = is.readAll(tail.getBuffer(), 0, tail.getCapacity())) > 0) {
365       length -= len;
366
367       tail.setLength(len);
368       if (os != null)
369         os.write(tail.getBuffer(), 0, len);
370
371       if (length > 0) {
372         tail.setNext(new TempBuffer(BLOCK_SIZE));
373         tail = tail.getNext();
374       }
375       else
376         break;
377     }
378
379     return head;
380   }
381
382   public StreamImpl openWriteImpl() throws IOException JavaDoc
383   {
384     String JavaDoc path = getPath();
385     LruCache<String JavaDoc,Cache> cache = ((CachePath) _root)._cache;
386     cache.remove(path);
387     return getFile().openWriteImpl();
388   }
389
390   public StreamImpl openAppendImpl() throws IOException JavaDoc
391   {
392     return getFile().openAppendImpl();
393   }
394
395   public StreamImpl openReadWriteImpl() throws IOException JavaDoc
396   {
397     return getFile().openReadWriteImpl();
398   }
399
400   /**
401    * Returns the hash code, which is based on the underlying file.
402    */

403   public int hashCode()
404   {
405     return getFile().hashCode();
406   }
407
408   public boolean equals(Object JavaDoc b)
409   {
410     if (b instanceof CachePath) {
411       CachePath test = (CachePath) b;
412
413       return getFile().equals(test.getFile());
414     }
415     else
416       return getFile().equals(b);
417   }
418
419   /**
420    * Returns the underlying file.
421    */

422   private Path getFile()
423   {
424     if (_file == null)
425       _file = ((CachePath) _root)._backingRoot.lookup(getPath());
426     
427     return _file;
428   }
429
430   static class Cache implements CacheListener {
431     private CachePath _root;
432     private Path _path;
433     private TempBuffer _head;
434     private long _size;
435
436     Cache(CachePath root, Path path, TempBuffer head)
437     {
438       _root = root;
439       _path = path;
440       _head = head;
441       
442       for (TempBuffer ptr = head; ptr != null; ptr = ptr.getNext())
443     _size += ptr.getCapacity();
444     }
445
446     TempBuffer getHead()
447     {
448       return _head;
449     }
450
451     long getSize()
452     {
453       return _size;
454     }
455
456     public void removeEvent()
457     {
458       synchronized (_root) {
459     _root._size -= _size;
460         _size = 0;
461       }
462
463       // Don't free because the entry might be in use
464
_head = null;
465
466       if (_root._removeOnRelease) {
467         try {
468           _path.remove();
469         } catch (IOException JavaDoc e) {
470         }
471       }
472     }
473   }
474 }
475
Popular Tags