Skip to content

Commit

Permalink
Implemented NioFile
Browse files Browse the repository at this point in the history
* Implementation of NioFile methods
* Extracted Readable/WritableNioFile into separate classes
** Created SharedFileChannel to allow Readable/WritableNioFile for the
same NioFile to use a single, shared FileChannel
* Added tests for NioFile
* Tests for Readable/WritableNioFile pending
  • Loading branch information
markuskreusch committed Dec 31, 2015
1 parent 806e366 commit 39535d0
Show file tree
Hide file tree
Showing 10 changed files with 838 additions and 72 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@
class Mover {

public static void move(File source, File destination) {
if (source == destination) {
return;
}
try (OpenFiles openFiles = DeadlockSafeFileOpener.withWritable(source).andWritable(destination).open()) {
openFiles.writable(source).moveTo(openFiles.writable(destination));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,13 @@

public interface WritableFile extends WritableByteChannel {

/**
* <p>
* Moves this file including content to another.
* <p>
* Moving a file causes itself and the target to be
* {@link WritableFile#close() closed}.
*/
void moveTo(WritableFile other) throws UncheckedIOException;

void setLastModified(Instant instant) throws UncheckedIOException;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@

import java.io.IOException;
import java.io.UncheckedIOException;
import java.nio.ByteBuffer;
import java.nio.file.Files;
import java.nio.file.Path;
import java.time.Instant;
Expand All @@ -18,101 +17,56 @@
class NioFile extends NioNode implements File {

private final ReentrantReadWriteLock lock = new ReentrantReadWriteLock(true);
private SharedFileChannel sharedChannel;

public NioFile(Optional<NioFolder> parent, Path path) {
super(parent, path);
sharedChannel = new SharedFileChannel(path);
}

SharedFileChannel channel() {
return sharedChannel;
}

public ReentrantReadWriteLock lock() {
return lock;
}

@Override
public ReadableFile openReadable() throws UncheckedIOException {
if (lock.getWriteHoldCount() > 0) {
throw new IllegalStateException("Current thread is currently reading this file");
throw new IllegalStateException("Current thread is currently writing this file");
}
if (lock.getReadHoldCount() > 0) {
throw new IllegalStateException("Current thread is already reading this file");
}
lock.readLock().lock();
return new ReadableView();
return new ReadableNioFile(this);
}

@Override
public WritableFile openWritable() throws UncheckedIOException {
if (lock.getWriteHoldCount() > 0) {
throw new IllegalStateException("Current thread is already writing this file");
}
if (lock.getReadHoldCount() > 0) {
throw new IllegalStateException("Current thread is currently reading this file");
}
lock.readLock().lock();
return new WritableView();
lock.writeLock().lock();
return new WritableNioFile(this);
}

@Override
public boolean exists() throws UncheckedIOException {
return false;
}

private class ReadableView implements ReadableFile {

@Override
public int read(ByteBuffer target) throws UncheckedIOException {
return -1;
}

@Override
public boolean isOpen() {
return false;
}

@Override
public void position(long position) throws UncheckedIOException {
}

@Override
public void copyTo(WritableFile other) throws UncheckedIOException {
}

@Override
public void close() throws UncheckedIOException {
}

return Files.isRegularFile(path);
}

private class WritableView implements WritableFile {

@Override
public int write(ByteBuffer source) throws UncheckedIOException {
return -1;
}

@Override
public boolean isOpen() {
return false;
}

@Override
public void position(long position) throws UncheckedIOException {
}

@Override
public void moveTo(WritableFile other) throws UncheckedIOException {
}

@Override
public void setLastModified(Instant instant) throws UncheckedIOException {
}

@Override
public void delete() throws UncheckedIOException {
try {
Files.delete(path);
} catch (IOException e) {
throw new UncheckedIOException(e);
}
}

@Override
public void truncate() throws UncheckedIOException {
}

@Override
public void close() throws UncheckedIOException {
@Override
public Instant lastModified() throws UncheckedIOException {
if (Files.exists(path) && !exists()) {
throw new UncheckedIOException(new IOException(format("%s is a folder", path)));
}

return super.lastModified();
}

@Override
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package org.cryptomator.filesystem.nio;

enum OpenMode {
READ, WRITE
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
package org.cryptomator.filesystem.nio;

import static java.lang.String.format;
import static org.cryptomator.filesystem.nio.OpenMode.READ;

import java.io.UncheckedIOException;
import java.nio.ByteBuffer;
import java.nio.channels.ClosedChannelException;

import org.cryptomator.filesystem.ReadableFile;
import org.cryptomator.filesystem.WritableFile;

class ReadableNioFile implements ReadableFile {

private final NioFile nioFile;

private boolean open = true;
private long position = 0;

public ReadableNioFile(NioFile nioFile) {
this.nioFile = nioFile;
nioFile.channel().open(READ);
}

@Override
public int read(ByteBuffer target) throws UncheckedIOException {
assertOpen();
int read = nioFile.channel().readFully(position, target);
if (read != SharedFileChannel.EOF) {
position += read;
}
return read;
}

@Override
public boolean isOpen() {
return open;
}

@Override
public void position(long position) throws UncheckedIOException {
assertOpen();
this.position = position;
}

@Override
public void copyTo(WritableFile other) throws UncheckedIOException {
assertOpen();
if (belongsToSameFilesystem(other)) {
internalCopyTo((WritableNioFile) other);
} else {
throw new IllegalArgumentException("Can only copy to a WritableFile from the same FileSystem");
}
}

private boolean belongsToSameFilesystem(WritableFile other) {
return other instanceof WritableNioFile && ((WritableNioFile) other).nioFile().belongsToSameFilesystem(nioFile);
}

private void internalCopyTo(WritableNioFile target) {
target.ensureChannelIsOpened();
SharedFileChannel targetChannel = target.channel();
targetChannel.truncate(0);
long size = nioFile.channel().size();
long transferred = 0;
while (transferred < size) {
transferred += nioFile.channel().transferTo(transferred, size - transferred, targetChannel);
}
}

@Override
public void close() {
if (!open) {
return;
}
open = false;
try {
nioFile.channel().close();
} finally {
nioFile.lock().readLock().unlock();
}
}

private void assertOpen() {
if (!open) {
throw new UncheckedIOException(format("%s already closed.", this), new ClosedChannelException());
}
}

@Override
public String toString() {
return format("Readable%s", nioFile);
}

}
Loading

0 comments on commit 39535d0

Please sign in to comment.