View Javadoc

1   /*
2    * Copyright 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 at
7    *
8    *     http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * 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 and
14   * limitations under the License.
15   */
16  package org.apache.commons.cache;
17  
18  import java.util.HashMap;
19  import java.util.Iterator;
20  import java.util.ArrayList;
21  import java.util.StringTokenizer;
22  import java.io.ByteArrayOutputStream;
23  import java.io.ObjectOutputStream;
24  import java.io.ObjectInputStream;
25  import java.io.FileOutputStream;
26  import java.io.FileInputStream;
27  import java.io.BufferedInputStream;
28  import java.io.BufferedOutputStream;
29  import java.io.Serializable;
30  import java.io.IOException;
31  import java.io.File;
32  
33  /**
34   * tk.
35   * @version $Id: FileStash.java 155435 2005-02-26 13:17:27Z dirkv $
36   * @author Rodney Waldhoff
37   */
38  public class FileStash extends BaseStash implements Stash {
39    public transient static final long DEFAULT_MAX_OBJS;
40    static {
41      int defaultMaxObjs = -1;
42      try {
43        defaultMaxObjs = Integer.parseInt(System.getProperty("org.apache.commons.cache.FileStash.max-objs","-1"));
44      } catch(Exception e) {
45        defaultMaxObjs = -1;
46      }
47      DEFAULT_MAX_OBJS = defaultMaxObjs;
48    }
49  
50    public transient static final long DEFAULT_MAX_BYTES;
51    static {
52      int defaultMaxBytes = -1;
53      try {
54        defaultMaxBytes = Integer.parseInt(System.getProperty("org.apache.commons.cache.FileStash.max-bytes","-1"));
55      } catch(Exception e) {
56        defaultMaxBytes = -1;
57      }
58      DEFAULT_MAX_BYTES = defaultMaxBytes;
59    }
60  
61    public transient static final File[] DEFAULT_TEMP_DIRS;
62    static {
63      ArrayList v = new ArrayList();
64      String rootdirstr = System.getProperty("org.apache.commons.cache.FileStash.root-cache-dir");
65      File rootdir = null;
66      if(null == rootdir) {
67        DEFAULT_TEMP_DIRS = null;
68      } else {
69        rootdir = new File(rootdirstr);
70        int branchingFactor = 10;
71        try {
72          branchingFactor = Integer.parseInt(System.getProperty("org.apache.commons.cache.FileStash.num-cache-dirs","10"));
73        } catch(Exception e) {
74          branchingFactor = 10;
75        }
76        if(branchingFactor < 0) {
77          branchingFactor = 10;
78        } else if(branchingFactor==0) {
79          v.add(rootdir);
80        } else {
81          for(int i=0;i<branchingFactor;i++) {
82            v.add(new File(rootdir,String.valueOf(i)));
83          }
84        }
85        DEFAULT_TEMP_DIRS = (File[])(v.toArray(new File[0]));
86      }
87    }
88  
89    public transient static final String DEFAULT_FILE_PREFIX;
90    static {
91      DEFAULT_FILE_PREFIX = System.getProperty("org.apache.commons.cached.FileStash.default-file-prefix","cache");
92    }
93  
94  
95    public transient static final String DEFAULT_FILE_SUFFIX;
96    static {
97      DEFAULT_FILE_SUFFIX = System.getProperty("org.apache.commons.cache.FileStash.default-file-suffix",".ser");
98    }
99  
100   protected HashMap _hash = null;
101   protected long _maxObjs = DEFAULT_MAX_OBJS;
102   protected long _maxBytes = DEFAULT_MAX_BYTES;
103   protected Cache _cache = null;
104   protected long _curBytes = 0;
105   protected File[] _tempFileDirs = null;
106   protected String _tempFilePrefix = DEFAULT_FILE_PREFIX;
107   protected String _tempFileSuffix = DEFAULT_FILE_SUFFIX;
108   protected boolean _cleanupfiles = false;
109 
110   public FileStash() {
111     this(DEFAULT_MAX_BYTES,DEFAULT_MAX_OBJS,DEFAULT_TEMP_DIRS,false);
112   }
113 
114   public FileStash(long maxbytes) {
115     this(maxbytes,DEFAULT_MAX_OBJS,DEFAULT_TEMP_DIRS,false);
116   }
117 
118   public FileStash(long maxbytes, long maxobjs) {
119     this(maxbytes,maxobjs,DEFAULT_TEMP_DIRS,false);
120   }
121 
122   public FileStash(long maxbytes, long maxobjs, File[] tempdirs, boolean cleanup) {
123     _maxObjs = maxobjs;
124     _maxBytes = maxbytes;
125     _tempFileDirs = tempdirs;
126     _hash = new HashMap();
127     _cleanupfiles = cleanup;
128   }
129 
130   public FileStash(long maxbytes, long maxobjs, String rootdir, int numdirs) {
131     this(maxbytes,maxobjs,(null == rootdir ? ((File)null) : new File(rootdir)),numdirs,false);
132   }
133 
134   public FileStash(long maxbytes, long maxobjs, String rootdir, int numdirs, boolean cleanup) {
135     this(maxbytes,maxobjs,(null == rootdir ? ((File)null) : new File(rootdir)),numdirs,cleanup);
136   }
137 
138   public FileStash(long maxbytes, long maxobjs, File rootdir, int numdirs) {
139     this(maxbytes,maxobjs,rootdir,numdirs,false);
140   }
141 
142   public FileStash(long maxbytes, long maxobjs, File rootdir, int numdirs, boolean cleanup) {
143     _maxObjs = maxobjs;
144     _maxBytes = maxbytes;
145     ArrayList v = new ArrayList();
146     if(null == rootdir) {
147       _tempFileDirs = null;
148     } else {
149       if(numdirs < 0) {
150         numdirs = 10;
151       } else if(numdirs==0) {
152         v.add(rootdir);
153       } else {
154         for(int i=0;i<numdirs;i++) {
155           v.add(new File(rootdir,String.valueOf(i)));
156         }
157       }
158       _tempFileDirs = (File[])(v.toArray(new File[0]));
159     }
160     _cleanupfiles = cleanup;
161     _hash = new HashMap();
162   }
163 
164   protected byte[] getSerializedForm(Serializable val) {
165     byte[] serform = null;
166     if(null == val) { return null; }
167     ByteArrayOutputStream byout = null;
168     ObjectOutputStream out = null;
169     try {
170       byout = new ByteArrayOutputStream();
171       out = new ObjectOutputStream(byout);
172       out.writeObject(val);
173       out.flush();
174       serform = byout.toByteArray();
175     } catch(IOException e) {
176       serform = null;
177     } finally {
178       try { byout.close(); } catch(Exception e) { }
179       try { out.close(); } catch(Exception e) { }
180     }
181     return serform;
182   }
183 
184   protected synchronized Serializable readFromFile(File file) {
185     ObjectInputStream oin = null;
186     try {
187       oin = new ObjectInputStream(new BufferedInputStream(new FileInputStream(file)));
188       return (Serializable)(oin.readObject());
189     } catch(Exception e) {
190       return null;
191     } finally {
192       try { oin.close(); } catch(Exception e) { }
193     }
194   }
195 
196   public synchronized int canStore(Serializable key, Serializable val, Long expiresAt, Long cost, Serializable group, byte[] serialized) {
197     if(null == serialized) {
198       serialized = getSerializedForm(val);
199     }
200     if(null == serialized) {
201       return Stash.NO;
202     } else if(_maxBytes != -1 && serialized.length > _maxBytes) {
203       return Stash.NO_NOT_STORABLE;
204     } else if(_maxBytes != -1 && _curBytes + serialized.length > _maxBytes) {
205       return Stash.NO_FULL;
206     } else if(_maxObjs != -1 && _hash.size() > _maxObjs) {
207       return Stash.NO_FULL;
208     } else {
209       return Stash.YES;
210     }
211   }
212 
213   protected int _tempfileCounter = 0;
214 
215   protected File getTempfile() {
216     File cachefile = null;
217     try {
218       File tempdir = null;
219       if(_tempFileDirs != null && _tempFileDirs.length > 0) {
220         tempdir = _tempFileDirs[_tempfileCounter++%_tempFileDirs.length];
221         if(!tempdir.exists()) {
222           if(_cleanupfiles) { tempdir.deleteOnExit(); }
223           tempdir.mkdirs();
224           tempdir.mkdir();
225         }
226       }
227       cachefile = File.createTempFile(_tempFilePrefix,_tempFileSuffix,tempdir);
228       if(_cleanupfiles) { cachefile.deleteOnExit(); }
229     } catch(Exception e) {
230       return null;
231     }
232     return cachefile;
233   }
234 
235   public synchronized boolean store(Serializable key, Serializable val, Long expiresAt, Long cost, Serializable group, byte[] serialized) {
236     if(null == serialized) {
237       serialized = getSerializedForm(val);
238     }
239     if(null == serialized) {
240       return false;
241     }
242 
243     File cachefile = getTempfile();
244     if(null == cachefile) {
245       return false;
246     }
247 
248     BufferedOutputStream fout = null;
249     try {
250       fout = new BufferedOutputStream(new FileOutputStream(cachefile));
251       fout.write(serialized);
252       fout.flush();
253     } catch(Exception e) {
254       try { fout.close(); } catch(Exception ex) { }
255       fout = null;
256       try { cachefile.delete(); } catch(Exception ex) { }
257       return false;
258     } finally {
259       try { fout.close(); } catch(Exception e) { }
260     }
261 
262     Object oldobj = null;
263     try {
264       oldobj = _hash.put(key,new CachedObjectInfoImpl(cachefile,expiresAt,new Long(serialized.length)));
265     } catch(Exception e) {
266       try { cachefile.delete(); } catch(Exception ex)  { }
267       return false;
268     } finally {
269       if(null != oldobj && oldobj instanceof CachedObjectInfo) {
270         CachedObjectInfo oldcachedobj = (CachedObjectInfo)oldobj;
271         try {
272           _curBytes -= oldcachedobj.getCost().longValue();
273         } catch(NullPointerException ex) {
274           // ignored
275         }
276         try {
277           File f = (File)(oldcachedobj.getKey());
278           f.delete();
279         } catch(Exception ex) {
280             ex.printStackTrace();
281           // ignored
282         }
283       }
284     }
285     _curBytes += serialized.length;
286     return true;
287   }
288 
289   public Serializable retrieve(Serializable key) {
290     // grab a lock on the cache first, since it may attempt to grab a lock on this
291     synchronized(_cache) {
292       synchronized(this) {
293         CachedObjectInfo info = (CachedObjectInfo)(_hash.get(key));
294         if(null != info) {
295           Long expiry = info.getExpirationTs();
296           if(null != expiry) {
297             if(expiry.longValue() < System.currentTimeMillis()) {
298               _cache.clear(key);
299               return null;
300             } else {
301               return readFromFile((File)(info.getKey()));
302             }
303           } else {
304             return readFromFile((File)(info.getKey()));
305           }
306         } else {
307           return null;
308         }
309       }
310     }
311   }
312 
313   public synchronized boolean contains(Serializable key) {
314     return _hash.containsKey(key);
315   }
316 
317   public synchronized float capacity() {
318     float objcount = 0;
319     if(_maxObjs > 0) {
320        objcount = (((float)_hash.size())/((float)_maxObjs));
321     }
322     float bytecount = 0;
323     if(_maxBytes > 0) {
324       bytecount = (((float)_curBytes)/((float)_maxBytes));
325     }
326     return (objcount > bytecount) ? objcount : bytecount;
327   }
328 
329   public synchronized void clear(Serializable key) {
330     CachedObjectInfo obj = (CachedObjectInfo)(_hash.remove(key));
331     if(null != obj) {
332       _curBytes -= obj.getCost().longValue();
333       ((File)(obj.getKey())).delete();
334     }
335   }
336 
337   public synchronized void clear() {
338     Iterator it = _hash.keySet().iterator();
339     while(it.hasNext()) {
340       try {
341         CachedObjectInfo obj = (CachedObjectInfo)(it.next());
342         ((File)(obj.getKey())).delete();
343       } catch(Exception e) {
344           e.printStackTrace();
345         // ignored
346       }
347     }
348     _hash.clear();
349     _curBytes = 0;
350   }
351 
352   public void setCache(Cache c) {
353     if(null != _cache) {
354       Object mutex = _cache;
355       synchronized(mutex) {
356         synchronized(this) {
357           unsetCache();
358           _cache = c;
359         }
360       }
361     } else {
362       _cache = c;
363     }
364   }
365 
366   public void unsetCache() {
367     if(null != _cache) {
368       Object mutex = _cache;
369       synchronized(mutex) {
370         clear();
371         _cache = null;
372       }
373     }
374   }
375 
376   public boolean wantsSerializedForm() {
377     return true;
378   }
379 }