1
2
3
4
5
6
7
8
9
10
11
12
13
14
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
35
36
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
275 }
276 try {
277 File f = (File)(oldcachedobj.getKey());
278 f.delete();
279 } catch(Exception ex) {
280 ex.printStackTrace();
281
282 }
283 }
284 }
285 _curBytes += serialized.length;
286 return true;
287 }
288
289 public Serializable retrieve(Serializable key) {
290
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
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 }