View Javadoc

1   /*
2    * Copyright 2001-2004 The Apache Software Foundation
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    *     http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  package org.apache.commons.cache;
17  
18  import java.util.StringTokenizer;
19  import java.io.ByteArrayOutputStream;
20  import java.io.ObjectOutputStream;
21  import java.io.ObjectInputStream;
22  import java.io.FileOutputStream;
23  import java.io.FileInputStream;
24  import java.io.BufferedInputStream;
25  import java.io.BufferedOutputStream;
26  import java.io.Serializable;
27  import java.io.IOException;
28  import java.io.File;
29  
30  /**
31   * tk.
32   * @version $Id: ShareableFileStash.java 155435 2005-02-26 13:17:27Z dirkv $
33   * @author Rodney Waldhoff
34   */
35  public class ShareableFileStash extends BaseStash implements Stash {
36      protected int _numDirectories = 10;
37      protected File _rootdir = null;
38      protected int _maxFilenameLength = 128;
39  
40      /**
41       * @param root the root directory to store objects in
42       * @param numdirs the number of directories to create under root (must be >0)
43       */
44      public ShareableFileStash(String root, int numdirs) {
45          _rootdir = new File(root);
46          _numDirectories = numdirs;
47      }
48  
49      /**
50       * @param root the root directory to store objects in
51       * @param numdirs the number of directories to create under root (must be >0)
52       */
53      public ShareableFileStash(File root, int numdirs) {
54          _rootdir = root;
55          _numDirectories = numdirs;
56      }
57  
58      /**
59       * @param root the root directory to store objects in
60       * @param numdirs the number of directories to create under root (must be >0)
61       * @param maxfilenamelength the maximum length filename to create
62       */
63      public ShareableFileStash(File root, int numdirs, int maxfilenamelength) {
64          _rootdir = root;
65          _numDirectories = numdirs;
66          _maxFilenameLength = maxfilenamelength;
67      }
68  
69      /**
70       * @param root the root directory to store objects in
71       * @param numdirs the number of directories to create under root (must be >0)
72       * @param maxfilenamelength the maximum length filename to create
73       */
74      public ShareableFileStash(String root, int numdirs, int maxfilenamelength) {
75          _rootdir = new File(root);
76          _numDirectories = numdirs;
77          _maxFilenameLength = maxfilenamelength;
78      }
79  
80      protected byte[] getSerializedForm(Serializable val) {
81          byte[] serform = null;
82          if(null == val) {
83              return null;
84          }
85          ByteArrayOutputStream byout = null;
86          ObjectOutputStream out = null;
87          try {
88              byout = new ByteArrayOutputStream();
89              out = new ObjectOutputStream(byout);
90              out.writeObject(val);
91              out.flush();
92              serform = byout.toByteArray();
93          } catch(IOException e) {
94              serform = null;
95          } finally {
96              try {
97                  byout.close();
98              } catch(Exception e) {
99              }
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