KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > mckoi > store > ScatteringStoreDataAccessor


1 /**
2  * com.mckoi.store.ScatteringStoreDataAccessor 13 Jun 2003
3  *
4  * Mckoi SQL Database ( http://www.mckoi.com/database )
5  * Copyright (C) 2000, 2001, 2002 Diehl and Associates, Inc.
6  *
7  * This program is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License
9  * Version 2 as published by the Free Software Foundation.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14  * GNU General Public License Version 2 for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * Version 2 along with this program; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
19  *
20  * Change Log:
21  *
22  *
23  */

24
25 package com.mckoi.store;
26
27 import java.util.ArrayList JavaDoc;
28 import java.io.*;
29
30 /**
31  * An implementation of StoreDataAccessor that scatters the addressible
32  * data resource across multiple files in the file system. When one store
33  * data resource reaches a certain threshold size, the content 'flows' over
34  * to the next file.
35  *
36  * @author Tobias Downer
37  */

38
39 public class ScatteringStoreDataAccessor implements StoreDataAccessor {
40
41 // /**
42
// * True if the data accessor is read only.
43
// */
44
// private boolean read_only;
45

46   /**
47    * The path of this store in the file system.
48    */

49   private final File path;
50
51   /**
52    * The name of the file in the file system minus the extension.
53    */

54   private final String JavaDoc file_name;
55
56   /**
57    * The extension of the first file in the sliced set.
58    */

59   private final String JavaDoc first_ext;
60
61   /**
62    * The maximum size a file slice can grow too before a new slice is created.
63    */

64   private final long max_slice_size;
65   
66   /**
67    * The list of RandomAccessFile objects for each file that represents a
68    * slice of the store. (FileSlice objects)
69    */

70   private ArrayList JavaDoc slice_list;
71
72   /**
73    * The current actual physical size of the store data on disk.
74    */

75   private long true_file_length;
76
77   /**
78    * A lock when modifying the true_data_size, and slice_list.
79    */

80   private final Object JavaDoc lock = new Object JavaDoc();;
81
82   /**
83    * Set when the store is openned.
84    */

85   private boolean open = false;
86   
87
88
89   /**
90    * Constructs the store data accessor.
91    */

92   public ScatteringStoreDataAccessor(File path, String JavaDoc file_name,
93                                      String JavaDoc first_ext, long max_slice_size) {
94     slice_list = new ArrayList JavaDoc();
95     this.path = path;
96     this.file_name = file_name;
97     this.first_ext = first_ext;
98     this.max_slice_size = max_slice_size;
99   }
100
101   /**
102    * Given a file, this will convert to a scattering file store with files
103    * no larger than the maximum slice size.
104    */

105   public void convertToScatteringStore(File f) throws IOException {
106
107     int BUFFER_SIZE = 65536;
108
109     RandomAccessFile src = new RandomAccessFile(f, "rw");
110     long file_size = f.length();
111     long current_p = max_slice_size;
112     long to_write = Math.min(file_size - current_p, max_slice_size);
113     int write_to_part = 1;
114
115     byte[] copy_buffer = new byte[BUFFER_SIZE];
116
117     while (to_write > 0) {
118
119       src.seek(current_p);
120
121       File to_f = slicePartFile(write_to_part);
122       if (to_f.exists()) {
123         throw new IOException("Copy error, slice already exists.");
124       }
125       FileOutputStream to_raf = new FileOutputStream(to_f);
126
127       while (to_write > 0) {
128         int size_to_copy = (int) Math.min(BUFFER_SIZE, to_write);
129
130         src.readFully(copy_buffer, 0, size_to_copy);
131         to_raf.write(copy_buffer, 0, size_to_copy);
132
133         current_p += size_to_copy;
134         to_write -= size_to_copy;
135       }
136
137       to_raf.flush();
138       to_raf.close();
139
140       to_write = Math.min(file_size - current_p, max_slice_size);
141       ++write_to_part;
142     }
143
144     // Truncate the source file
145
if (file_size > max_slice_size) {
146       src.seek(0);
147       src.setLength(max_slice_size);
148     }
149     src.close();
150
151   }
152
153   /**
154    * Given an index value, this will return a File object for the nth slice in
155    * the file system. For example, given '4' will return [file name].004,
156    * given 1004 will return [file name].1004, etc.
157    */

158   private File slicePartFile(int i) {
159     if (i == 0) {
160       return new File(path, file_name + "." + first_ext);
161     }
162     StringBuffer JavaDoc fn = new StringBuffer JavaDoc();
163     fn.append(file_name);
164     fn.append(".");
165     if (i < 10) {
166       fn.append("00");
167     }
168     else if (i < 100) {
169       fn.append("0");
170     }
171     fn.append(i);
172     return new File(path, fn.toString());
173   }
174
175   /**
176    * Counts the number of files in the file store that represent this store.
177    */

178   private int countStoreFiles() {
179     int i = 0;
180     File f = slicePartFile(i);
181     while (f.exists()) {
182       ++i;
183       f = slicePartFile(i);
184     }
185     return i;
186   }
187
188   /**
189    * Creates a StoreDataAccessor object for accessing a given slice.
190    */

191   private StoreDataAccessor createSliceDataAccessor(File file) {
192     // Currently we only support an IOStoreDataAccessor object.
193
return new IOStoreDataAccessor(file);
194   }
195
196   /**
197    * Discovers the size of the data resource (doesn't require the file to be
198    * open).
199    */

200   private long discoverSize() throws IOException {
201     long running_total = 0;
202     
203     synchronized (lock) {
204       // Does the file exist?
205
int i = 0;
206       File f = slicePartFile(i);
207       while (f.exists()) {
208         running_total += createSliceDataAccessor(f).getSize();
209
210         ++i;
211         f = slicePartFile(i);
212       }
213     }
214
215     return running_total;
216   }
217   
218   // ---------- Implemented from StoreDataAccessor ----------
219

220   public void open(boolean read_only) throws IOException {
221     long running_length;
222     
223     synchronized (lock) {
224       slice_list = new ArrayList JavaDoc();
225       
226       // Does the file exist?
227
File f = slicePartFile(0);
228       boolean open_existing = f.exists();
229       
230       // If the file already exceeds the threshold and there isn't a secondary
231
// file then we need to convert the file.
232
if (open_existing && f.length() > max_slice_size) {
233         File f2 = slicePartFile(1);
234         if (f2.exists()) {
235           throw new IOException(
236                           "File length exceeds maximum slice size setting.");
237         }
238         // We need to scatter the file.
239
if (!read_only) {
240           convertToScatteringStore(f);
241         }
242         else {
243           throw new IOException(
244                 "Unable to convert to a scattered store because read-only.");
245         }
246       }
247   
248       // Setup the first file slice
249
FileSlice slice = new FileSlice();
250       slice.data = createSliceDataAccessor(f);
251       slice.data.open(read_only);
252       
253       slice_list.add(slice);
254       running_length = slice.data.getSize();
255   
256       // If we are opening a store that exists already, there may be other
257
// slices we need to setup.
258
if (open_existing) {
259         int i = 1;
260         File slice_part = slicePartFile(i);
261         while (slice_part.exists()) {
262           // Create the new slice information for this part of the file.
263
slice = new FileSlice();
264           slice.data = createSliceDataAccessor(slice_part);
265           slice.data.open(read_only);
266   
267           slice_list.add(slice);
268           running_length += slice.data.getSize();
269   
270           ++i;
271           slice_part = slicePartFile(i);
272         }
273       }
274   
275       true_file_length = running_length;
276       
277       open = true;
278     }
279   }
280   
281   public void close() throws IOException {
282     synchronized (lock) {
283       int sz = slice_list.size();
284       for (int i = 0; i < sz; ++i) {
285         FileSlice slice = (FileSlice) slice_list.get(i);
286         slice.data.close();
287       }
288       slice_list = null;
289       open = false;
290     }
291   }
292
293   public boolean delete() {
294     // The number of files
295
int count_files = countStoreFiles();
296     // Delete each file from back to front
297
for (int i = count_files - 1; i >= 0; --i) {
298       File f = slicePartFile(i);
299       boolean delete_success = createSliceDataAccessor(f).delete();
300       if (!delete_success) {
301         return false;
302       }
303     }
304     return true;
305   }
306
307   public boolean exists() {
308     return slicePartFile(0).exists();
309   }
310   
311   
312   public void read(long position, byte[] buf, int off, int len)
313                                                         throws IOException {
314     // Reads the array (potentially across multiple slices).
315
while (len > 0) {
316       int file_i = (int) (position / max_slice_size);
317       long file_p = (position % max_slice_size);
318       int file_len = (int) Math.min((long) len, max_slice_size - file_p);
319
320       FileSlice slice;
321       synchronized (lock) {
322         // Return if out of bounds.
323
if (file_i < 0 || file_i >= slice_list.size()) {
324           return;
325         }
326         slice = (FileSlice) slice_list.get(file_i);
327       }
328       slice.data.read(file_p, buf, off, file_len);
329       
330       position += file_len;
331       off += file_len;
332       len -= file_len;
333     }
334   }
335
336   public void write(long position, byte[] buf, int off, int len)
337                                                         throws IOException {
338     // Writes the array (potentially across multiple slices).
339
while (len > 0) {
340       int file_i = (int) (position / max_slice_size);
341       long file_p = (position % max_slice_size);
342       int file_len = (int) Math.min((long) len, max_slice_size - file_p);
343
344       FileSlice slice;
345       synchronized (lock) {
346         // Return if out of bounds.
347
if (file_i < 0 || file_i >= slice_list.size()) {
348           return;
349         }
350         slice = (FileSlice) slice_list.get(file_i);
351       }
352       slice.data.write(file_p, buf, off, file_len);
353
354       position += file_len;
355       off += file_len;
356       len -= file_len;
357     }
358   }
359
360   public void setSize(long length) throws IOException {
361     synchronized (lock) {
362       // The size we need to grow the data area
363
long total_size_to_grow = length - true_file_length;
364       // Assert that we aren't shrinking the data area size.
365
if (total_size_to_grow < 0) {
366         throw new IOException("Unable to make the data area size " +
367                               "smaller for this type of store.");
368       }
369
370       while (total_size_to_grow > 0) {
371         // Grow the last slice by this size
372
int last = slice_list.size() - 1;
373         FileSlice slice = (FileSlice) slice_list.get(last);
374         final long old_slice_length = slice.data.getSize();
375         long to_grow = Math.min(total_size_to_grow,
376                                 (max_slice_size - old_slice_length));
377
378         // Flush the buffer and set the length of the file
379
slice.data.setSize(old_slice_length + to_grow);
380         // Synchronize the file change. XP appears to defer a file size change
381
// and it can result in errors if the JVM is terminated.
382
slice.data.synch();
383
384         total_size_to_grow -= to_grow;
385         // Create a new empty slice if we need to extend the data area
386
if (total_size_to_grow > 0) {
387           File slice_file = slicePartFile(last + 1);
388
389           slice = new FileSlice();
390           slice.data = createSliceDataAccessor(slice_file);
391           slice.data.open(false);
392
393           slice_list.add(slice);
394         }
395       }
396       true_file_length = length;
397     }
398
399   }
400
401   public long getSize() throws IOException {
402     synchronized (lock) {
403       if (open) {
404         return true_file_length;
405       }
406       else {
407         return discoverSize();
408       }
409     }
410   }
411   
412   public void synch() throws IOException {
413     synchronized (lock) {
414       int sz = slice_list.size();
415       for (int i = 0; i < sz; ++i) {
416         FileSlice slice = (FileSlice) slice_list.get(i);
417         slice.data.synch();
418       }
419     }
420   }
421
422   // ---------- Inner classes ----------
423

424   /**
425    * An object that contains information about a file slice. The information
426    * includes the name of the file, the RandomAccessFile that represents the
427    * slice, and the size of the file.
428    */

429   private static class FileSlice {
430
431     StoreDataAccessor data;
432     
433   }
434   
435 }
436
437
Popular Tags