Refactored filesystem api

* Removed Readable- and WritableBytes
** Replaced with Readable-/WritableByteChannel
** Methods now integrated in Readable- and WritableFile
** Replaced positioned read/write by method to set the position
This commit is contained in:
Markus Kreusch
2015-12-28 20:53:01 +01:00
parent 1804a52740
commit 356ea5c319
14 changed files with 155 additions and 131 deletions

View File

@@ -1,4 +1,4 @@
package org.cryptomator.commons.test.matcher;
package org.cryptomator.common.test.matcher;
import org.hamcrest.Matcher;
import org.hamcrest.Matchers;

View File

@@ -3,7 +3,7 @@
* This file is licensed under the terms of the MIT license.
* See the LICENSE.txt file for more info.
******************************************************************************/
package org.cryptomator.commons.test.matcher;
package org.cryptomator.common.test.matcher;
import java.util.function.Function;

View File

@@ -39,7 +39,7 @@ public class OpenFiles implements AutoCloseable {
}
static void cleanup(Collection<ReadableFile> readableFiles, Collection<WritableFile> writableFiles) {
Iterator<AutoCloseable> iterator = Stream.concat(readableFiles.stream(), writableFiles.stream()).iterator();
Iterator<? extends AutoCloseable> iterator = Stream.concat(readableFiles.stream(), writableFiles.stream()).iterator();
UncheckedIOException firstException = null;
while (iterator.hasNext()) {
AutoCloseable openFile = iterator.next();

View File

@@ -1,46 +0,0 @@
/*******************************************************************************
* Copyright (c) 2015 Markus Kreusch
* This file is licensed under the terms of the MIT license.
* See the LICENSE.txt file for more info.
******************************************************************************/
package org.cryptomator.filesystem;
import java.io.IOException;
import java.io.UncheckedIOException;
import java.nio.ByteBuffer;
public interface ReadableBytes {
/**
* <p>
* Tries to fill the remaining space in the given byte buffer with data from
* this readable bytes from the current position.
* <p>
* May read less bytes if the end of this readable bytes has been reached.
*
* @param target
* the byte buffer to fill
* @throws UncheckedIOException
* if an {@link IOException} occurs while reading from this
* {@code ReadableBytes}
*/
void read(ByteBuffer target) throws UncheckedIOException;
/**
* <p>
* Tries to fill the remaining space in the given byte buffer with data from
* this readable bytes from the given position.
* <p>
* May read less bytes if the end of this readable bytes has been reached.
*
* @param target
* the byte buffer to fill
* @param position
* the position to read bytes from
* @throws UncheckedIOException
* if an {@link IOException} occurs while reading from this
* {@code ReadableBytes}
*/
void read(ByteBuffer target, long position) throws UncheckedIOException;
}

View File

@@ -5,12 +5,46 @@
******************************************************************************/
package org.cryptomator.filesystem;
import java.io.IOException;
import java.io.UncheckedIOException;
import java.nio.ByteBuffer;
import java.nio.channels.ReadableByteChannel;
public interface ReadableFile extends ReadableBytes, AutoCloseable {
public interface ReadableFile extends ReadableByteChannel {
void copyTo(WritableFile other) throws UncheckedIOException;
/**
* <p>
* Tries to fill the remaining space in the given byte buffer with data from
* this readable bytes from the current position.
* <p>
* May read less bytes if the end of this readable bytes has been reached.
*
* @param target
* the byte buffer to fill
* @return the number of bytes actually read, or {@code -1} if the end of
* file has been reached
* @throws UncheckedIOException
* if an {@link IOException} occurs while reading from this
* {@code ReadableBytes}
*/
int read(ByteBuffer target) throws UncheckedIOException;
/**
* <p>
* Fast-forwards or rewinds the file to the specified position.
* <p>
* Consecutive reads on the file will begin at the new position.
*
* @param position
* the position to set the file to
* @throws UncheckedIOException
* if an {@link IOException} occurs
*
*/
void position(long position) throws UncheckedIOException;
@Override
void close() throws UncheckedIOException;

View File

@@ -1,38 +0,0 @@
/*******************************************************************************
* Copyright (c) 2015 Markus Kreusch
* This file is licensed under the terms of the MIT license.
* See the LICENSE.txt file for more info.
******************************************************************************/
package org.cryptomator.filesystem;
import java.io.IOException;
import java.io.UncheckedIOException;
import java.nio.ByteBuffer;
public interface WritableBytes {
/**
* Writes the data in the given byte buffer to this readable bytes at the
* current position.
*
* @param target
* the byte buffer to use
* @throws UncheckedIOException
* if an {@link IOException} occurs while writing
*/
void write(ByteBuffer source) throws UncheckedIOException;
/**
* Writes the data in the given byte buffer to this readable bytes at the
* given position, overwriting existing content (not inserting).
*
* @param target
* the byte buffer to use
* @param position
* the position to write the data to
* @throws UncheckedIOException
* if an {@link IOException} occurs while writing
*/
void write(ByteBuffer source, int position) throws UncheckedIOException;
}

View File

@@ -5,10 +5,13 @@
******************************************************************************/
package org.cryptomator.filesystem;
import java.io.IOException;
import java.io.UncheckedIOException;
import java.nio.ByteBuffer;
import java.nio.channels.WritableByteChannel;
import java.time.Instant;
public interface WritableFile extends WritableBytes, AutoCloseable {
public interface WritableFile extends WritableByteChannel {
void moveTo(WritableFile other) throws UncheckedIOException;
@@ -24,6 +27,37 @@ public interface WritableFile extends WritableBytes, AutoCloseable {
void truncate() throws UncheckedIOException;
/**
* Writes the data in the given byte buffer to this readable bytes at the
* current position.
*
* @param source
* the byte buffer to use
* @return the number of bytes written, always equal to
* {@code source.remaining()}
* @throws UncheckedIOException
* if an {@link IOException} occurs while writing
*/
int write(ByteBuffer source) throws UncheckedIOException;
/**
* <p>
* Fast-forwards or rewinds the file to the specified position.
* <p>
* Consecutive writes on the file will begin at the new position.
* <p>
* If the position is set to a value greater than the current end of file
* consecutive writes will write data to the given position. The value of
* all bytes between this position and the previous end of file will be
* unspecified.
*
* @param position
* the position to set the file to
* @throws UncheckedIOException
* if an {@link IOException} occurs
*/
void position(long position) throws UncheckedIOException;
/**
* <p>
* Closes this {@code WritableFile} which finally commits all operations

View File

@@ -1,5 +1,6 @@
package org.cryptomator.crypto.fs;
import java.io.UncheckedIOException;
import java.nio.ByteBuffer;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
@@ -14,7 +15,10 @@ import org.cryptomator.io.ByteBuffers;
class CryptoReadableFile implements ReadableFile {
private static final int READ_BUFFER_SIZE = 32 * 1024 + 32; // aligned with encrypted chunk size + MAC size
private static final int READ_BUFFER_SIZE = 32 * 1024 + 32; // aligned with
// encrypted
// chunk size +
// MAC size
private static final ByteBuffer EMPTY_BUFFER = ByteBuffer.allocate(0);
private final ExecutorService executorService = Executors.newSingleThreadScheduledExecutor();
@@ -26,7 +30,8 @@ class CryptoReadableFile implements ReadableFile {
public CryptoReadableFile(FileContentCryptor cryptor, ReadableFile file) {
final int headerSize = cryptor.getHeaderSize();
final ByteBuffer header = ByteBuffer.allocate(headerSize);
file.read(header, 0);
file.position(0);
file.read(header);
header.flip();
this.decryptor = cryptor.createFileContentDecryptor(header);
this.file = file;
@@ -42,31 +47,37 @@ class CryptoReadableFile implements ReadableFile {
}
@Override
public void read(ByteBuffer target) {
public int read(ByteBuffer target) {
try {
if (bufferedCleartext == FileContentCryptor.EOF) {
return -1;
}
int bytesRead = 0;
while (target.remaining() > 0 && bufferedCleartext != FileContentCryptor.EOF) {
bufferCleartext();
readFromBufferedCleartext(target);
bytesRead += readFromBufferedCleartext(target);
}
return bytesRead;
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
throw new RuntimeException(e);
}
}
@Override
public void position(long position) throws UncheckedIOException {
throw new UnsupportedOperationException("Partial read unsupported");
}
private void bufferCleartext() throws InterruptedException {
if (!bufferedCleartext.hasRemaining()) {
bufferedCleartext = decryptor.cleartext();
}
}
private void readFromBufferedCleartext(ByteBuffer target) {
private int readFromBufferedCleartext(ByteBuffer target) {
assert bufferedCleartext != null;
ByteBuffers.copy(bufferedCleartext, target);
}
@Override
public void read(ByteBuffer target, long position) {
throw new UnsupportedOperationException("Partial read not implemented yet.");
return ByteBuffers.copy(bufferedCleartext, target);
}
@Override
@@ -79,6 +90,11 @@ class CryptoReadableFile implements ReadableFile {
}
}
@Override
public boolean isOpen() {
return file.isOpen();
}
@Override
public void close() {
executorService.shutdownNow();
@@ -95,7 +111,7 @@ class CryptoReadableFile implements ReadableFile {
@Override
public Void call() {
file.read(EMPTY_BUFFER, startpos);
file.position(startpos);
int bytesRead = -1;
try {
do {

View File

@@ -32,24 +32,27 @@ class CryptoWritableFile implements WritableFile {
private void writeHeader() {
ByteBuffer header = encryptor.getHeader();
header.rewind();
file.write(header, 0);
file.position(0);
file.write(header);
}
@Override
public void write(ByteBuffer source) {
final ByteBuffer cleartextCopy = ByteBuffer.allocate(source.remaining());
public int write(ByteBuffer source) {
final int size = source.remaining();
final ByteBuffer cleartextCopy = ByteBuffer.allocate(size);
ByteBuffers.copy(source, cleartextCopy);
cleartextCopy.flip();
try {
encryptor.append(cleartextCopy);
file.write(source);
return size;
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
throw new RuntimeException(e);
}
}
@Override
public void write(ByteBuffer source, int position) {
public void position(long position) {
throw new UnsupportedOperationException("Partial write not implemented yet.");
}
@@ -75,7 +78,16 @@ class CryptoWritableFile implements WritableFile {
@Override
public void truncate() {
this.write(ByteBuffer.allocate(0), 0);
/*
* TODO kill writer thread (EOF) and reinitialize CryptoWritableFile
* after truncating the file
*/
throw new UnsupportedOperationException("Truncate not supported yet");
}
@Override
public boolean isOpen() {
return file.isOpen();
}
@Override

View File

@@ -52,31 +52,24 @@ class InMemoryFile extends InMemoryNode implements File, ReadableFile, WritableF
}
@Override
public void read(ByteBuffer target) {
ByteBuffers.copy(content, target);
}
@Override
public void read(ByteBuffer target, long position) {
if (position > Integer.MAX_VALUE) {
throw new IllegalArgumentException("Can not keep files of virtually unlimited size in memory.");
}
public void position(long position) throws UncheckedIOException {
content.position((int) position);
ByteBuffers.copy(content, target);
}
@Override
public void write(ByteBuffer source) {
this.write(source, content.position());
public int read(ByteBuffer target) {
return ByteBuffers.copy(content, target);
}
@Override
public void write(ByteBuffer source, int position) {
public int write(ByteBuffer source) {
assert content != null;
expandContentCapacityIfRequired(position + source.remaining());
content.position(position);
final int initialContentPosition = content.position();
expandContentCapacityIfRequired(initialContentPosition + source.remaining());
content.position(initialContentPosition);
assert content.remaining() >= source.remaining();
content.put(source);
return content.position() - initialContentPosition;
}
private void expandContentCapacityIfRequired(int requiredCapacity) {
@@ -121,7 +114,12 @@ class InMemoryFile extends InMemoryNode implements File, ReadableFile, WritableF
// returning null removes the entry.
return null;
});
assert!this.exists();
assert !this.exists();
}
@Override
public boolean isOpen() {
return lock.isWriteLockedByCurrentThread() || lock.getReadHoldCount() > 0;
}
@Override

View File

@@ -136,7 +136,8 @@ public class InMemoryFileSystemTest {
}
final ByteBuffer readBuf = ByteBuffer.allocate(5);
try (ReadableFile readable = bazFile.openReadable()) {
readable.read(readBuf, 6);
readable.position(6);
readable.read(readBuf);
}
Assert.assertEquals("world", new String(readBuf.array()));
}
@@ -159,7 +160,8 @@ public class InMemoryFileSystemTest {
Assert.assertTrue(test1File.exists());
Assert.assertTrue(test2File.exists());
// copy foo/bar/ to qwe/asd/ (result is qwe/asd/file1.txt & qwe/asd/file2.txt)
// copy foo/bar/ to qwe/asd/ (result is qwe/asd/file1.txt &
// qwe/asd/file2.txt)
fooBarFolder.copyTo(qweAsdFolder);
Assert.assertTrue(qweAsdFolder.exists());
Assert.assertEquals(2, qweAsdFolder.files().count());

View File

@@ -42,11 +42,17 @@ class NioFile extends NioNode implements File {
private class ReadableView implements ReadableFile {
@Override
public void read(ByteBuffer target) throws UncheckedIOException {
public int read(ByteBuffer target) throws UncheckedIOException {
return -1;
}
@Override
public void read(ByteBuffer target, long position) throws UncheckedIOException {
public boolean isOpen() {
return false;
}
@Override
public void position(long position) throws UncheckedIOException {
}
@Override
@@ -62,11 +68,17 @@ class NioFile extends NioNode implements File {
private class WritableView implements WritableFile {
@Override
public void write(ByteBuffer source) throws UncheckedIOException {
public int write(ByteBuffer source) throws UncheckedIOException {
return -1;
}
@Override
public void write(ByteBuffer source, int position) throws UncheckedIOException {
public boolean isOpen() {
return false;
}
@Override
public void position(long position) throws UncheckedIOException {
}
@Override

View File

@@ -1,7 +1,7 @@
package org.cryptomator.filesystem.nio;
import static java.util.stream.Collectors.toList;
import static org.cryptomator.commons.test.matcher.ContainsMatcher.containsInAnyOrder;
import static org.cryptomator.common.test.matcher.ContainsMatcher.containsInAnyOrder;
import static org.cryptomator.filesystem.FolderCreateMode.FAIL_IF_PARENT_IS_MISSING;
import static org.cryptomator.filesystem.FolderCreateMode.INCLUDING_PARENTS;
import static org.cryptomator.filesystem.nio.FilesystemSetupUtils.emptyFilesystem;

View File

@@ -2,7 +2,7 @@ package org.cryptomator.filesystem.nio;
import static org.hamcrest.CoreMatchers.equalTo;
import org.cryptomator.commons.test.matcher.PropertyMatcher;
import org.cryptomator.common.test.matcher.PropertyMatcher;
import org.cryptomator.filesystem.File;
import org.cryptomator.filesystem.Folder;
import org.hamcrest.Matcher;