KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > eclipse > osgi > framework > internal > reliablefile > ReliableFile


1 /*******************************************************************************
2  * Copyright (c) 2003, 2006 IBM Corporation and others.
3  * All rights reserved. This program and the accompanying materials
4  * are made available under the terms of the Eclipse Public License v1.0
5  * which accompanies this distribution, and is available at
6  * http://www.eclipse.org/legal/epl-v10.html
7  *
8  * Contributors:
9  * IBM Corporation - initial API and implementation
10  *******************************************************************************/

11
12 package org.eclipse.osgi.framework.internal.reliablefile;
13
14 import java.io.*;
15 import java.util.*;
16 import java.util.zip.CRC32 JavaDoc;
17 import java.util.zip.Checksum JavaDoc;
18 import org.eclipse.osgi.framework.internal.core.FrameworkProperties;
19
20 /**
21  * ReliableFile class used by ReliableFileInputStream and ReliableOutputStream.
22  * This class encapsulates all the logic for reliable file support.
23  *
24  */

25 public class ReliableFile {
26     /**
27      * Open mask. Obtain the best data stream available. If the primary data
28      * contents are invalid (corrupt, missing, etc.), the data for a prior
29      * version may be used.
30      * An IOException will be thrown if a valid data content can not be
31      * determined.
32      * This is mutually exclusive with <code>OPEN_FAIL_ON_PRIMARY</code>.
33      */

34     public static final int OPEN_BEST_AVAILABLE = 0;
35     /**
36      * Open mask. Obtain only the data stream for the primary file where any other
37      * version will not be valid. This should be used for data streams that are
38      * managed as a group as a prior contents may not match the other group data.
39      * If the primary data is not invalid, a IOException will be thrown.
40      * This is mutually exclusive with <code>OPEN_BEST_AVAILABLE</code>.
41      */

42     public static final int OPEN_FAIL_ON_PRIMARY = 1;
43
44     /**
45      * Use the last generation of the file
46      */

47     public static final int GENERATION_LATEST = 0;
48     /**
49      * Keep intinite backup files
50      */

51     public static final int GENERATIONS_INFINITE = 0;
52
53     /**
54      * Extension of tmp file used during writing.
55      * A reliable file with this extension should
56      * never be directly used.
57      */

58     public static final String JavaDoc tmpExt = ".tmp"; //$NON-NLS-1$
59

60     /**
61      * Property to set the maximum size of a file that will be buffered. When calculating a ReliableFile
62      * checksum, if the file is this size or small, ReliableFile will read the file contents into a
63      * <code>BufferedInputStream</code> and reset the buffer to avoid having to read the data from the
64      * media twice. Since this method require memory for storage, it is limited to this size. The default
65      * maximum is 128-KBytes.
66      */

67     public static final String JavaDoc PROP_MAX_BUFFER = "osgi.reliableFile.maxInputStreamBuffer"; //$NON-NLS-1$
68
/**
69      * The maximum number of generations to keep as backup files in case last generation
70      * file is determined to be invalid.
71      */

72     public static final String JavaDoc PROP_MAX_GENERATIONS = "osgi.ReliableFile.maxGenerations"; //$NON-NLS-1$
73
/**
74      * @see org.eclipse.core.runtime.internal.adaptor.BasicLocation#PROP_OSGI_LOCKING
75      */

76     public static final String JavaDoc PROP_OSGI_LOCKING = "osgi.locking"; //$NON-NLS-1$
77

78     protected static final int FILETYPE_UNKNOWN = -1;
79     protected static final int FILETYPE_VALID = 0;
80     protected static final int FILETYPE_CORRUPT = 1;
81     protected static final int FILETYPE_NOSIGNATURE = 2;
82
83     protected static final byte identifier1[] = {'.', 'c', 'r', 'c'};
84     protected static final byte identifier2[] = {'.', 'v', '1', '\n'};
85
86     private static final int BUF_SIZE = 4096;
87     private static int maxInputStreamBuffer = 128 * 1024; //128k
88
private static int defaultMaxGenerations = 2;
89     private static boolean fileSharing = true;
90     //our cache of the last lookuped up generations for a file
91
private static File lastGenerationFile = null;
92     private static int[] lastGenerations = null;
93
94     static {
95         String JavaDoc prop = FrameworkProperties.getProperty(PROP_MAX_BUFFER);
96         if (prop != null) {
97             try {
98                 maxInputStreamBuffer = Integer.parseInt(prop);
99             } catch (NumberFormatException JavaDoc e) {/*ignore*/
100             }
101         }
102         prop = FrameworkProperties.getProperty(PROP_MAX_GENERATIONS);
103         if (prop != null) {
104             try {
105                 defaultMaxGenerations = Integer.parseInt(prop);
106             } catch (NumberFormatException JavaDoc e) {/*ignore*/
107             }
108         }
109         prop = FrameworkProperties.getProperty(PROP_OSGI_LOCKING);
110         if (prop != null) {
111             if (prop.equals("none")) { //$NON-NLS-1$
112
fileSharing = false;
113             }
114         }
115     }
116
117     /** File object for original reference file */
118     private File referenceFile;
119
120     /** List of checksum file objects: File => specific ReliableFile generation */
121     private static Hashtable cacheFiles = new Hashtable(20);
122
123     private File inputFile = null;
124     private File outputFile = null;
125     private Checksum JavaDoc appendChecksum = null;
126
127     /**
128      * ReliableFile object factory. This method is called by ReliableFileInputStream
129      * and ReliableFileOutputStream to get a ReliableFile object for a target file.
130      * If the object is in the cache, the cached copy is returned.
131      * Otherwise a new ReliableFile object is created and returned.
132      * The use count of the returned ReliableFile object is incremented.
133      *
134      * @param name Name of the target file.
135      * @return A ReliableFile object for the target file.
136      * @throws IOException If the target file is a directory.
137      */

138     protected static ReliableFile getReliableFile(String JavaDoc name) throws IOException {
139         return getReliableFile(new File(name));
140     }
141
142     /**
143      * ReliableFile object factory. This method is called by ReliableFileInputStream
144      * and ReliableFileOutputStream to get a ReliableFile object for a target file.
145      * If the object is in the cache, the cached copy is returned.
146      * Otherwise a new ReliableFile object is created and returned.
147      * The use count of the returned ReliableFile object is incremented.
148      *
149      * @param file File object for the target file.
150      * @return A ReliableFile object for the target file.
151      * @throws IOException If the target file is a directory.
152      */

153     protected static ReliableFile getReliableFile(File file) throws IOException {
154         if (file.isDirectory()) {
155             throw new FileNotFoundException("file is a directory"); //$NON-NLS-1$
156
}
157         return new ReliableFile(file);
158     }
159
160     /**
161      * Private constructor used by the static getReliableFile factory methods.
162      *
163      * @param file File object for the target file.
164      */

165     private ReliableFile(File file) {
166         referenceFile = file;
167     }
168
169     private static int[] getFileGenerations(File file) {
170         if (!fileSharing && lastGenerationFile != null) {
171             //shortcut maybe, only if filesharing is not supported
172
if (file.equals(lastGenerationFile))
173                 return lastGenerations;
174         }
175         int[] generations = null;
176         try {
177             String JavaDoc name = file.getName();
178             String JavaDoc prefix = name + '.';
179             int prefixLen = prefix.length();
180             File parent = new File(file.getParent());
181             String JavaDoc[] files = parent.list();
182             if (files == null)
183                 return null;
184             ArrayList list = new ArrayList(defaultMaxGenerations);
185             if (file.exists())
186                 list.add(new Integer JavaDoc(0)); //base file exists
187
for (int i = 0; i < files.length; i++) {
188                 if (files[i].startsWith(prefix)) {
189                     try {
190                         int id = Integer.parseInt(files[i].substring(prefixLen));
191                         list.add(new Integer JavaDoc(id));
192                     } catch (NumberFormatException JavaDoc e) {/*ignore*/
193                     }
194                 }
195             }
196             if (list.size() == 0)
197                 return null;
198             Object JavaDoc[] array = list.toArray();
199             Arrays.sort(array);
200             generations = new int[array.length];
201             for (int i = 0, j = array.length - 1; i < array.length; i++, j--) {
202                 generations[i] = ((Integer JavaDoc) array[j]).intValue();
203             }
204             return generations;
205         } finally {
206             if (!fileSharing) {
207                 lastGenerationFile = file;
208                 lastGenerations = generations;
209             }
210         }
211     }
212
213     /**
214      * Returns an InputStream object for reading the target file.
215      *
216      * @param generation the maximum generation to evaluate
217      * @param openMask mask used to open data.
218      * are invalid (corrupt, missing, etc).
219      * @return An InputStream object which can be used to read the target file.
220      * @throws IOException If an error occurs preparing the file.
221      */

222     protected InputStream getInputStream(int generation, int openMask) throws IOException {
223         if (inputFile != null) {
224             throw new IOException("Input stream already open"); //$NON-NLS-1$
225
}
226         int[] generations = getFileGenerations(referenceFile);
227         if (generations == null) {
228             throw new FileNotFoundException("File not found"); //$NON-NLS-1$
229
}
230         String JavaDoc name = referenceFile.getName();
231         File parent = new File(referenceFile.getParent());
232
233         boolean failOnPrimary = (openMask & OPEN_FAIL_ON_PRIMARY) != 0;
234         if (failOnPrimary && generation == GENERATIONS_INFINITE)
235             generation = generations[0];
236
237         File textFile = null;
238         InputStream textIS = null;
239         for (int idx = 0; idx < generations.length; idx++) {
240             if (generation != 0) {
241                 if (generations[idx] > generation || (failOnPrimary && generations[idx] != generation))
242                     continue;
243             }
244             File file;
245             if (generations[idx] != 0)
246                 file = new File(parent, name + '.' + generations[idx]);
247             else
248                 file = referenceFile;
249             InputStream is = null;
250             CacheInfo info;
251             synchronized (cacheFiles) {
252                 info = (CacheInfo) cacheFiles.get(file);
253             }
254             long timeStamp = file.lastModified();
255             if (info == null || timeStamp != info.timeStamp) {
256                 try {
257                     is = new FileInputStream(file);
258                     if (is.available() < maxInputStreamBuffer)
259                         is = new BufferedInputStream(is);
260                     Checksum JavaDoc cksum = getChecksumCalculator();
261                     int filetype = getStreamType(is, cksum);
262                     info = new CacheInfo(filetype, cksum, timeStamp);
263                     synchronized (cacheFiles) {
264                         cacheFiles.put(file, info);
265                     }
266                 } catch (IOException e) {/*ignore*/
267                 }
268             }
269
270             // if looking for a specific generation only, only look at one
271
// and return the result.
272
if (failOnPrimary) {
273                 if (info != null && info.filetype == FILETYPE_VALID) {
274                     inputFile = file;
275                     if (is != null)
276                         return is;
277                     return new FileInputStream(file);
278                 }
279                 throw new IOException("ReliableFile is corrupt"); //$NON-NLS-1$
280
}
281
282             // if error, ignore this file & try next
283
if (info == null)
284                 continue;
285
286             // we're not looking for a specific version, so let's pick the best case
287
switch (info.filetype) {
288                 case FILETYPE_VALID :
289                     inputFile = file;
290                     if (is != null)
291                         return is;
292                     return new FileInputStream(file);
293
294                 case FILETYPE_NOSIGNATURE :
295                     if (textFile == null) {
296                         textFile = file;
297                         textIS = is;
298                     }
299                     break;
300             }
301         }
302
303         // didn't find any valid files, if there are any plain text files
304
// use it instead
305
if (textFile != null) {
306             inputFile = textFile;
307             if (textIS != null)
308                 return textIS;
309             return new FileInputStream(textFile);
310         }
311         throw new IOException("ReliableFile is corrupt"); //$NON-NLS-1$
312
}
313
314     /**
315      * Returns an OutputStream object for writing the target file.
316      *
317      * @param append append new data to an existing file.
318      * @param appendGeneration specific generation of file to append from.
319      * @return An OutputStream object which can be used to write the target file.
320      * @throws IOException IOException If an error occurs preparing the file.
321      */

322     protected OutputStream getOutputStream(boolean append, int appendGeneration) throws IOException {
323         if (outputFile != null)
324             throw new IOException("Output stream is already open"); //$NON_NLS-1$ //$NON-NLS-1$
325
String JavaDoc name = referenceFile.getName();
326         File parent = new File(referenceFile.getParent());
327         File tmpFile = File.createTempFile(name, tmpExt, parent);
328
329         if (!append) {
330             OutputStream os = new FileOutputStream(tmpFile);
331             outputFile = tmpFile;
332             return os;
333         }
334
335         InputStream is;
336         try {
337             is = getInputStream(appendGeneration, OPEN_BEST_AVAILABLE);
338         } catch (FileNotFoundException e) {
339             OutputStream os = new FileOutputStream(tmpFile);
340             outputFile = tmpFile;
341             return os;
342         }
343
344         try {
345             CacheInfo info = (CacheInfo) cacheFiles.get(inputFile);
346             appendChecksum = info.checksum;
347             OutputStream os = new FileOutputStream(tmpFile);
348             if (info.filetype == FILETYPE_NOSIGNATURE) {
349                 cp(is, os, 0);
350             } else {
351                 cp(is, os, 16); // don't copy checksum signature
352
}
353             outputFile = tmpFile;
354             return os;
355         } finally {
356             closeInputFile();
357         }
358     }
359
360     /**
361      * Close the target file for reading.
362      *
363      * @param checksum Checksum of the file contenets
364      * @throws IOException If an error occurs closing the file.
365      */

366     protected void closeOutputFile(Checksum JavaDoc checksum) throws IOException {
367         if (outputFile == null)
368             throw new IOException("Output stream is not open"); //$NON-NLS-1$
369
int[] generations = getFileGenerations(referenceFile);
370         String JavaDoc name = referenceFile.getName();
371         File parent = new File(referenceFile.getParent());
372         File newFile;
373         if (generations == null)
374             newFile = new File(parent, name + ".1"); //$NON-NLS-1$
375
else
376             newFile = new File(parent, name + '.' + (generations[0] + 1));
377
378         mv(outputFile, newFile); // throws IOException if problem
379
outputFile = null;
380         appendChecksum = null;
381         CacheInfo info = new CacheInfo(FILETYPE_VALID, checksum, newFile.lastModified());
382         cacheFiles.put(newFile, info);
383         cleanup(generations, true);
384         lastGenerationFile = null;
385         lastGenerations = null;
386     }
387
388     /**
389      * Abort the current output stream and do not update the reliable file table.
390      *
391      */

392     protected void abortOutputFile() {
393         if (outputFile == null)
394             return;
395         outputFile.delete();
396         outputFile = null;
397         appendChecksum = null;
398     }
399
400     protected File getOutputFile() {
401         return outputFile;
402     }
403
404     /**
405      * Close the target file for reading.
406      */

407     void closeInputFile() {
408         inputFile = null;
409     }
410
411     private void cleanup(int[] generations, boolean generationAdded) {
412         if (generations == null)
413             return;
414         String JavaDoc name = referenceFile.getName();
415         File parent = new File(referenceFile.getParent());
416         int generationCount = generations.length;
417         // if a base file is in the list (0 in generations[]), we will
418
// never delete these files, so don't count them in the old
419
// generation count.
420
if (generations[generationCount - 1] == 0)
421             generationCount--;
422         // assume here that the int[] does not include a file just created
423
int rmCount = generationCount - defaultMaxGenerations;
424         if (generationAdded)
425             rmCount++;
426         if (rmCount < 1)
427             return;
428         synchronized (cacheFiles) {
429             // first, see if any of the files not deleted are known to
430
// be corrupt. If so, be sure to keep not to delete good
431
// backup files.
432
for (int idx = 0, count = generationCount - rmCount; idx < count; idx++) {
433                 File file = new File(parent, name + '.' + generations[idx]);
434                 CacheInfo info = (CacheInfo) cacheFiles.get(file);
435                 if (info != null) {
436                     if (info.filetype == FILETYPE_CORRUPT)
437                         rmCount--;
438                 }
439             }
440             for (int idx = generationCount - 1; rmCount > 0; idx--, rmCount--) {
441                 File rmFile = new File(parent, name + '.' + generations[idx]);
442                 rmFile.delete();
443                 cacheFiles.remove(rmFile);
444             }
445         }
446     }
447
448     /**
449      * Rename a file.
450      *
451      * @param from The original file.
452      * @param to The new file name.
453      * @throws IOException If the rename failed.
454      */

455     private static void mv(File from, File to) throws IOException {
456         if (!from.renameTo(to)) {
457             throw new IOException("rename failed"); //$NON-NLS-1$
458
}
459     }
460
461     /**
462      * Copy a file.
463      *
464      * @throws IOException If the copy failed.
465      */

466     private static void cp(InputStream in, OutputStream out, int truncateSize) throws IOException {
467         try {
468             int length = in.available();
469             if (truncateSize > length)
470                 length = 0;
471             else
472                 length -= truncateSize;
473             if (length > 0) {
474                 int bufferSize;
475                 if (length > BUF_SIZE) {
476                     bufferSize = BUF_SIZE;
477                 } else {
478                     bufferSize = length;
479                 }
480
481                 byte buffer[] = new byte[bufferSize];
482                 int size = 0;
483                 int count;
484                 while ((count = in.read(buffer, 0, length)) > 0) {
485                     if ((size + count) >= length)
486                         count = length - size;
487                     out.write(buffer, 0, count);
488                     size += count;
489                 }
490             }
491         } finally {
492             try {
493                 in.close();
494             } catch (IOException e) {/*ignore*/
495             }
496             out.close();
497         }
498     }
499
500     /**
501      * Answers a boolean indicating whether or not the specified reliable file
502      * exists on the underlying file system. This call only returns if a file
503      * exists and not if the file contents are valid.
504      * @param file returns true if the specified reliable file exists; otherwise false is returned
505      *
506      * @return <code>true</code> if the specified reliable file exists,
507      * <code>false</code> otherwise.
508      */

509     public static boolean exists(File file) {
510         String JavaDoc prefix = file.getName() + '.';
511         File parent = new File(file.getParent());
512         int prefixLen = prefix.length();
513         String JavaDoc[] files = parent.list();
514         if (files == null)
515             return false;
516         for (int i = 0; i < files.length; i++) {
517             if (files[i].startsWith(prefix)) {
518                 try {
519                     Integer.parseInt(files[i].substring(prefixLen));
520                     return true;
521                 } catch (NumberFormatException JavaDoc e) {/*ignore*/
522                 }
523             }
524         }
525         return file.exists();
526     }
527
528     /**
529      * Returns the time that the reliable file was last modified. Only the time
530      * of the last file generation is returned.
531      * @param file the file to determine the time of.
532      * @return time the file was last modified (see java.io.File.lastModified()).
533      */

534     public static long lastModified(File file) {
535         int[] generations = getFileGenerations(file);
536         if (generations == null)
537             return 0L;
538         if (generations[0] == 0)
539             return file.lastModified();
540         String JavaDoc name = file.getName();
541         File parent = new File(file.getParent());
542         File newFile = new File(parent, name + '.' + generations[0]);
543         return newFile.lastModified();
544     }
545
546     /**
547      * Returns the time that this ReliableFile was last modified. This method is only valid
548      * after requesting an input stream and the time of the actual input file is returned.
549      *
550      * @return time the file was last modified (see java.io.File.lastModified()) or
551      * 0L if an input stream is not open.
552      */

553     public long lastModified() {
554         if (inputFile != null) {
555             return inputFile.lastModified();
556         }
557         return 0L;
558     }
559
560     /**
561      * Returns the a version number of a reliable managed file. The version can be expected
562      * to be unique for each successfule file update.
563      *
564      * @param file the file to determine the version of.
565      * @return a unique version of this current file. A value of -1 indicates the file does
566      * not exist or an error occurred.
567      */

568     public static int lastModifiedVersion(File file) {
569         int[] generations = getFileGenerations(file);
570         if (generations == null)
571             return -1;
572         return generations[0];
573     }
574
575     /**
576      * Delete the specified reliable file on the underlying file system.
577      * @param deleteFile the reliable file to delete
578      *
579      * @return <code>true</code> if the specified reliable file was deleted,
580      * <code>false</code> otherwise.
581      */

582     public static boolean delete(File deleteFile) {
583         int[] generations = getFileGenerations(deleteFile);
584         if (generations == null)
585             return false;
586         String JavaDoc name = deleteFile.getName();
587         File parent = new File(deleteFile.getParent());
588         synchronized (cacheFiles) {
589             for (int idx = 0; idx < generations.length; idx++) {
590                 // base files (.0 in generations[]) will never be deleted
591
if (generations[idx] == 0)
592                     continue;
593                 File file = new File(parent, name + '.' + generations[idx]);
594                 if (file.exists()) {
595                     file.delete();
596                 }
597                 cacheFiles.remove(file);
598             }
599         }
600         return true;
601     }
602
603     /**
604      * Get a list of ReliableFile base names in a given directory. Only files with a valid
605      * ReliableFile generation are included.
606      * @param directory the directory to inquire.
607      * @return an array of ReliableFile names in the directory.
608      * @throws IOException if an error occurs.
609      */

610     public static String JavaDoc[] getBaseFiles(File directory) throws IOException {
611         if (!directory.isDirectory())
612             throw new IOException("Not a valid directory"); //$NON-NLS-1$
613
String JavaDoc files[] = directory.list();
614         HashSet list = new HashSet(files.length / 2);
615         for (int idx = 0; idx < files.length; idx++) {
616             String JavaDoc file = files[idx];
617             int pos = file.lastIndexOf('.');
618             if (pos == -1)
619                 continue;
620             String JavaDoc ext = file.substring(pos + 1);
621             int generation = 0;
622             try {
623                 generation = Integer.parseInt(ext);
624             } catch (NumberFormatException JavaDoc e) {/*skip*/
625             }
626             if (generation == 0)
627                 continue;
628             String JavaDoc base = file.substring(0, pos);
629             list.add(base);
630         }
631         files = new String JavaDoc[list.size()];
632         int idx = 0;
633         for (Iterator iter = list.iterator(); iter.hasNext();) {
634             files[idx++] = (String JavaDoc) iter.next();
635         }
636         return files;
637     }
638
639     /**
640      * Delete any old excess generations of a given reliable file.
641      * @param base realible file.
642      */

643     public static void cleanupGenerations(File base) {
644         ReliableFile rf = new ReliableFile(base);
645         int[] generations = getFileGenerations(base);
646         rf.cleanup(generations, false);
647         lastGenerationFile = null;
648         lastGenerations = null;
649     }
650
651     /**
652      * Inform ReliableFile that a file has been updated outside of
653      * ReliableFile.
654      * @param file
655      */

656     public static void fileUpdated(File file) {
657         lastGenerationFile = null;
658         lastGenerations = null;
659     }
660
661     /**
662      * Append a checksum value to the end of an output stream.
663      * @param out the output stream.
664      * @param checksum the checksum value to append to the file.
665      * @throws IOException if a write error occurs.
666      */

667     protected void writeChecksumSignature(OutputStream out, Checksum JavaDoc checksum) throws IOException {
668         // tag on our signature and checksum
669
out.write(ReliableFile.identifier1);
670         out.write(intToHex((int) checksum.getValue()));
671         out.write(ReliableFile.identifier2);
672     }
673
674     /**
675      * Returns the size of the ReliableFile signature + CRC at the end of the file.
676      * This method should be called only after calling getInputStream() or
677      * getOutputStream() methods.
678      *
679      * @return <code>int</code> size of the ReliableFIle signature + CRC appended
680      * to the end of the file.
681      * @throws IOException if getInputStream() or getOutputStream has not been
682      * called.
683      */

684     protected int getSignatureSize() throws IOException {
685         if (inputFile != null) {
686             CacheInfo info;
687             synchronized (cacheFiles) {
688                 info = (CacheInfo) cacheFiles.get(inputFile);
689             }
690             if (info != null) {
691                 switch (info.filetype) {
692                     case FILETYPE_VALID :
693                     case FILETYPE_CORRUPT :
694                         return 16;
695                     case FILETYPE_NOSIGNATURE :
696                         return 0;
697                 }
698             }
699         }
700         throw new IOException("ReliableFile signature size is unknown"); //$NON-NLS-1$
701
}
702
703     /**
704      * Returns a Checksum object for the current file contents. This method
705      * should be called only after calling getInputStream() or
706      * getOutputStream() methods.
707      *
708      * @return Object implementing Checksum interface initialized to the
709      * current file contents.
710      * @throws IOException if getOutputStream for append has not been called.
711      */

712     protected Checksum JavaDoc getFileChecksum() throws IOException {
713         if (appendChecksum == null)
714             throw new IOException("Checksum is invalid!"); //$NON-NLS-1$
715
return appendChecksum;
716     }
717
718     /**
719      * Create a checksum implementation used by ReliableFile.
720      *
721      * @return Object implementing Checksum interface used to calculate
722      * a reliable file checksum
723      */

724     protected Checksum JavaDoc getChecksumCalculator() {
725         // Using CRC32 because Adler32 isn't in the eeMinimum library.
726
return new CRC32 JavaDoc();
727     }
728
729     /**
730      * Determine if a File is a valid ReliableFile
731      *
732      * @return <code>true</code> if the file is a valid ReliableFile
733      * @throws IOException If an error occurs verifying the file.
734      */

735     private int getStreamType(InputStream is, Checksum JavaDoc crc) throws IOException {
736         boolean markSupported = is.markSupported();
737         if (markSupported)
738             is.mark(is.available());
739         try {
740             int len = is.available();
741             if (len < 16) {
742                 if (crc != null) {
743                     byte data[] = new byte[16];
744                     int num = is.read(data);
745                     if (num > 0)
746                         crc.update(data, 0, num);
747                 }
748                 return FILETYPE_NOSIGNATURE;
749             }
750             len -= 16;
751
752             int pos = 0;
753             byte data[] = new byte[BUF_SIZE];
754
755             while (pos < len) {
756                 int read = data.length;
757                 if (pos + read > len)
758                     read = len - pos;
759
760                 int num = is.read(data, 0, read);
761                 if (num == -1) {
762                     throw new IOException("Unable to read entire file."); //$NON-NLS-1$
763
}
764
765                 crc.update(data, 0, num);
766                 pos += num;
767             }
768
769             int num = is.read(data); // read last 16-byte signature
770
if (num != 16) {
771                 throw new IOException("Unable to read entire file."); //$NON-NLS-1$
772
}
773
774             int i, j;
775             for (i = 0; i < 4; i++)
776                 if (identifier1[i] != data[i]) {
777                     crc.update(data, 0, 16); // update crc w/ sig bytes
778
return FILETYPE_NOSIGNATURE;
779                 }
780             for (i = 0, j = 12; i < 4; i++, j++)
781                 if (identifier2[i] != data[j]) {
782                     crc.update(data, 0, 16); // update crc w/ sig bytes
783
return FILETYPE_NOSIGNATURE;
784                 }
785             long crccmp;
786             try {
787                 crccmp = Long.valueOf(new String JavaDoc(data, 4, 8, "UTF-8"), 16).longValue(); //$NON-NLS-1$
788
} catch (UnsupportedEncodingException e) {
789                 crccmp = Long.valueOf(new String JavaDoc(data, 4, 8), 16).longValue();
790             }
791             if (crccmp == crc.getValue()) {
792                 return FILETYPE_VALID;
793             }
794             // do not update CRC
795
return FILETYPE_CORRUPT;
796         } finally {
797             if (markSupported)
798                 is.reset();
799         }
800     }
801
802     private static byte[] intToHex(int l) {
803         byte[] buffer = new byte[8];
804         int count = 8;
805
806         do {
807             int ch = (l & 0xf);
808             if (ch > 9)
809                 ch = ch - 10 + 'a';
810             else
811                 ch += '0';
812             buffer[--count] = (byte) ch;
813             l >>= 4;
814         } while (count > 0);
815         return buffer;
816     }
817
818     private class CacheInfo {
819         int filetype;
820         Checksum JavaDoc checksum;
821         long timeStamp;
822
823         CacheInfo(int filetype, Checksum JavaDoc checksum, long timeStamp) {
824             this.filetype = filetype;
825             this.checksum = checksum;
826             this.timeStamp = timeStamp;
827         }
828     }
829 }
830
Popular Tags