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.StringTokenizer; 019 import java.io.ByteArrayOutputStream; 020 import java.io.ObjectOutputStream; 021 import java.io.ObjectInputStream; 022 import java.io.FileOutputStream; 023 import java.io.FileInputStream; 024 import java.io.BufferedInputStream; 025 import java.io.BufferedOutputStream; 026 import java.io.Serializable; 027 import java.io.IOException; 028 import java.io.File; 029 030 /** 031 * tk. 032 * @version $Id: ShareableFileStash.java 155435 2005-02-26 13:17:27Z dirkv $ 033 * @author Rodney Waldhoff 034 */ 035 public class ShareableFileStash extends BaseStash implements Stash { 036 protected int _numDirectories = 10; 037 protected File _rootdir = null; 038 protected int _maxFilenameLength = 128; 039 040 /** 041 * @param root the root directory to store objects in 042 * @param numdirs the number of directories to create under root (must be >0) 043 */ 044 public ShareableFileStash(String root, int numdirs) { 045 _rootdir = new File(root); 046 _numDirectories = numdirs; 047 } 048 049 /** 050 * @param root the root directory to store objects in 051 * @param numdirs the number of directories to create under root (must be >0) 052 */ 053 public ShareableFileStash(File root, int numdirs) { 054 _rootdir = root; 055 _numDirectories = numdirs; 056 } 057 058 /** 059 * @param root the root directory to store objects in 060 * @param numdirs the number of directories to create under root (must be >0) 061 * @param maxfilenamelength the maximum length filename to create 062 */ 063 public ShareableFileStash(File root, int numdirs, int maxfilenamelength) { 064 _rootdir = root; 065 _numDirectories = numdirs; 066 _maxFilenameLength = maxfilenamelength; 067 } 068 069 /** 070 * @param root the root directory to store objects in 071 * @param numdirs the number of directories to create under root (must be >0) 072 * @param maxfilenamelength the maximum length filename to create 073 */ 074 public ShareableFileStash(String root, int numdirs, int maxfilenamelength) { 075 _rootdir = new File(root); 076 _numDirectories = numdirs; 077 _maxFilenameLength = maxfilenamelength; 078 } 079 080 protected byte[] getSerializedForm(Serializable val) { 081 byte[] serform = null; 082 if(null == val) { 083 return null; 084 } 085 ByteArrayOutputStream byout = null; 086 ObjectOutputStream out = null; 087 try { 088 byout = new ByteArrayOutputStream(); 089 out = new ObjectOutputStream(byout); 090 out.writeObject(val); 091 out.flush(); 092 serform = byout.toByteArray(); 093 } catch(IOException e) { 094 serform = null; 095 } finally { 096 try { 097 byout.close(); 098 } catch(Exception e) { 099 } 100 try { 101 out.close(); 102 } catch(Exception e) { 103 } 104 } 105 return serform; 106 } 107 108 protected synchronized Serializable readFromFile(File file) { 109 ObjectInputStream oin = null; 110 try { 111 oin = new ObjectInputStream(new BufferedInputStream(new FileInputStream(file))); 112 return(Serializable)(oin.readObject()); 113 } catch(Exception e) { 114 return null; 115 } finally { 116 try { 117 oin.close(); 118 } catch(Exception e) { 119 } 120 } 121 } 122 123 protected File getFile(Serializable key, boolean mkdirs) { 124 String keystr = key.toString(); 125 char[] chars = keystr.toCharArray(); 126 StringBuffer buf = new StringBuffer(); 127 File parent = new File(_rootdir,String.valueOf(Math.abs(keystr.hashCode()%_numDirectories))); 128 for(int i=0;i<chars.length;i++) { 129 if((chars[i] >= 'a' && chars[i] <= 'z') || 130 (chars[i] >= 'A' && chars[i] <= 'Z') || 131 (chars[i] >= '0' && chars[i] <= '9')) { 132 if(buf.length() >= _maxFilenameLength) { 133 buf.append("_"); 134 parent = new File(parent,buf.toString()); 135 buf.setLength(0); 136 } 137 buf.append(chars[i]); 138 } else { 139 if(buf.length() + 5 >= _maxFilenameLength) { 140 buf.append("_"); 141 parent = new File(parent,buf.toString()); 142 buf.setLength(0); 143 } 144 buf.append("_"); 145 buf.append(Integer.toHexString((int)chars[i])); 146 } 147 } 148 if(buf.length() == 0) { 149 buf.append("_"); 150 } 151 if(mkdirs) { 152 parent.mkdirs(); 153 } 154 return new File(parent,buf.toString()); 155 } 156 157 public int canStore(Serializable key, Serializable val, Long expiresAt, Long cost, Serializable group, byte[] serialized) { 158 if(null == serialized) { 159 serialized = getSerializedForm(val); 160 } 161 if(null == serialized) { 162 return Stash.NO; 163 } else { 164 return Stash.YES; 165 } 166 } 167 168 public boolean store(Serializable key, Serializable val, Long expiresAt, Long cost, Serializable group, byte[] serialized) { 169 if(null == serialized) { 170 serialized = getSerializedForm(val); 171 } 172 if(null == serialized) { 173 return false; 174 } 175 176 File cachefile = getFile(key,true); 177 if(null == cachefile) { 178 return false; 179 } 180 181 BufferedOutputStream fout = null; 182 try { 183 fout = new BufferedOutputStream(new FileOutputStream(cachefile)); 184 fout.write(serialized); 185 fout.flush(); 186 } catch(Exception e) { 187 try { 188 fout.close(); 189 } catch(Exception ex) { 190 } 191 fout = null; 192 try { 193 cachefile.delete(); 194 } catch(Exception ex) { 195 } 196 return false; 197 } finally { 198 try { 199 fout.close(); 200 } catch(Exception e) { 201 } 202 } 203 return true; 204 } 205 206 public Serializable retrieve(Serializable key) { 207 File cachefile = getFile(key,false); 208 if(cachefile.exists()) { 209 return readFromFile(cachefile); 210 } else { 211 return null; 212 } 213 } 214 215 public boolean contains(Serializable key) { 216 File cachefile = getFile(key,false); 217 return cachefile.exists(); 218 } 219 220 public float capacity() { 221 return 0; 222 } 223 224 public void clear(Serializable key) { 225 File cachefile = getFile(key,false); 226 cachefile.delete(); 227 } 228 229 public synchronized void clear() { 230 File f = getMoveToLoc(); 231 try { 232 f.getParentFile().mkdirs(); 233 } catch(NullPointerException e) { 234 // ignored 235 } 236 _rootdir.renameTo(f); 237 _rootdir.mkdirs(); 238 Thread t = new Thread(new RecursiveFileDeleter(f)); 239 t.start(); 240 } 241 242 public boolean wantsSerializedForm() { 243 return true; 244 } 245 246 public void unsetCache() { 247 } 248 249 public void setCache(Cache c) { 250 } 251 252 protected File getMoveToLoc() { 253 File parent = _rootdir.getAbsoluteFile().getParentFile(); 254 for(int i=0;i<100;i++) { 255 String fname = String.valueOf(System.currentTimeMillis() % 100000000L); 256 File f = new File(parent,fname); 257 if(!f.exists()) { 258 return f; 259 } 260 try { 261 Thread.currentThread().sleep(17); 262 } catch(Exception e) { 263 // ignored 264 } 265 } 266 throw new RuntimeException("Couldn't create a temp file to move the shareable file stash to."); 267 } 268 } 269 270 class RecursiveFileDeleter implements Runnable { 271 private File _root = null; 272 273 public RecursiveFileDeleter(File root) { 274 _root = root; 275 } 276 277 public void run() { 278 boolean success = recursiveDelete(_root); 279 if(!success) { 280 System.err.println("Unable to fully delete the file at \"" + _root + "\". Please delete it manually."); 281 } 282 } 283 284 boolean recursiveDelete(File f) { 285 if(f.isDirectory()) { 286 File[] files = f.listFiles(); 287 for(int i=0;i<files.length;i++) { 288 if(files[i].isFile()) { 289 if(!files[i].delete()) { 290 return false; 291 } 292 } else { 293 if(!recursiveDelete(files[i])) { 294 return false; 295 } 296 } 297 } 298 } 299 return f.delete(); 300 } 301 } 302 303 304