KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > info > magnolia > cms > cache > simple > CacheImpl


1 package info.magnolia.cms.cache.simple;
2
3 import info.magnolia.cms.cache.Cache;
4 import info.magnolia.cms.cache.CacheConfig;
5 import info.magnolia.cms.cache.CacheKey;
6 import info.magnolia.cms.cache.CacheableEntry;
7 import info.magnolia.cms.core.Path;
8
9 import java.io.File JavaDoc;
10 import java.io.FileInputStream JavaDoc;
11 import java.io.FileOutputStream JavaDoc;
12 import java.io.IOException JavaDoc;
13 import java.io.OutputStream JavaDoc;
14 import java.util.Date JavaDoc;
15 import java.util.Hashtable JavaDoc;
16 import java.util.Map JavaDoc;
17 import java.util.zip.GZIPOutputStream JavaDoc;
18
19 import javax.servlet.http.HttpServletResponse JavaDoc;
20
21 import org.apache.commons.io.FileUtils;
22 import org.apache.commons.io.IOUtils;
23 import org.apache.commons.lang.StringUtils;
24 import org.slf4j.Logger;
25 import org.slf4j.LoggerFactory;
26
27
28 /**
29  * A <code>Cache</code> implementation based on the Magnolia 2.x filesystem cache.
30  * @author Andreas Brenk
31  * @author Fabrizio Giustina
32  * @author Sameer Charles
33  * @since 3.0
34  * $Id:CacheImpl.java 6314 2006-09-11 08:24:51Z scharles $
35  */

36 public class CacheImpl implements Cache {
37
38     private static final Logger log = LoggerFactory.getLogger(CacheImpl.class);
39
40     /**
41      * Cached items: the key is the URI of the cached request and the entry is a Cache instance
42      */

43     private Map JavaDoc cachedURIList = new Hashtable JavaDoc();
44
45     /**
46      * This is used to syncronize between cache flushing and creating
47      * We cannot use syncronized blocks here because it will block other threads even to cache simultaniusly
48      * */

49     private static boolean currentlyRemoving;
50
51     private CacheConfig config;
52
53     /**
54      * Cache this request in default and optimized stores
55      * @param key
56      * @param entry
57      * @param canCompress
58      */

59     public void cacheRequest(CacheKey key, CacheableEntry entry, boolean canCompress) {
60         // This implementation flushes entire cache, its safe to simply check on one flag, for other
61
// implementations we need to check and synchronize on CacheKey
62
if (currentlyRemoving) {
63             return; // simply ignore this cache request
64
}
65         int compressedSize = 0;
66         File JavaDoc file = getFile(key, false);
67
68         if (file.isDirectory()) {
69             // additional check, if file name is something like "/"
70
return;
71         }
72
73         // it should not cache again if the resource already existing
74
// its a responsibility of a cache manager to call remove or flush if resource needs to be
75
// invalidated
76
if (!file.exists()) {
77             FileOutputStream JavaDoc out = null;
78             if (log.isDebugEnabled()) {
79                 log.debug("creating file {}", file.getAbsolutePath());
80             }
81             try {
82                 file.getParentFile().mkdirs();
83                 file.createNewFile();
84
85                 out = new FileOutputStream JavaDoc(file);
86                 out.write(entry.getOut());
87                 out.flush();
88             } catch (Exception JavaDoc e) {
89                 log.error("Failed to cache "+key.toString(), e);
90             } finally {
91                 IOUtils.closeQuietly(out);
92             }
93         }
94
95         if (canCompress) {
96             File JavaDoc gzipFile = getFile(key, true);
97             if (!gzipFile.exists()) {
98                 GZIPOutputStream JavaDoc gzipOut = null;
99                 try {
100                     gzipFile.getParentFile().mkdirs();
101                     gzipFile.createNewFile();
102                     FileOutputStream JavaDoc out = new FileOutputStream JavaDoc(gzipFile);
103                     gzipOut = new GZIPOutputStream JavaDoc(out);
104                     gzipOut.write(entry.getOut());
105                     gzipOut.flush();
106                 } catch (Exception JavaDoc e) {
107                     log.error("Failed to create compressed entry for "+key.toString(), e);
108                 } finally {
109                     IOUtils.closeQuietly(gzipOut);
110                 }
111             }
112
113             compressedSize = (new Long JavaDoc(gzipFile.length())).intValue();
114         }
115
116         addToCachedURIList(key, new Date JavaDoc().getTime(), (int) file.length(), compressedSize);
117     }
118
119     public synchronized void flush() {
120         try {
121             File JavaDoc cacheDir = getCacheDirectory();
122             currentlyRemoving = true;
123             if (cacheDir.exists() && cacheDir.isDirectory()) {
124                 FileUtils.deleteDirectory(cacheDir);
125             }
126
127             // this will create cache start directory again
128
cacheDir.mkdirs();
129
130             // clear in-memory cache also
131
clearCachedURIList();
132         }
133         catch (Throwable JavaDoc t) {
134             log.error(t.getMessage(), t);
135         } finally {
136             currentlyRemoving = false;
137         }
138     }
139
140     public long getCreationTime(CacheKey key) {
141         CachedItem item = (CachedItem) this.cachedURIList.get(key);
142         if (item == null) {
143             return -1;
144         }
145
146         return item.time;
147     }
148
149     public void start(CacheConfig config) {
150         this.config = config;
151
152         File JavaDoc cacheDir = getCacheDirectory();
153
154         if (!cacheDir.exists()) {
155             boolean result = cacheDir.mkdirs();
156             if (!result) {
157                 log.error("Failed to create cache directory location {}", cacheDir.getAbsolutePath());
158             }
159         } else {
160             updateInMemoryCache(cacheDir);
161         }
162     }
163
164     /**
165      * synchronize file system with in-memory cache list
166      * @param cacheDir
167      * */

168     private void updateInMemoryCache(File JavaDoc cacheDir) {
169         File JavaDoc[] items = cacheDir.listFiles();
170         for (int index=0; index < items.length; index++) {
171             File JavaDoc item = items[index];
172             if (item.isDirectory()) {
173                 updateInMemoryCache(item);
174             } else {
175                 if (item.getName().lastIndexOf("gzip") < 0) {
176                     // use this as key
177
String JavaDoc cacheHome = getCacheDirectory().getPath();
178                     CacheKey key = new CacheKey(StringUtils.substringAfter(item.getPath(), cacheHome));
179                     int size = (int) item.length();
180                     File JavaDoc compressedFile = new File JavaDoc(item.getPath()+".gzip");
181                     int compressedSize = -1;
182                     if (compressedFile.exists()) compressedSize = (int) compressedFile.length();
183                     addToCachedURIList(key, item.lastModified(), size, compressedSize);
184                 }
185             }
186         }
187     }
188
189
190     public boolean isCached(CacheKey key) {
191         return this.cachedURIList.get(key) != null;
192     }
193
194     public void stop() {
195         // NOTE: it should not flush the cache here. otherwise on server stop all filesystem
196
// cache is removed, only clean in-memory cache
197
clearCachedURIList();
198     }
199
200     /**
201      * Spools cached data back to the client. This only works if specified request is a GET request and does not have
202      * any request parameter, else it wont write anything on the output stream.
203      * @param key HttpServletRequest
204      * @param response HttpServletResponse
205      * @return <code>true</code> is successful
206      */

207     public boolean streamFromCache(CacheKey key, HttpServletResponse JavaDoc response, boolean canCompress) {
208
209         FileInputStream JavaDoc fin = null;
210         OutputStream JavaDoc out = null;
211         try {
212             File JavaDoc file = getFile(key, canCompress);
213
214             if (!file.exists() || file.isDirectory() || file.length() < 4) {
215                 return false;
216             }
217
218             if (log.isDebugEnabled()) {
219                 log.debug("Streaming from cache the file: {}", file.getAbsolutePath()); // $NON-NLS-1$
220
}
221
222             fin = new FileInputStream JavaDoc(file);
223             out = response.getOutputStream();
224             response.setDateHeader("Last-Modified", this.getCreationTime(key));
225             if (canCompress) {
226                 response.setContentLength(getCompressedSize(key));
227                 response.setHeader("Content-Encoding", "gzip");
228                 IOUtils.copy(fin, out);
229             }
230             else {
231                 response.setContentLength(getSize(key));
232                 IOUtils.copy(fin, out);
233             }
234             out.flush();
235         }
236         catch (IOException JavaDoc e) {
237             log.error("Error while reading cache for: '" + key + "'.", e);
238             return false;
239         }
240         finally {
241             IOUtils.closeQuietly(out);
242             IOUtils.closeQuietly(fin);
243         }
244
245         return true;
246     }
247
248     /**
249      * @param lastModified last modification time (ms from 1970)
250      * @param size original size
251      * @param compressedSize compressed size
252      * @param lastModified
253      */

254     private void addToCachedURIList(CacheKey key, long lastModified, int size, int compressedSize) {
255         CachedItem entry = new CachedItem(lastModified, size, compressedSize);
256
257         if (log.isDebugEnabled()) {
258             log.debug("Caching URI [{}]", key); // $NON-NLS-1$
259
}
260
261         this.cachedURIList.put(key, entry);
262     }
263
264     private void clearCachedURIList() {
265         this.cachedURIList.clear();
266     }
267
268     /**
269      * Empties the cache for the specified resource. Currenty it expects the entire path, including cache location.
270      * This is never used for simple cache since there are no way to find out how to flush related items
271      */

272     public void remove(CacheKey key) {
273         File JavaDoc file = this.getFile(key, false);
274         try {
275             if (file.isDirectory()) {
276                 FileUtils.deleteDirectory(file);
277                 clearCachedURIList();
278             }
279             else {
280                 if (log.isDebugEnabled()) {
281                     log.debug("Flushing {}", file.getPath()); // $NON-NLS-1$
282
}
283
284                 file.delete();
285                 removeFromCachedURIList(key);
286             }
287         }
288         catch (Exception JavaDoc e) {
289             log.error("Failed to flush [" + file.getPath() + "]: " + e.getMessage(), e); // $NON-NLS-1$ //$NON-NLS-2$
290
}
291     }
292
293     /**
294      * @return size as on disk
295      */

296     private int getCompressedSize(CacheKey key) {
297         CachedItem item = (CachedItem) this.cachedURIList.get(key);
298         if (item == null) {
299             return -1;
300         }
301
302         return item.compressedSize;
303     }
304
305     private File JavaDoc getFile(CacheKey key, boolean compressed) {
306
307         String JavaDoc fileName = key.toString();
308         File JavaDoc cacheFile;
309         if (compressed) {
310             cacheFile = new File JavaDoc(getCacheDirectory(), fileName + ".gzip");
311         }
312         else {
313             cacheFile = new File JavaDoc(getCacheDirectory(), fileName);
314         }
315         return cacheFile;
316     }
317
318     /**
319      * @return size as on disk
320      */

321     private int getSize(CacheKey key) {
322         CachedItem item = (CachedItem) this.cachedURIList.get(key);
323         if (item == null) {
324             return -1;
325         }
326
327         return item.size;
328     }
329
330     /**
331      * @param key
332      */

333     private void removeFromCachedURIList(CacheKey key) {
334         this.cachedURIList.remove(key);
335     }
336
337     private File JavaDoc getCacheDirectory() {
338
339         // add a fixed "mgnl-cache" subdir: the cache dir is removed on flush and if the user choose an existing
340
// and not empty dir we should be sure to not remove everything!
341
return new File JavaDoc(Path.getCacheDirectory(), "mgnl-cache");
342     }
343
344     private static class CachedItem {
345
346         /**
347          * Compressed size.
348          */

349         protected int compressedSize;
350
351         /**
352          * Original size.
353          */

354         protected int size;
355
356         /**
357          * Time in milliseconds.
358          */

359         protected long time;
360
361         public CachedItem(long time, int size, int compressedSize) {
362             this.time = time;
363             this.size = size;
364             this.compressedSize = compressedSize;
365         }
366     }
367 }
368
Popular Tags