Start line:  
End line:  

Snippet Preview

Snippet HTML Code

Stack Overflow Questions
Copyright (c) 2003, 2007 IBM Corporation and others. All rights reserved. This program and the accompanying materials are made available under the terms of the Eclipse Public License v1.0 which accompanies this distribution, and is available at http://www.eclipse.org/legal/epl-v10.html Contributors: IBM Corporation - initial API and implementation /
 
 
 package org.eclipse.osgi.framework.internal.reliablefile;
 
 import java.io.*;
 import java.util.*;
ReliableFile class used by ReliableFileInputStream and ReliableOutputStream. This class encapsulates all the logic for reliable file support.
 
 public class ReliableFile {
Open mask. Obtain the best data stream available. If the primary data contents are invalid (corrupt, missing, etc.), the data for a prior version may be used. An IOException will be thrown if a valid data content can not be determined. This is mutually exclusive with OPEN_FAIL_ON_PRIMARY.
 
 	public static final int OPEN_BEST_AVAILABLE = 0;
Open mask. Obtain only the data stream for the primary file where any other version will not be valid. This should be used for data streams that are managed as a group as a prior contents may not match the other group data. If the primary data is not invalid, a IOException will be thrown. This is mutually exclusive with OPEN_BEST_AVAILABLE.
 
 	public static final int OPEN_FAIL_ON_PRIMARY = 1;

Use the last generation of the file
 
 	public static final int GENERATION_LATEST = 0;
Keep infinite backup files
 
 	public static final int GENERATIONS_INFINITE = 0;

Extension of tmp file used during writing. A reliable file with this extension should never be directly used.
 
 	public static final String tmpExt = ".tmp"//$NON-NLS-1$
 
Property to set the maximum size of a file that will be buffered. When calculating a ReliableFile checksum, if the file is this size or small, ReliableFile will read the file contents into a BufferedInputStream and reset the buffer to avoid having to read the data from the media twice. Since this method require memory for storage, it is limited to this size. The default maximum is 128-KBytes.
 
 	public static final String PROP_MAX_BUFFER = "osgi.reliableFile.maxInputStreamBuffer"//$NON-NLS-1$
 	
The maximum number of generations to keep as backup files in case last generation file is determined to be invalid.
 
 	public static final String PROP_MAX_GENERATIONS = "osgi.ReliableFile.maxGenerations"//$NON-NLS-1$
 	
 
 	public static final String PROP_OSGI_LOCKING = "osgi.locking"//$NON-NLS-1$
 
 	private static final int FILETYPE_VALID = 0;
 	private static final int FILETYPE_CORRUPT = 1;
 	private static final int FILETYPE_NOSIGNATURE = 2;
 
 	private static final byte identifier1[] = {'.''c''r''c'};
 	private static final byte identifier2[] = {'.''v''1''\n'};
 
 	private static final int BUF_SIZE = 4096;
 	private static final int maxInputStreamBuffer;
 	private static final int defaultMaxGenerations;
 	private static final boolean fileSharing;
 	//our cache of the last looked up generations for a file
 	private static File lastGenerationFile = null;
 	private static int[] lastGenerations = null;
 	private static final Object lastGenerationLock = new Object();
 
 	static {
 		String prop = FrameworkProperties.getProperty();
 		int tmpMaxInput = 128 * 1024; //128k
 		if (prop != null) {
 			try {
 				tmpMaxInput = Integer.parseInt(prop);
catch (NumberFormatException e) {/*ignore*/
			}
		}
		 = tmpMaxInput;
		int tmpDefaultMax = 2;
		prop = FrameworkProperties.getProperty();
		if (prop != null) {
			try {
				tmpDefaultMax = Integer.parseInt(prop);
catch (NumberFormatException e) {/*ignore*/
			}
		}
		 = tmpDefaultMax;
		prop = FrameworkProperties.getProperty();
		boolean tmpFileSharing = true;
		if (prop != null) {
			if (prop.equals("none")) { //$NON-NLS-1$
				tmpFileSharing = false;
			}
		}
		 = tmpFileSharing;
	}

File object for original reference file
	private File referenceFile;

List of checksum file objects: File => specific ReliableFile generation
	private static Hashtable cacheFiles = new Hashtable(20);
	private File inputFile = null;
	private File outputFile = null;
	private Checksum appendChecksum = null;

ReliableFile object factory. This method is called by ReliableFileInputStream and ReliableFileOutputStream to get a ReliableFile object for a target file. If the object is in the cache, the cached copy is returned. Otherwise a new ReliableFile object is created and returned. The use count of the returned ReliableFile object is incremented.

Parameters:
name Name of the target file.
Returns:
A ReliableFile object for the target file.
Throws:
java.io.IOException If the target file is a directory.
	static ReliableFile getReliableFile(String namethrows IOException {
		return getReliableFile(new File(name));
	}

ReliableFile object factory. This method is called by ReliableFileInputStream and ReliableFileOutputStream to get a ReliableFile object for a target file. If the object is in the cache, the cached copy is returned. Otherwise a new ReliableFile object is created and returned. The use count of the returned ReliableFile object is incremented.

Parameters:
file File object for the target file.
Returns:
A ReliableFile object for the target file.
Throws:
java.io.IOException If the target file is a directory.
	static ReliableFile getReliableFile(File filethrows IOException {
		if (file.isDirectory()) {
			throw new FileNotFoundException("file is a directory"); //$NON-NLS-1$
		}
		return new ReliableFile(file);
	}

Private constructor used by the static getReliableFile factory methods.

Parameters:
file File object for the target file.
	private ReliableFile(File file) {
		 = file;
	}
	private static int[] getFileGenerations(File file) {
		if (!) {
			synchronized () {
				if ( != null) {
					//shortcut maybe, only if filesharing is not supported
				}
			}
		}
		int[] generations = null;
		try {
			String name = file.getName();
			String prefix = name + '.';
			int prefixLen = prefix.length();
			File parent = new File(file.getParent());
			String[] files = parent.list();
			if (files == null)
				return null;
			if (file.exists())
				list.add(new Integer(0)); //base file exists
			for (int i = 0; i < files.lengthi++) {
				if (files[i].startsWith(prefix)) {
					try {
						int id = Integer.parseInt(files[i].substring(prefixLen));
						list.add(new Integer(id));
catch (NumberFormatException e) {/*ignore*/
					}
				}
			}
			if (list.size() == 0)
				return null;
			Object[] array = list.toArray();
			Arrays.sort(array);
			generations = new int[array.length];
			for (int i = 0, j = array.length - 1; i < array.lengthi++, j--) {
				generations[i] = ((Integerarray[j]).intValue();
			}
			return generations;
finally {
			if (!) {
				synchronized () {
					 = generations;
				}
			}
		}
	}

Returns an InputStream object for reading the target file.

Parameters:
generation the maximum generation to evaluate
openMask mask used to open data. are invalid (corrupt, missing, etc).
Returns:
An InputStream object which can be used to read the target file.
Throws:
java.io.IOException If an error occurs preparing the file.
	InputStream getInputStream(int generationint openMaskthrows IOException {
		if ( != null) {
			throw new IOException("Input stream already open"); //$NON-NLS-1$
		}
		int[] generations = getFileGenerations();
		if (generations == null) {
			throw new FileNotFoundException("File not found"); //$NON-NLS-1$
		}
		File parent = new File(.getParent());
		boolean failOnPrimary = (openMask & ) != 0;
		if (failOnPrimary && generation == )
			generation = generations[0];
		File textFile = null;
		InputStream textIS = null;
		for (int idx = 0; idx < generations.lengthidx++) {
			if (generation != 0) {
				if (generations[idx] > generation || (failOnPrimary && generations[idx] != generation))
					continue;
			}
			File file;
			if (generations[idx] != 0)
				file = new File(parentname + '.' + generations[idx]);
			else
				file = ;
			InputStream is = null;
			CacheInfo info;
			synchronized () {
				info = (CacheInfo.get(file);
				long timeStamp = file.lastModified();
				if (info == null || timeStamp != info.timeStamp) {
					try {
						is = new FileInputStream(file);
							is = new BufferedInputStream(is);
						int filetype = getStreamType(iscksum);
						info = new CacheInfo(filetypecksumtimeStamp);
						.put(fileinfo);
catch (IOException e) {/*ignore*/
					}
				}
			}
			// if looking for a specific generation only, only look at one
			//  and return the result.
			if (failOnPrimary) {
				if (info != null && info.filetype == ) {
					 = file;
					if (is != null)
						return is;
					return new FileInputStream(file);
				}
				throw new IOException("ReliableFile is corrupt"); //$NON-NLS-1$
			}
			// if error, ignore this file & try next
			if (info == null)
				continue;
			// we're  not looking for a specific version, so let's pick the best case
			switch (info.filetype) {
					 = file;
					if (is != null)
						return is;
					return new FileInputStream(file);
					if (textFile == null) {
						textFile = file;
						textIS = is;
					}
					break;
			}
		}
		// didn't find any valid files, if there are any plain text files
		//  use it instead
		if (textFile != null) {
			 = textFile;
			if (textIS != null)
				return textIS;
			return new FileInputStream(textFile);
		}
		throw new IOException("ReliableFile is corrupt"); //$NON-NLS-1$
	}

Returns an OutputStream object for writing the target file.

Parameters:
append append new data to an existing file.
appendGeneration specific generation of file to append from.
Returns:
An OutputStream object which can be used to write the target file.
Throws:
java.io.IOException IOException If an error occurs preparing the file.
	OutputStream getOutputStream(boolean appendint appendGenerationthrows IOException {
		if ( != null)
			throw new IOException("Output stream is already open"); //$NON_NLS-1$ //$NON-NLS-1$
		File parent = new File(.getParent());
		File tmpFile = File.createTempFile(nameparent);
		if (!append) {
			OutputStream os = new FileOutputStream(tmpFile);
			 = tmpFile;
			return os;
		}
		try {
			is = getInputStream(appendGeneration);
catch (FileNotFoundException e) {
			OutputStream os = new FileOutputStream(tmpFile);
			 = tmpFile;
			return os;
		}
		try {
			 = info.checksum;
			OutputStream os = new FileOutputStream(tmpFile);
			if (info.filetype == ) {
				cp(isos, 0);
else {
				cp(isos, 16); // don't copy checksum signature
			}
			 = tmpFile;
			return os;
finally {
		}
	}

Close the target file for reading.

Parameters:
checksum Checksum of the file contents
Throws:
java.io.IOException If an error occurs closing the file.
	void closeOutputFile(Checksum checksumthrows IOException {
		if ( == null)
			throw new IOException("Output stream is not open"); //$NON-NLS-1$
		int[] generations = getFileGenerations();
		File parent = new File(.getParent());
		File newFile;
		if (generations == null)
			newFile = new File(parentname + ".1"); //$NON-NLS-1$
		else
			newFile = new File(parentname + '.' + (generations[0] + 1));
		mv(newFile); // throws IOException if problem
		 = null;
		CacheInfo info = new CacheInfo(checksumnewFile.lastModified());
		.put(newFileinfo);
		cleanup(generationstrue);
	}

Abort the current output stream and do not update the reliable file table.
	void abortOutputFile() {
		if ( == null)
			return;
		 = null;
	}
		return ;
	}

Close the target file for reading.
	void closeInputFile() {
		 = null;
	}
	private void cleanup(int[] generationsboolean generationAdded) {
		if (generations == null)
			return;
		File parent = new File(.getParent());
		int generationCount = generations.length;
		// if a base file is in the list (0 in generations[]), we will 
		//  never delete these files, so don't count them in the old
		//  generation count.
		if (generations[generationCount - 1] == 0)
			generationCount--;
		// assume here that the int[] does not include a file just created
		int rmCount = generationCount - ;
		if (generationAdded)
			rmCount++;
		if (rmCount < 1)
			return;
		synchronized () {
			// first, see if any of the files not deleted are known to
			//  be corrupt. If so, be sure to keep not to delete good
			//  backup files.
			for (int idx = 0, count = generationCount - rmCountidx < countidx++) {
				File file = new File(parentname + '.' + generations[idx]);
				CacheInfo info = (CacheInfo.get(file);
				if (info != null) {
					if (info.filetype == )
						rmCount--;
				}
			}
			for (int idx = generationCount - 1; rmCount > 0; idx--, rmCount--) {
				File rmFile = new File(parentname + '.' + generations[idx]);
				rmFile.delete();
			}
		}
	}

Rename a file.

Parameters:
from The original file.
to The new file name.
Throws:
java.io.IOException If the rename failed.
	private static void mv(File fromFile tothrows IOException {
		if (!from.renameTo(to)) {
			throw new IOException("rename failed"); //$NON-NLS-1$
		}
	}

Copy a file.

Throws:
java.io.IOException If the copy failed.
	private static void cp(InputStream inOutputStream outint truncateSizethrows IOException {
		try {
			int length = in.available();
			if (truncateSize > length)
				length = 0;
			else
				length -= truncateSize;
			if (length > 0) {
				int bufferSize;
				if (length > ) {
					bufferSize = ;
else {
					bufferSize = length;
				}
				byte buffer[] = new byte[bufferSize];
				int size = 0;
				int count;
				while ((count = in.read(buffer, 0, length)) > 0) {
					if ((size + count) >= length)
						count = length - size;
					out.write(buffer, 0, count);
					size += count;
				}
			}
finally {
			try {
				in.close();
catch (IOException e) {/*ignore*/
			}
			out.close();
		}
	}

Answers a boolean indicating whether or not the specified reliable file exists on the underlying file system. This call only returns if a file exists and not if the file contents are valid.

Parameters:
file returns true if the specified reliable file exists; otherwise false is returned
Returns:
true if the specified reliable file exists, false otherwise.
	public static boolean exists(File file) {
		String prefix = file.getName() + '.';
		File parent = new File(file.getParent());
		int prefixLen = prefix.length();
		String[] files = parent.list();
		if (files == null)
			return false;
		for (int i = 0; i < files.lengthi++) {
			if (files[i].startsWith(prefix)) {
				try {
					Integer.parseInt(files[i].substring(prefixLen));
					return true;
catch (NumberFormatException e) {/*ignore*/
				}
			}
		}
		return file.exists();
	}

Returns the time that the reliable file was last modified. Only the time of the last file generation is returned.

Parameters:
file the file to determine the time of.
Returns:
time the file was last modified (see java.io.File.lastModified()).
	public static long lastModified(File file) {
		int[] generations = getFileGenerations(file);
		if (generations == null)
			return 0L;
		if (generations[0] == 0)
			return file.lastModified();
		String name = file.getName();
		File parent = new File(file.getParent());
		File newFile = new File(parentname + '.' + generations[0]);
		return newFile.lastModified();
	}

Returns the time that this ReliableFile was last modified. This method is only valid after requesting an input stream and the time of the actual input file is returned.

Returns:
time the file was last modified (see java.io.File.lastModified()) or 0L if an input stream is not open.
	public long lastModified() {
		if ( != null) {
		}
		return 0L;
	}

Returns the a version number of a reliable managed file. The version can be expected to be unique for each successful file update.

Parameters:
file the file to determine the version of.
Returns:
a unique version of this current file. A value of -1 indicates the file does not exist or an error occurred.
	public static int lastModifiedVersion(File file) {
		int[] generations = getFileGenerations(file);
		if (generations == null)
			return -1;
		return generations[0];
	}

Delete the specified reliable file on the underlying file system.

Parameters:
deleteFile the reliable file to delete
Returns:
true if the specified reliable file was deleted, false otherwise.
	public static boolean delete(File deleteFile) {
		int[] generations = getFileGenerations(deleteFile);
		if (generations == null)
			return false;
		String name = deleteFile.getName();
		File parent = new File(deleteFile.getParent());
		synchronized () {
			for (int idx = 0; idx < generations.lengthidx++) {
				// base files (.0 in generations[]) will never be deleted
				if (generations[idx] == 0)
					continue;
				File file = new File(parentname + '.' + generations[idx]);
				if (file.exists()) {
					file.delete();
				}
			}
		}
		return true;
	}

Get a list of ReliableFile base names in a given directory. Only files with a valid ReliableFile generation are included.

Parameters:
directory the directory to inquire.
Returns:
an array of ReliableFile names in the directory.
Throws:
java.io.IOException if an error occurs.
	public static String[] getBaseFiles(File directorythrows IOException {
		if (!directory.isDirectory())
			throw new IOException("Not a valid directory"); //$NON-NLS-1$
		String files[] = directory.list();
		HashSet list = new HashSet(files.length / 2);
		for (int idx = 0; idx < files.lengthidx++) {
			String file = files[idx];
			int pos = file.lastIndexOf('.');
			if (pos == -1)
				continue;
			String ext = file.substring(pos + 1);
			int generation = 0;
			try {
				generation = Integer.parseInt(ext);
catch (NumberFormatException e) {/*skip*/
			}
			if (generation == 0)
				continue;
			String base = file.substring(0, pos);
			list.add(base);
		}
		files = new String[list.size()];
		int idx = 0;
		for (Iterator iter = list.iterator(); iter.hasNext();) {
			files[idx++] = (Stringiter.next();
		}
		return files;
	}

Delete any old excess generations of a given reliable file.

Parameters:
base realible file.
	public static void cleanupGenerations(File base) {
		ReliableFile rf = new ReliableFile(base);
		int[] generations = getFileGenerations(base);
		rf.cleanup(generationsfalse);
	}

Inform ReliableFile that a file has been updated outside of ReliableFile.

Parameters:
file
	public static void fileUpdated(File file) {
	}

Append a checksum value to the end of an output stream.

Parameters:
out the output stream.
checksum the checksum value to append to the file.
Throws:
java.io.IOException if a write error occurs.
	void writeChecksumSignature(OutputStream outChecksum checksumthrows IOException {
		// tag on our signature and checksum
		out.write(intToHex((intchecksum.getValue()));
	}

Returns the size of the ReliableFile signature + CRC at the end of the file. This method should be called only after calling getInputStream() or getOutputStream() methods.

Returns:
int size of the ReliableFIle signature + CRC appended to the end of the file.
Throws:
java.io.IOException if getInputStream() or getOutputStream has not been called.
	int getSignatureSize() throws IOException {
		if ( != null) {
			if (info != null) {
				switch (info.filetype) {
						return 16;
						return 0;
				}
			}
		}
		throw new IOException("ReliableFile signature size is unknown"); //$NON-NLS-1$
	}

Returns a Checksum object for the current file contents. This method should be called only after calling getInputStream() or getOutputStream() methods.

Returns:
Object implementing Checksum interface initialized to the current file contents.
Throws:
java.io.IOException if getOutputStream for append has not been called.
		if ( == null)
			throw new IOException("Checksum is invalid!"); //$NON-NLS-1$
	}

Create a checksum implementation used by ReliableFile.

Returns:
Object implementing Checksum interface used to calculate a reliable file checksum
		// Using CRC32 because Adler32 isn't in the eeMinimum library.
		return new CRC32();
	}

Determine if a File is a valid ReliableFile

Returns:
true if the file is a valid ReliableFile
Throws:
java.io.IOException If an error occurs verifying the file.
	private int getStreamType(InputStream isChecksum crcthrows IOException {
		boolean markSupported = is.markSupported();
		if (markSupported)
			is.mark(is.available());
		try {
			int len = is.available();
			if (len < 16) {
				if (crc != null) {
					byte data[] = new byte[16];
					int num = is.read(data);
					if (num > 0)
						crc.update(data, 0, num);
				}
			}
			len -= 16;
			int pos = 0;
			byte data[] = new byte[];
			while (pos < len) {
				int read = data.length;
				if (pos + read > len)
					read = len - pos;
				int num = is.read(data, 0, read);
				if (num == -1) {
					throw new IOException("Unable to read entire file."); //$NON-NLS-1$
				}
				crc.update(data, 0, num);
				pos += num;
			}
			int num = is.read(data); // read last 16-byte signature
			if (num != 16) {
				throw new IOException("Unable to read entire file."); //$NON-NLS-1$
			}
			int ij;
			for (i = 0; i < 4; i++)
				if ([i] != data[i]) {
					crc.update(data, 0, 16); // update crc w/ sig bytes
				}
			for (i = 0, j = 12; i < 4; i++, j++)
				if ([i] != data[j]) {
					crc.update(data, 0, 16); // update crc w/ sig bytes
				}
			long crccmp;
			try {
				crccmp = Long.valueOf(new String(data, 4, 8, "UTF-8"), 16).longValue(); //$NON-NLS-1$
				crccmp = Long.valueOf(new String(data, 4, 8), 16).longValue();
			}
			if (crccmp == crc.getValue()) {
			}
			// do not update CRC
finally {
			if (markSupported)
				is.reset();
		}
	}
	private static byte[] intToHex(int l) {
		byte[] buffer = new byte[8];
		int count = 8;
		do {
			int ch = (l & 0xf);
			if (ch > 9)
				ch = ch - 10 + 'a';
			else
				ch += '0';
			buffer[--count] = (bytech;
			l >>= 4;
while (count > 0);
		return buffer;
	}
	private class CacheInfo {
		long timeStamp;
		CacheInfo(int filetypeChecksum checksumlong timeStamp) {
			this. = filetype;
			this. = checksum;
			this. = timeStamp;
		}
	}
New to GrepCode? Check out our FAQ X