001 /* 002 * Copyright 2001-2004 The Apache Software Foundation 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016 package org.apache.commons.cache; 017 018 import java.util.HashMap; 019 import java.util.Iterator; 020 import java.util.ArrayList; 021 import java.util.StringTokenizer; 022 import java.io.ByteArrayOutputStream; 023 import java.io.ObjectOutputStream; 024 import java.io.ObjectInputStream; 025 import java.io.FileOutputStream; 026 import java.io.FileInputStream; 027 import java.io.BufferedInputStream; 028 import java.io.BufferedOutputStream; 029 import java.io.Serializable; 030 import java.io.IOException; 031 import java.io.File; 032 033 /** 034 * tk. 035 * @version $Id: FileStash.java 155435 2005-02-26 13:17:27Z dirkv $ 036 * @author Rodney Waldhoff 037 */ 038 public class FileStash extends BaseStash implements Stash { 039 public transient static final long DEFAULT_MAX_OBJS; 040 static { 041 int defaultMaxObjs = -1; 042 try { 043 defaultMaxObjs = Integer.parseInt(System.getProperty("org.apache.commons.cache.FileStash.max-objs","-1")); 044 } catch(Exception e) { 045 defaultMaxObjs = -1; 046 } 047 DEFAULT_MAX_OBJS = defaultMaxObjs; 048 } 049 050 public transient static final long DEFAULT_MAX_BYTES; 051 static { 052 int defaultMaxBytes = -1; 053 try { 054 defaultMaxBytes = Integer.parseInt(System.getProperty("org.apache.commons.cache.FileStash.max-bytes","-1")); 055 } catch(Exception e) { 056 defaultMaxBytes = -1; 057 } 058 DEFAULT_MAX_BYTES = defaultMaxBytes; 059 } 060 061 public transient static final File[] DEFAULT_TEMP_DIRS; 062 static { 063 ArrayList v = new ArrayList(); 064 String rootdirstr = System.getProperty("org.apache.commons.cache.FileStash.root-cache-dir"); 065 File rootdir = null; 066 if(null == rootdir) { 067 DEFAULT_TEMP_DIRS = null; 068 } else { 069 rootdir = new File(rootdirstr); 070 int branchingFactor = 10; 071 try { 072 branchingFactor = Integer.parseInt(System.getProperty("org.apache.commons.cache.FileStash.num-cache-dirs","10")); 073 } catch(Exception e) { 074 branchingFactor = 10; 075 } 076 if(branchingFactor < 0) { 077 branchingFactor = 10; 078 } else if(branchingFactor==0) { 079 v.add(rootdir); 080 } else { 081 for(int i=0;i<branchingFactor;i++) { 082 v.add(new File(rootdir,String.valueOf(i))); 083 } 084 } 085 DEFAULT_TEMP_DIRS = (File[])(v.toArray(new File[0])); 086 } 087 } 088 089 public transient static final String DEFAULT_FILE_PREFIX; 090 static { 091 DEFAULT_FILE_PREFIX = System.getProperty("org.apache.commons.cached.FileStash.default-file-prefix","cache"); 092 } 093 094 095 public transient static final String DEFAULT_FILE_SUFFIX; 096 static { 097 DEFAULT_FILE_SUFFIX = System.getProperty("org.apache.commons.cache.FileStash.default-file-suffix",".ser"); 098 } 099 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 }