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