View Javadoc

1   /*   Open Source Java Caching Service
2   *    Copyright (C) 2002 Frank Karlstrøm
3   *    This library is free software; you can redistribute it and/or
4   *    modify it under the terms of the GNU Lesser General Public
5   *    License as published by the Free Software Foundation; either
6   *    version 2.1 of the License, or (at your option) any later version.
7   *
8   *    This library is distributed in the hope that it will be useful,
9   *    but WITHOUT ANY WARRANTY; without even the implied warranty of
10  *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11  *    Lesser General Public License for more details.
12  *
13  *    You should have received a copy of the GNU Lesser General Public
14  *    License along with this library; if not, write to the Free Software
15  *    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
16  *
17  *    The author can be contacted by email: fjankk@users.sourceforge.net
18  */
19  package org.fjank.jcache.persistence;
20  
21  import java.io.BufferedInputStream;
22  import java.io.ByteArrayInputStream;
23  import java.io.ByteArrayOutputStream;
24  import java.io.File;
25  import java.io.FileNotFoundException;
26  import java.io.IOException;
27  import java.io.NotSerializableException;
28  import java.io.ObjectInputStream;
29  import java.io.ObjectOutputStream;
30  import java.io.RandomAccessFile;
31  import java.io.Serializable;
32  import javax.util.jcache.DiskCacheException;
33  import org.fjank.jcache.CacheObject;
34  import org.fjank.jcache.DiskCacheObject;
35  
36  
37  /**
38   * Provides thread safe access to the underlying random access file.
39   */
40  public class CacheFileAdapter {
41      /** the path to the files */
42      private final String filepath;
43  
44      /** an accessor to the file */
45      private RandomAccessFile raf;
46     
47      /**
48       * Creates a new CacheFileAdapter object.
49       *
50       * @param file the file to adapt to
51       *
52       * @throws DiskCacheException if any exceptioins occur.
53       */
54      public CacheFileAdapter(final File file) {
55          this.filepath = file.getAbsolutePath();
56          try {
57              raf = new RandomAccessFile(filepath, "rw");
58          } catch (FileNotFoundException e) {
59              throw new IllegalStateException(
60                  "The disk cache could not be initialized.");
61          }
62      }
63  
64      /**
65       * returns a String representation of this object.
66       *
67       * @return a String representation of this object.
68       */
69      public String toString() {
70          return getClass().getName() + ", file:" + filepath;
71      }
72      /**
73       * reads from the diskfile, and return the raw bytes
74       * contained at that pos. Its up to the client to deSerialize.
75       * @param pos the position to start at.
76       * @return a byte[] containing the raw bytes for the object.
77       */
78      public byte[] read(long pos) {
79          byte[] data = null;
80          boolean corrupted = false;
81          try {
82              synchronized (this) {
83                  raf.seek(pos);
84                  int datalen = raf.readInt();
85                  if (datalen > raf.length()) {
86                      corrupted = true;
87                  } else {
88                      raf.readFully(data = new byte[datalen]);
89                  }
90              }
91              if (corrupted) {
92                  throw new IllegalStateException("The Cache file is corrupted.");
93              }
94              return data;
95          } catch (IOException e) {
96              throw new IllegalStateException(e.getMessage());
97          } 
98      }
99      
100     /**
101      * reads an object starting at the position,.
102      *
103      * @param pos the position to start at
104      *
105      * @return a Serializable object read from the diskcache.
106      * @throws DiskCacheException
107      *
108      * @throws DiskCacheException if any exceptions occur.
109      */
110     public Serializable readObject(long pos) throws DiskCacheException {
111         byte[] data = read(pos);
112         return deSerialize(data);
113          
114     }
115 
116     
117 
118     public synchronized void append(byte[] data) throws DiskCacheException {
119         try {
120             write(data, raf.length());
121         } catch (IOException e) {
122             throw new DiskCacheException(e);
123         }
124     }
125 
126     public synchronized void write(byte[] data, long pos) {
127         try {
128             raf.seek(pos);
129             raf.writeInt(data.length);
130             raf.write(data);
131         } catch (IOException e) {
132             throw new IllegalStateException(e.getMessage());
133         }
134     }
135 
136     /**
137      * writes an object down to the cachefile
138      *
139      * @param obj the object to write
140      * @param pos the position to start at.
141      *
142      * @throws DiskCacheException if any exception occur.
143      */
144     public void writeObject(final Serializable obj, final long pos) {
145         write(serialize(obj), pos);
146     }
147 
148     /**
149      * adds an object to the diskcache.
150      *
151      * @param obj the object to add.
152      *
153      * @return a disk element descriptor used to locate the object
154      *
155      * @throws DiskCacheException if any exceptions occur.
156      */
157     public DiskElementDescriptor appendObject(CacheObject obj) {
158         long pos = -1;
159         DiskElementDescriptor ded = new DiskElementDescriptor();
160         byte[] data = serialize((Serializable) obj);
161         synchronized (this) {
162             pos = length();
163             ded.init(pos, data);
164             write(data, pos);
165         }
166         return ded;
167     }
168 
169     public synchronized long length() {
170         try {
171             return raf.length();
172         } catch (IOException e) {
173             throw new IllegalStateException(e.getMessage());
174         }
175     }
176 
177     public synchronized void close() {
178         try {
179             raf.close();
180         } catch (IOException e) {
181             throw new IllegalStateException(e.getMessage());
182         }
183     }
184 
185     public synchronized void reset() {
186         close();
187         File f = new File(filepath);
188         //boolean deleted = f.delete();
189         int retries=10;
190 
191         while (!f.delete()) {
192             retries--;
193             if(retries<=0) {
194             	throw new IllegalStateException("Failed to delete " + f.getName());
195             }
196             // todo busy wait.
197             try {
198 				Thread.sleep(100);
199 			} catch (InterruptedException e1) {
200 				//interrupted.
201 			}
202         }
203         try {
204             raf = new RandomAccessFile(filepath, "rw");
205         } catch (FileNotFoundException e) {
206             throw new IllegalStateException(e.getMessage());
207         }
208     }
209 
210     public static byte[] serialize(Serializable obj) {
211         ObjectOutputStream oos = null;
212         try {
213             ByteArrayOutputStream baos = new ByteArrayOutputStream();
214             oos = new ObjectOutputStream(baos);
215             oos.writeObject(obj);
216             return baos.toByteArray();
217         } catch (NotSerializableException e) {
218             throw new IllegalStateException(e.getMessage());
219         } catch (IOException e) {
220             throw new IllegalStateException(e.getMessage());
221         } finally {
222             if (oos != null) {
223                 try {
224                     oos.close();
225                 } catch (IOException e) {
226                     throw new IllegalStateException(
227                         "Failed to close stream to DiskCache, "
228                         + "cache may be corrupted. Reason: " + e.getMessage());
229                 }
230             }
231         }
232     }
233     public static DiskCacheObject deSerialize(byte[] data) throws DiskCacheException {
234         ObjectInputStream ois = null;
235         try {
236             ByteArrayInputStream bais = new ByteArrayInputStream(data);
237             BufferedInputStream bis = new BufferedInputStream(bais);
238             ois=new ObjectInputStream(bis);
239             DiskCacheObject obj = (DiskCacheObject) ois.readObject();
240             obj.getCacheObject().resetRefCount();
241             return obj;
242         } catch (IOException e) {
243             throw new IllegalStateException("An exception occured when reading from the disk cache." +
244             		" Reason: " + e.getMessage());
245         } catch (ClassNotFoundException e) {
246             throw new IllegalStateException("A class could not be found when deSerializing." +
247             		" Reason: " + e.getMessage());
248         } finally {
249 	        if (ois != null) {
250 	            try {
251 	                ois.close();
252 	            } catch (IOException e) {
253 	                throw new IllegalStateException(
254 	                    "Failed to close stream to DiskCache, "
255 	                    + "cache may be corrupted. Reason: " + e.getMessage());
256 	            }
257 	        }
258         }
259     }
260 }