Feature/filepersister (#115)
* generic file persister with path resolver
This commit is contained in:
parent
f4fdd1e9d9
commit
d01f2abe45
13 changed files with 245 additions and 58 deletions
10
filesystem/src/main/java/com/nytimes/android/external/fs/BarCodePathResolver.java
vendored
Normal file
10
filesystem/src/main/java/com/nytimes/android/external/fs/BarCodePathResolver.java
vendored
Normal file
|
@ -0,0 +1,10 @@
|
|||
package com.nytimes.android.external.fs;
|
||||
|
||||
import com.nytimes.android.external.store.base.impl.BarCode;
|
||||
|
||||
class BarCodePathResolver implements PathResolver<BarCode> {
|
||||
@Override
|
||||
public String resolve(BarCode key) {
|
||||
return key.toString();
|
||||
}
|
||||
}
|
41
filesystem/src/main/java/com/nytimes/android/external/fs/FSReader.java
vendored
Normal file
41
filesystem/src/main/java/com/nytimes/android/external/fs/FSReader.java
vendored
Normal file
|
@ -0,0 +1,41 @@
|
|||
package com.nytimes.android.external.fs;
|
||||
|
||||
import com.nytimes.android.external.fs.filesystem.FileSystem;
|
||||
import com.nytimes.android.external.store.base.DiskRead;
|
||||
|
||||
import java.io.FileNotFoundException;
|
||||
import java.util.concurrent.Callable;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
import okio.BufferedSource;
|
||||
import rx.Observable;
|
||||
|
||||
/**
|
||||
* FSReader is used when persisting from file system
|
||||
* PathResolver will be used in creating file system paths based on cache keys.
|
||||
* Make sure to have keys containing same data resolve to same "path"
|
||||
* @param <T> key type
|
||||
*/
|
||||
public class FSReader<T> implements DiskRead<BufferedSource, T> {
|
||||
final FileSystem fileSystem;
|
||||
final PathResolver<T> pathResolver;
|
||||
|
||||
public FSReader(FileSystem fileSystem, PathResolver<T> pathResolver) {
|
||||
this.fileSystem = fileSystem;
|
||||
this.pathResolver = pathResolver;
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public Observable<BufferedSource> read(final T id) {
|
||||
return fileSystem.exists(pathResolver.resolve(id)) ?
|
||||
Observable.fromCallable(new Callable<BufferedSource>() {
|
||||
@Override
|
||||
public BufferedSource call() throws FileNotFoundException {
|
||||
return fileSystem.read(pathResolver.resolve(id));
|
||||
}
|
||||
}) :
|
||||
Observable.<BufferedSource>empty();
|
||||
}
|
||||
}
|
40
filesystem/src/main/java/com/nytimes/android/external/fs/FSWriter.java
vendored
Normal file
40
filesystem/src/main/java/com/nytimes/android/external/fs/FSWriter.java
vendored
Normal file
|
@ -0,0 +1,40 @@
|
|||
package com.nytimes.android.external.fs;
|
||||
|
||||
import com.nytimes.android.external.fs.filesystem.FileSystem;
|
||||
import com.nytimes.android.external.store.base.DiskWrite;
|
||||
|
||||
import java.util.concurrent.Callable;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
import okio.BufferedSource;
|
||||
import rx.Observable;
|
||||
/**
|
||||
* FSReader is used when persisting to file system
|
||||
* PathResolver will be used in creating file system paths based on cache keys.
|
||||
* Make sure to have keys containing same data resolve to same "path"
|
||||
* @param <T> key type
|
||||
*/
|
||||
public class FSWriter<T> implements DiskWrite<BufferedSource, T> {
|
||||
final FileSystem fileSystem;
|
||||
final PathResolver<T> pathResolver;
|
||||
|
||||
public FSWriter(FileSystem fileSystem, PathResolver<T> pathResolver) {
|
||||
this.fileSystem = fileSystem;
|
||||
this.pathResolver = pathResolver;
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public Observable<Boolean> write(final T barCode, final BufferedSource data) {
|
||||
return Observable.fromCallable(new Callable<Boolean>() {
|
||||
@Nonnull
|
||||
@Override
|
||||
@SuppressWarnings("PMD.SignatureDeclareThrowsException")
|
||||
public Boolean call() throws Exception {
|
||||
fileSystem.write(pathResolver.resolve(barCode), data);
|
||||
return true;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
48
filesystem/src/main/java/com/nytimes/android/external/fs/FileSystemPersister.java
vendored
Normal file
48
filesystem/src/main/java/com/nytimes/android/external/fs/FileSystemPersister.java
vendored
Normal file
|
@ -0,0 +1,48 @@
|
|||
package com.nytimes.android.external.fs;
|
||||
|
||||
import com.nytimes.android.external.fs.filesystem.FileSystem;
|
||||
import com.nytimes.android.external.store.base.Persister;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
import okio.BufferedSource;
|
||||
import rx.Observable;
|
||||
/**
|
||||
* FileSystemPersister is used when persisting to/from file system
|
||||
* PathResolver will be used in creating file system paths based on cache keys.
|
||||
* Make sure to have keys containing same data resolve to same "path"
|
||||
* @param <T> key type
|
||||
*/
|
||||
public final class FileSystemPersister<T> implements Persister<BufferedSource, T> {
|
||||
final FileSystem fileSystem;
|
||||
private final FSReader<T> fileReader;
|
||||
private final FSWriter<T> fileWriter;
|
||||
|
||||
private FileSystemPersister(FileSystem fileSystem, PathResolver<T> pathResolver) {
|
||||
|
||||
fileReader = new FSReader<>(fileSystem, pathResolver);
|
||||
fileWriter = new FSWriter<>(fileSystem, pathResolver);
|
||||
this.fileSystem = fileSystem;
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
public static <T> Persister<BufferedSource, T> create(FileSystem fileSystem,
|
||||
PathResolver<T> pathResolver) {
|
||||
if (fileSystem == null) {
|
||||
throw new IllegalArgumentException("root file cannot be null.");
|
||||
}
|
||||
return new FileSystemPersister<>(fileSystem, pathResolver);
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public Observable<BufferedSource> read(final T id) {
|
||||
return fileReader.read(id);
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public Observable<Boolean> write(final T barCode, final BufferedSource data) {
|
||||
return fileWriter.write(barCode, data);
|
||||
}
|
||||
}
|
15
filesystem/src/main/java/com/nytimes/android/external/fs/PathResolver.java
vendored
Normal file
15
filesystem/src/main/java/com/nytimes/android/external/fs/PathResolver.java
vendored
Normal file
|
@ -0,0 +1,15 @@
|
|||
package com.nytimes.android.external.fs;
|
||||
|
||||
/**
|
||||
* Created by 206847 on 2/8/17.
|
||||
*/
|
||||
|
||||
/**
|
||||
* PathResolver will be used in creating file system paths based on cache keys.
|
||||
* Make sure to have keys containing same data resolve to same "path"
|
||||
* @param <T> Store key/request param type
|
||||
*/
|
||||
public interface PathResolver<T> {
|
||||
|
||||
String resolve(T key);
|
||||
}
|
|
@ -5,42 +5,20 @@ import com.nytimes.android.external.store.base.DiskRead;
|
|||
import com.nytimes.android.external.store.base.RecordState;
|
||||
import com.nytimes.android.external.store.base.impl.BarCode;
|
||||
|
||||
import java.io.FileNotFoundException;
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.inject.Inject;
|
||||
|
||||
import okio.BufferedSource;
|
||||
import rx.Observable;
|
||||
|
||||
import static com.nytimes.android.external.fs.SourcePersister.pathForBarcode;
|
||||
|
||||
public class SourceFileReader implements DiskRead<BufferedSource, BarCode> {
|
||||
public class SourceFileReader extends FSReader<BarCode> implements DiskRead<BufferedSource, BarCode> {
|
||||
|
||||
final FileSystem fileSystem;
|
||||
|
||||
@Inject
|
||||
public SourceFileReader(FileSystem fileSystem) {
|
||||
this.fileSystem = fileSystem;
|
||||
super(fileSystem, new BarCodePathResolver());
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public Observable<BufferedSource> read(@Nonnull final BarCode barCode) {
|
||||
return Observable.fromCallable(new Callable<BufferedSource>() {
|
||||
@Nonnull
|
||||
@Override
|
||||
public BufferedSource call() throws FileNotFoundException {
|
||||
return fileSystem.read(pathForBarcode(barCode));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public boolean exists(@Nonnull BarCode barCode) {
|
||||
return fileSystem.exists(pathForBarcode(barCode));
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
public RecordState getRecordState(@Nonnull BarCode barCode,
|
||||
|
|
|
@ -4,38 +4,12 @@ import com.nytimes.android.external.fs.filesystem.FileSystem;
|
|||
import com.nytimes.android.external.store.base.DiskWrite;
|
||||
import com.nytimes.android.external.store.base.impl.BarCode;
|
||||
|
||||
import java.util.concurrent.Callable;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.inject.Inject;
|
||||
|
||||
import okio.BufferedSource;
|
||||
import rx.Observable;
|
||||
|
||||
import static com.nytimes.android.external.fs.SourcePersister.pathForBarcode;
|
||||
import static okio.Okio.buffer;
|
||||
public class SourceFileWriter extends FSWriter<BarCode> implements DiskWrite<BufferedSource, BarCode> {
|
||||
|
||||
public class SourceFileWriter implements DiskWrite<BufferedSource, BarCode> {
|
||||
|
||||
final FileSystem fileSystem;
|
||||
|
||||
@Inject
|
||||
public SourceFileWriter(FileSystem fileSystem) {
|
||||
this.fileSystem = fileSystem;
|
||||
super(fileSystem, new BarCodePathResolver());
|
||||
}
|
||||
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public Observable<Boolean> write(@Nonnull final BarCode barCode, @Nonnull final BufferedSource data) {
|
||||
return Observable.fromCallable(new Callable<Boolean>() {
|
||||
@Nonnull
|
||||
@Override
|
||||
@SuppressWarnings("PMD.SignatureDeclareThrowsException")
|
||||
public Boolean call() throws Exception {
|
||||
fileSystem.write(pathForBarcode(barCode), buffer(data));
|
||||
return true;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -41,7 +41,7 @@ public class SourcePersister implements Persister<BufferedSource, BarCode>{
|
|||
@Nonnull
|
||||
@Override
|
||||
public Observable<BufferedSource> read(@Nonnull final BarCode barCode) {
|
||||
return sourceFileReader.exists(barCode) ? sourceFileReader.read(barCode) : Observable.<BufferedSource>empty();
|
||||
return sourceFileReader.read(barCode);
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
|
|
|
@ -39,10 +39,11 @@ public final class SourcePersisterFactory {
|
|||
* persistence {@link com.nytimes.android.external.fs.filesystem.FileSystem}.
|
||||
**/
|
||||
@Nonnull
|
||||
public static Persister<BufferedSource, BarCode> create(@Nonnull FileSystem fileSystem) {
|
||||
public static Persister<BufferedSource, BarCode> create(@Nonnull FileSystem fileSystem) {
|
||||
if (fileSystem == null) {
|
||||
throw new IllegalArgumentException("fileSystem cannot be null.");
|
||||
}
|
||||
return new SourcePersister(fileSystem);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
79
filesystem/src/test/java/com/nytimes/android/external/fs/FilePersisterTest.java
vendored
Normal file
79
filesystem/src/test/java/com/nytimes/android/external/fs/FilePersisterTest.java
vendored
Normal file
|
@ -0,0 +1,79 @@
|
|||
package com.nytimes.android.external.fs;
|
||||
|
||||
import com.nytimes.android.external.fs.filesystem.FileSystem;
|
||||
import com.nytimes.android.external.store.base.Persister;
|
||||
import com.nytimes.android.external.store.base.impl.BarCode;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.rules.ExpectedException;
|
||||
import org.mockito.InOrder;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.util.NoSuchElementException;
|
||||
|
||||
import okio.BufferedSource;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.Mockito.inOrder;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
public class FilePersisterTest {
|
||||
@Rule
|
||||
public ExpectedException expectedException = ExpectedException.none();
|
||||
|
||||
@Mock
|
||||
FileSystem fileSystem;
|
||||
@Mock
|
||||
BufferedSource bufferedSource;
|
||||
|
||||
private final BarCode simple = new BarCode("type", "key");
|
||||
private final String resolvedPath = new BarCodePathResolver().resolve(simple);
|
||||
private Persister<BufferedSource, BarCode> fileSystemPersister;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
fileSystemPersister = FileSystemPersister.create(fileSystem, new BarCodePathResolver());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void readExists() throws FileNotFoundException {
|
||||
when(fileSystem.exists(resolvedPath))
|
||||
.thenReturn(true);
|
||||
when(fileSystem.read(resolvedPath)).thenReturn(bufferedSource);
|
||||
|
||||
BufferedSource returnedValue = fileSystemPersister.read(simple).toBlocking().single();
|
||||
assertThat(returnedValue).isEqualTo(bufferedSource);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void readDoesNotExist() throws FileNotFoundException {
|
||||
expectedException.expect(NoSuchElementException.class);
|
||||
when(fileSystem.exists(resolvedPath))
|
||||
.thenReturn(false);
|
||||
|
||||
fileSystemPersister.read(simple).toBlocking().single();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void writeThenRead() throws IOException {
|
||||
when(fileSystem.read(resolvedPath)).thenReturn(bufferedSource);
|
||||
when(fileSystem.exists(resolvedPath)).thenReturn(true);
|
||||
fileSystemPersister.write(simple, bufferedSource).toBlocking().single();
|
||||
BufferedSource source = fileSystemPersister.read(simple).toBlocking().first();
|
||||
InOrder inOrder = inOrder(fileSystem);
|
||||
inOrder.verify(fileSystem).write(resolvedPath, bufferedSource);
|
||||
inOrder.verify(fileSystem).exists(resolvedPath);
|
||||
inOrder.verify(fileSystem).read(resolvedPath);
|
||||
|
||||
assertThat(source).isEqualTo(bufferedSource);
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -41,9 +41,9 @@ public class RecordPersisterTest {
|
|||
|
||||
@Test
|
||||
public void readExists() throws FileNotFoundException {
|
||||
when(fileSystem.exists(SourcePersister.pathForBarcode(simple)))
|
||||
when(fileSystem.exists(simple.toString()))
|
||||
.thenReturn(true);
|
||||
when(fileSystem.read(SourcePersister.pathForBarcode(simple))).thenReturn(bufferedSource);
|
||||
when(fileSystem.read(simple.toString())).thenReturn(bufferedSource);
|
||||
|
||||
BufferedSource returnedValue = sourcePersister.read(simple).toBlocking().single();
|
||||
assertThat(returnedValue).isEqualTo(bufferedSource);
|
||||
|
|
|
@ -40,9 +40,9 @@ public class SourcePersisterTest {
|
|||
|
||||
@Test
|
||||
public void readExists() throws FileNotFoundException {
|
||||
when(fileSystem.exists(SourcePersister.pathForBarcode(simple)))
|
||||
when(fileSystem.exists(simple.toString()))
|
||||
.thenReturn(true);
|
||||
when(fileSystem.read(SourcePersister.pathForBarcode(simple))).thenReturn(bufferedSource);
|
||||
when(fileSystem.read(simple.toString())).thenReturn(bufferedSource);
|
||||
|
||||
BufferedSource returnedValue = sourcePersister.read(simple).toBlocking().single();
|
||||
assertThat(returnedValue).isEqualTo(bufferedSource);
|
||||
|
|
|
@ -6,6 +6,7 @@ import rx.Observable;
|
|||
|
||||
/**
|
||||
* Interface for fetching data from persister
|
||||
* when implementing also think about implementing PathResolver to ease in creating primary keys
|
||||
*
|
||||
* @param <Raw> data type before parsing
|
||||
*/
|
||||
|
|
Loading…
Reference in a new issue