More responsible (atomic) way to write files in Java – AtomicFileWriter

Did You ever have a situation in which a certain application has corrupted its file (e.g. config) by leaving incomplete or empty file due to an error during writing of the file?

Here is my proposed solution to prevent this and thus make applications more robust.

package com.karoldepka.librelib.android.io;

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;

/** Writes to a file in such a way to ensure that the previous contents
 * of the file will stay intact if the writing does not complete successfully.
 * 
 * Prevents a scenario in which the target file is left corrupted
 * with incomplete (or even empty) content due to failed attempt to write.
 * 
 * @author Karol Depka Pradzinski
 * @license LGPL
 */
public class AtomicFileWriter extends BufferedWriter {

	private final AtomicFileHandler partFileHandler;
	
	public AtomicFileWriter(File file, boolean append)
			throws IOException {
		super(new FileWriter(AtomicFileHandler.getPartFile(file), append));
		partFileHandler = new AtomicFileHandler(file);
		
	}
	
	@Override
	public void close() throws IOException {
		super.close();
		partFileHandler.onClose();
	}

}
package com.karoldepka.librelib.android.io;

import java.io.File;

/**
 * Handles partial/old file for atomic writing.
 * 
 * Logic in this class is separated from AtomicFileWriter to avoid tying it
 * to Writer (as opposed to other uses in e.g. OutputStream).
 * 
 * @author Karol Depka Pradzinski
 * @license LGPL
 */
public class AtomicFileHandler {
	private final File targetFile;
	
	public AtomicFileHandler(File targetFile) {
		this.targetFile = targetFile;
	}
	
	public static File getPartFile(File file) {
		return getFileWithSuffix(file, ".part");
	}

	public static File getFileWithSuffix(File file, String suffix) {
		return new File(file.getParentFile(), file.getName()+suffix);
	}
	
	public void onClose() {
		renamePartFileToTarget();
	}

	public void renamePartFileToTarget() {
		File partFile = getPartFile();
		File fileWithOldSuffix = getFileWithSuffix(targetFile, ".old");
		
		if ( targetFile.exists() ) {
			targetFile.renameTo(fileWithOldSuffix);
		}
		partFile.renameTo(targetFile);
		fileWithOldSuffix.delete();
	}

	public File getPartFile() {
		return getPartFile(targetFile);
	}

}

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s