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