diff --git a/library/src/main/java/com/ironz/binaryprefs/file/adapter/NioFileAdapter.java b/library/src/main/java/com/ironz/binaryprefs/file/adapter/NioFileAdapter.java index 9d47fe3..9bfebf9 100644 --- a/library/src/main/java/com/ironz/binaryprefs/file/adapter/NioFileAdapter.java +++ b/library/src/main/java/com/ironz/binaryprefs/file/adapter/NioFileAdapter.java @@ -4,6 +4,7 @@ import com.ironz.binaryprefs.file.directory.DirectoryProvider; import java.io.File; +import java.io.IOException; import java.io.RandomAccessFile; import java.nio.MappedByteBuffer; import java.nio.channels.FileChannel; @@ -16,7 +17,7 @@ * If adapter detects backup file it will be replaced * to original file. See {@link #fetchBackupOrOriginal(String)}. */ -public final class NioFileAdapter implements FileAdapter { +public class NioFileAdapter implements FileAdapter { private static final String ZERO_BYTES_MESSAGE = "%s key's value is zero bytes for saving"; @@ -56,10 +57,18 @@ private byte[] fetchBackupOrOriginal(String name) { File backupFile = new File(backupDir, name + BACKUP_EXTENSION); File file = new File(baseDir, name); if (backupFile.exists()) { + restoreBackup(file, backupFile); + } + return fetchInternal(file); + } + + private void restoreBackup(File file, File backupFile) { + if (backupFile.length() == 0) { delete(file); + delete(backupFile); + } else { swap(backupFile, file); } - return fetchInternal(file); } private byte[] fetchInternal(File file) { @@ -99,12 +108,14 @@ private void backupAndSave(String name, byte[] bytes) { } File file = new File(baseDir, name); File backupFile = new File(backupDir, name + BACKUP_EXTENSION); + + ensureExists(file); swap(file, backupFile); saveInternal(file, bytes); delete(backupFile); } - private void saveInternal(File file, byte[] bytes) { + void saveInternal(File file, byte[] bytes) { FileChannel channel = null; RandomAccessFile randomAccessFile = null; try { @@ -130,6 +141,17 @@ private void saveInternal(File file, byte[] bytes) { } } + private void ensureExists(File file) { + if (!file.exists()) { + try { + //noinspection ResultOfMethodCallIgnored + file.createNewFile(); + } catch (IOException e) { + throw new FileOperationException(e); + } + } + } + private void swap(File from, File to) { if (!from.exists()) { return; diff --git a/library/src/test/java/com/ironz/binaryprefs/file/NioFileAdapterTest.java b/library/src/test/java/com/ironz/binaryprefs/file/adapter/NioFileAdapterTest.java similarity index 76% rename from library/src/test/java/com/ironz/binaryprefs/file/NioFileAdapterTest.java rename to library/src/test/java/com/ironz/binaryprefs/file/adapter/NioFileAdapterTest.java index 1c9c2ab..eb57416 100644 --- a/library/src/test/java/com/ironz/binaryprefs/file/NioFileAdapterTest.java +++ b/library/src/test/java/com/ironz/binaryprefs/file/adapter/NioFileAdapterTest.java @@ -1,8 +1,6 @@ -package com.ironz.binaryprefs.file; +package com.ironz.binaryprefs.file.adapter; import com.ironz.binaryprefs.exception.FileOperationException; -import com.ironz.binaryprefs.file.adapter.FileAdapter; -import com.ironz.binaryprefs.file.adapter.NioFileAdapter; import com.ironz.binaryprefs.file.directory.DirectoryProvider; import org.junit.Before; import org.junit.Rule; @@ -26,6 +24,7 @@ public final class NioFileAdapterTest { public final TemporaryFolder folder = new TemporaryFolder(); private FileAdapter fileAdapter; + private FileAdapter failingAdapter; @Before public void setUp() throws Exception { @@ -49,6 +48,14 @@ public File getLockDirectory() { } }; fileAdapter = new NioFileAdapter(directoryProvider); + failingAdapter = new NioFileAdapter(directoryProvider) { + @Override + void saveInternal(File file, byte[] bytes) { + byte[] successBytes = Arrays.copyOf(bytes, 1); + super.saveInternal(file, successBytes); + throw new IllegalStateException(); + } + }; } @Test @@ -114,4 +121,26 @@ public void deleteAll() { assertNotNull(fileAdapter.fetch(FILE_NAME)); fileAdapter.fetch(FILE_NAME_1); } -} \ No newline at end of file + + @Test(expected = FileOperationException.class) + public void fetchNoFile() { + fileAdapter.fetch(FILE_NAME); + } + + @Test(expected = IllegalStateException.class) + public void failOnSave() { + failingAdapter.save(FILE_NAME, bytes); + } + + @Test(expected = FileOperationException.class) + public void recoverAfterFail() { + try { + failingAdapter.save(FILE_NAME, bytes); + throw new AssertionError(); + } catch (IllegalStateException ignored) { + } + + fileAdapter.fetch(FILE_NAME); + } + +}