KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > net > sf > jasperreports > engine > util > JRSwapFile


1 /*
2  * ============================================================================
3  * GNU Lesser General Public License
4  * ============================================================================
5  *
6  * JasperReports - Free Java report-generating library.
7  * Copyright (C) 2001-2006 JasperSoft Corporation http://www.jaspersoft.com
8  *
9  * This library is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU Lesser General Public
11  * License as published by the Free Software Foundation; either
12  * version 2.1 of the License, or (at your option) any later version.
13  *
14  * This library is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17  * Lesser General Public License for more details.
18  *
19  * You should have received a copy of the GNU Lesser General Public
20  * License along with this library; if not, write to the Free Software
21  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
22  *
23  * JasperSoft Corporation
24  * 303 Second Street, Suite 450 North
25  * San Francisco, CA 94107
26  * http://www.jaspersoft.com
27  */

28 package net.sf.jasperreports.engine.util;
29
30 import java.io.File JavaDoc;
31 import java.io.FileNotFoundException JavaDoc;
32 import java.io.IOException JavaDoc;
33 import java.io.RandomAccessFile JavaDoc;
34
35 import org.apache.commons.logging.Log;
36 import org.apache.commons.logging.LogFactory;
37
38 import net.sf.jasperreports.engine.JRRuntimeException;
39
40
41 /**
42  * Swap file implementation that can be used as a disk cache for arbitrary binary data.
43  * <p>
44  * Fixed-size blocks are allocated inside the swap file when a caller wants to write data.
45  * The caller receives a handle to the allocated area based on which it can read the data
46  * or free the area.
47  * <p>
48  * The implementation is thread-safe. I/O operations are performed in synchronized blocks,
49  * only one thread would do a read or write at one moment.
50  *
51  * @author Lucian Chirita (lucianc@users.sourceforge.net)
52  * @version $Id: JRSwapFile.java 1334 2006-07-11 16:05:31 +0300 (Tue, 11 Jul 2006) lucianc $
53  */

54 public class JRSwapFile
55 {
56     private static final Log log = LogFactory.getLog(JRSwapFile.class);
57
58     private final File JavaDoc swapFile;
59     protected final RandomAccessFile JavaDoc file;
60     private final int blockSize;
61     private final int minGrowCount;
62     private final LongQueue freeBlocks;
63     
64     
65     /**
66      * Creates a swap file.
67      *
68      * The file name is generated automatically.
69      *
70      * @param directory the directory where the file should be created.
71      * @param blockSize the size of the blocks allocated by the swap file
72      * @param minGrowCount the minimum number of blocks by which the swap file grows when full
73      */

74     public JRSwapFile(String JavaDoc directory, int blockSize, int minGrowCount)
75     {
76         try
77         {
78             String JavaDoc filename = "swap_" + System.identityHashCode(this) + "_" + System.currentTimeMillis();
79             swapFile = new File JavaDoc(directory, filename);
80             if (log.isDebugEnabled())
81             {
82                 log.debug("Creating swap file " + swapFile.getPath());
83             }
84             boolean fileExists = swapFile.exists();
85             swapFile.deleteOnExit();
86             file = new RandomAccessFile JavaDoc(swapFile, "rw");
87
88             this.blockSize = blockSize;
89             this.minGrowCount = minGrowCount;
90             freeBlocks = new LongQueue(minGrowCount);
91             
92             if (fileExists)
93             {
94                 file.setLength(0);
95                 if (log.isDebugEnabled())
96                 {
97                     log.debug("Swap file " + swapFile.getPath() + " exists, truncating");
98                 }
99             }
100         }
101         catch (FileNotFoundException JavaDoc e)
102         {
103             throw new JRRuntimeException(e);
104         }
105         catch (IOException JavaDoc e)
106         {
107             throw new JRRuntimeException(e);
108         }
109     }
110
111
112     /**
113      * Allocates an area in the swap file and writes data in it.
114      *
115      * @param data the data for which to allocate an area in the file
116      * @return a handle to the allocated area
117      * @throws IOException
118      */

119     public SwapHandle write(byte[] data) throws IOException JavaDoc
120     {
121         int blockCount = (data.length - 1) / blockSize + 1;
122         long[] offsets = reserveFreeBlocks(blockCount);
123         int lastBlockSize = (data.length - 1) % blockSize + 1;
124         SwapHandle handle = new SwapHandle(offsets, lastBlockSize);
125         for (int i = 0; i < blockCount; ++i)
126         {
127             int dataSize = i < blockCount - 1 ? blockSize : lastBlockSize;
128             int dataOffset = i * blockSize;
129             write(data, dataSize, dataOffset, offsets[i]);
130         }
131         
132         return handle;
133     }
134
135
136     protected void write(byte[] data, int dataSize, int dataOffset, long fileOffset) throws IOException JavaDoc
137     {
138         synchronized (this)
139         {
140             file.seek(fileOffset);
141             file.write(data, dataOffset, dataSize);
142         }
143     }
144
145     
146     /**
147      * Reads all the data from an allocated area.
148      *
149      * @param handle the allocated area handle
150      * @param free whether to free the area after reading
151      * @return the whole data saved in an allocated area
152      * @throws IOException
153      */

154     public byte[] read(SwapHandle handle, boolean free) throws IOException JavaDoc
155     {
156         long[] offsets = handle.getOffsets();
157         int totalLength = (offsets.length - 1) * blockSize + handle.getLastSize();
158         byte[] data = new byte[totalLength];
159         
160         for (int i = 0; i < offsets.length; ++i)
161         {
162             int dataOffset = i * blockSize;
163             int dataLength = i < offsets.length - 1 ? blockSize : handle.getLastSize();
164             read(data, dataOffset, dataLength, offsets[i]);
165         }
166         
167         if (free)
168         {
169             freeBlocks(offsets);
170         }
171         
172         return data;
173     }
174
175
176     protected void read(byte[] data, int dataOffset, int dataLength, long fileOffset) throws IOException JavaDoc
177     {
178         synchronized (this)
179         {
180             file.seek(fileOffset);
181             file.readFully(data, dataOffset, dataLength);
182         }
183     }
184     
185     
186     /**
187      * Frees an allocated area.
188      *
189      * @param handle the allocated area handle
190      */

191     public void free(SwapHandle handle)
192     {
193         freeBlocks(handle.getOffsets());
194     }
195     
196     
197     /**
198      * Closes and deletes the swap file.
199      */

200     public void dispose()
201     {
202         synchronized (this)
203         {
204             if (swapFile.exists())
205             {
206                 if (log.isDebugEnabled())
207                 {
208                     log.debug("Disposing swap file " + swapFile.getPath());
209                 }
210
211                 try
212                 {
213                     file.close();
214                 }
215                 catch (IOException JavaDoc e)
216                 {
217                     log.warn("Not able to close swap file " + swapFile.getPath());
218                 }
219
220                 if (!swapFile.delete())
221                 {
222                     log.warn("Not able to delete swap file " + swapFile.getPath());
223                 }
224             }
225         }
226     }
227
228
229     protected void finalize() throws Throwable JavaDoc
230     {
231         dispose();
232         super.finalize();
233     }
234
235
236     protected synchronized long[] reserveFreeBlocks(int blockCount) throws IOException JavaDoc
237     {
238         int growCount = blockCount - freeBlocks.size();
239         if (growCount > 0)
240         {
241             if (growCount < minGrowCount)
242             {
243                 growCount = minGrowCount;
244             }
245             
246             long length = file.length();
247             long newLength = length + growCount * blockSize;
248             if (log.isDebugEnabled())
249             {
250                 log.debug("Growing swap file " + swapFile.getPath() + " with " + growCount + " blocks x " + blockSize + " bytes to size " + newLength);
251             }
252             file.setLength(newLength);
253
254             for (int i = 0; i < growCount; ++i)
255             {
256                 freeBlocks.addLast(length + i * blockSize);
257             }
258         }
259         
260         long[] offsets = new long[blockCount];
261         for (int i = 0; i < blockCount; i++)
262         {
263             offsets[i] = freeBlocks.popFirst();
264         }
265         return offsets;
266     }
267
268
269     protected synchronized void freeBlocks(long []offsets)
270     {
271         for (int i = offsets.length - 1; i >= 0; --i)
272         {
273             freeBlocks.addFirst(offsets[i]);
274         }
275     }
276     
277     
278     protected static class LongQueue
279     {
280         private final int minGrowCount;
281         private long[] vals;
282         private int size;
283         private int first;
284         private int last;
285         
286         public LongQueue(int minGrowCount)
287         {
288             this.minGrowCount = minGrowCount;
289             vals = new long[minGrowCount];
290             size = 0;
291             first = 0;
292             last = 0;
293         }
294
295         public void addFirst(long val)
296         {
297             growIfFull();
298             
299             --first;
300             if (first == -1)
301             {
302                 first = vals.length - 1;
303             }
304             vals[first] = val;
305             ++size;
306         }
307
308         public void addLast(long val)
309         {
310             growIfFull();
311             
312             vals[last] = val;
313             ++size;
314             ++last;
315             if (last == vals.length)
316             {
317                 last = 0;
318             }
319         }
320
321         public long popFirst()
322         {
323             if (size == 0)
324             {
325                 throw new JRRuntimeException("Queue underflow");
326             }
327
328             long val = vals[first];
329             ++first;
330             if (first == vals.length)
331             {
332                 first = 0;
333             }
334             --size;
335
336             return val;
337         }
338
339         protected void growIfFull()
340         {
341             int valsLenght = vals.length;
342             if (size == valsLenght)
343             {
344                 int newLength = (valsLenght * 3) / 2 + 1;
345                 if (newLength - valsLenght < minGrowCount)
346                 {
347                     newLength = valsLenght + minGrowCount;
348                 }
349                 
350                 long[] newVals = new long[newLength];
351                 System.arraycopy(vals, first, newVals, 0, valsLenght - first);
352                 if (last > 0)
353                 {
354                     System.arraycopy(vals, 0, newVals, valsLenght - first, last);
355                 }
356                 
357                 vals = newVals;
358                 first = 0;
359                 last = valsLenght;
360             }
361         }
362
363         public int size()
364         {
365             return size;
366         }
367     }
368     
369     public static class SwapHandle
370     {
371         private final long[] offsets;
372         private final int lastSize;
373         
374         public SwapHandle(long[] offsets, int lastSize)
375         {
376             this.offsets = offsets;
377             this.lastSize = lastSize;
378         }
379
380         public long[] getOffsets()
381         {
382             return offsets;
383         }
384         
385         public int getLastSize()
386         {
387             return lastSize;
388         }
389     }
390 }
391
Popular Tags