- moved method from WritableFile to File: moveTo, setLastModified, setCreationTime, delete

- moved method from File and Folder to Node: setLastModified, setCreationTime, delete
This commit is contained in:
Sebastian Stenzel
2016-02-22 16:42:31 +01:00
parent e6a9786b7a
commit 1467c8315c
32 changed files with 468 additions and 783 deletions

View File

@@ -73,23 +73,9 @@ public interface File extends Node, Comparable<File> {
Copier.copy(this, destination);
}
default void moveTo(File destination) {
Mover.move(this, destination);
}
/**
* <p>
* Deletes the file if it exists.
* <p>
* Does nothign if the file does not exist.
* Moves this file including content to a new location specified by <code>destination</code>.
*/
default void delete() {
if (!exists()) {
return;
}
try (WritableFile writableFile = openWritable()) {
writableFile.delete();
}
}
void moveTo(File destination) throws UncheckedIOException;
}

View File

@@ -98,14 +98,6 @@ public interface Folder extends Node {
Copier.copy(this, target);
}
/**
* <p>
* Deletes the directory including all child elements.
* <p>
* If the directory does not exist this method does nothing.
*/
void delete();
/**
* Moves this directory and its contents to the given destination. If the
* target exists it is deleted before performing the move.

View File

@@ -1,22 +0,0 @@
/*******************************************************************************
* Copyright (c) 2015 Sebastian Stenzel and others.
* This file is licensed under the terms of the MIT license.
* See the LICENSE.txt file for more info.
*
* Contributors:
* Sebastian Stenzel - initial API and implementation
*******************************************************************************/
package org.cryptomator.filesystem;
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));
}
}
}

View File

@@ -30,10 +30,35 @@ public interface Node {
*/
Optional<? extends Folder> parent() throws UncheckedIOException;
/**
* @return <code>true</code> if the node exists.
*/
boolean exists() throws UncheckedIOException;
/**
* <p>
* Deletes the node if it exists.
* <p>
* Does nothing if the node does not exist.
*/
void delete() throws UncheckedIOException;
/**
* <p>
* Determines the last modified date of this node.
*
* @returns the last modified date of the file
*/
Instant lastModified() throws UncheckedIOException;
/**
* <p>
* Sets the last modified date of the file.
*
* @param lastModified the time to set as creation time
*/
void setLastModified(Instant lastModified) throws UncheckedIOException;
/**
* <p>
* Determines the creation time of this node.
@@ -53,9 +78,9 @@ public interface Node {
* Setting the creation time may not be supported by all {@link FileSystem FileSystems}. If the {@code FileSystem} this {@code Node} belongs to does not support the
* setting the creation time the behavior of this method is unspecified.
*
* @param instant the time to set as creation time
* @param creationTime the time to set as creation time
*/
default void setCreationTime(Instant instant) throws UncheckedIOException {
default void setCreationTime(Instant creationTime) throws UncheckedIOException {
throw new UncheckedIOException(new IOException("CreationTime not supported"));
}

View File

@@ -9,42 +9,9 @@ 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 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;
/**
* <p>
* Sets the creation time of the file.
* <p>
* Setting the creation time may not be supported by all {@link FileSystem FileSystems}. If the {@code FileSystem} this {@code WritableFile} belongs to does not support the
* setting the creation time the behavior of this method is unspecified.
*
* @param instant the time to set as creation time
*/
default void setCreationTime(Instant instant) throws UncheckedIOException {
throw new UncheckedIOException(new IOException("CreationTime not supported"));
}
/**
* <p>
* Deletes this file from the file system.
* <p>
* Deleting a file causes it to be {@link WritableFile#close() closed}.
*/
void delete() throws UncheckedIOException;
void truncate() throws UncheckedIOException;
/**
@@ -58,6 +25,7 @@ public interface WritableFile extends WritableByteChannel {
* @throws UncheckedIOException
* if an {@link IOException} occurs while writing
*/
@Override
int write(ByteBuffer source) throws UncheckedIOException;
/**

View File

@@ -9,7 +9,6 @@
package org.cryptomator.filesystem.delegating;
import java.io.UncheckedIOException;
import java.time.Instant;
import java.util.Optional;
import org.cryptomator.filesystem.File;
@@ -60,6 +59,11 @@ public abstract class DelegatingFile<D extends DelegatingFolder<D, ?>> extends D
}
}
@Override
public void delete() throws UncheckedIOException {
delegate.delete();
}
@Override
public int compareTo(File o) {
if (getClass().equals(o.getClass())) {
@@ -70,14 +74,4 @@ public abstract class DelegatingFile<D extends DelegatingFolder<D, ?>> extends D
}
}
@Override
public Optional<Instant> creationTime() throws UncheckedIOException {
return delegate.creationTime();
}
@Override
public void setCreationTime(Instant instant) throws UncheckedIOException {
delegate.setCreationTime(instant);
}
}

View File

@@ -9,7 +9,6 @@
package org.cryptomator.filesystem.delegating;
import java.io.UncheckedIOException;
import java.time.Instant;
import java.util.Optional;
import java.util.stream.Stream;
@@ -97,14 +96,4 @@ public abstract class DelegatingFolder<D extends DelegatingFolder<D, F>, F exten
}
}
@Override
public Optional<Instant> creationTime() throws UncheckedIOException {
return delegate.creationTime();
}
@Override
public void setCreationTime(Instant instant) throws UncheckedIOException {
delegate.setCreationTime(instant);
}
}

View File

@@ -40,11 +40,21 @@ public abstract class DelegatingNode<T extends Node> implements Node {
return delegate.lastModified();
}
@Override
public void setLastModified(Instant instant) throws UncheckedIOException {
delegate.setLastModified(instant);
}
@Override
public Optional<Instant> creationTime() throws UncheckedIOException {
return delegate.creationTime();
}
@Override
public void setCreationTime(Instant instant) throws UncheckedIOException {
delegate.setCreationTime(instant);
}
@Override
public int hashCode() {
return delegate.hashCode();

View File

@@ -10,7 +10,6 @@ package org.cryptomator.filesystem.delegating;
import java.io.UncheckedIOException;
import java.nio.ByteBuffer;
import java.time.Instant;
import org.cryptomator.filesystem.WritableFile;
@@ -27,26 +26,6 @@ public class DelegatingWritableFile implements WritableFile {
return delegate.isOpen();
}
@Override
public void moveTo(WritableFile destination) throws UncheckedIOException {
if (getClass().equals(destination.getClass())) {
final WritableFile delegateDest = ((DelegatingWritableFile) destination).delegate;
delegate.moveTo(delegateDest);
} else {
throw new IllegalArgumentException("Can only move DelegatingWritableFile to a DelegatingWritableFile.");
}
}
@Override
public void setLastModified(Instant instant) throws UncheckedIOException {
delegate.setLastModified(instant);
}
@Override
public void delete() throws UncheckedIOException {
delegate.delete();
}
@Override
public void truncate() throws UncheckedIOException {
delegate.truncate();
@@ -67,9 +46,4 @@ public class DelegatingWritableFile implements WritableFile {
delegate.close();
}
@Override
public void setCreationTime(Instant instant) throws UncheckedIOException {
delegate.setCreationTime(instant);
}
}

View File

@@ -9,6 +9,7 @@
package org.cryptomator.filesystem.delegating;
import java.time.Instant;
import java.util.Optional;
import org.cryptomator.filesystem.File;
import org.cryptomator.filesystem.Folder;
@@ -54,13 +55,43 @@ public class DelegatingFileTest {
@Test
public void testLastModified() {
File mockFile = Mockito.mock(File.class);
Instant now = Instant.now();
Mockito.when(mockFile.lastModified()).thenReturn(now);
DelegatingFile<?> delegatingFile = new TestDelegatingFile(null, mockFile);
Instant now = Instant.now();
Mockito.when(mockFile.lastModified()).thenReturn(now);
Assert.assertEquals(now, delegatingFile.lastModified());
}
@Test
public void testSetLastModified() {
File mockFile = Mockito.mock(File.class);
DelegatingFile<?> delegatingFile = new TestDelegatingFile(null, mockFile);
Instant now = Instant.now();
delegatingFile.setLastModified(now);
Mockito.verify(mockFile).setLastModified(now);
}
@Test
public void testCreationTime() {
File mockFile = Mockito.mock(File.class);
DelegatingFile<?> delegatingFile = new TestDelegatingFile(null, mockFile);
Instant now = Instant.now();
Mockito.when(mockFile.creationTime()).thenReturn(Optional.of(now));
Assert.assertEquals(now, delegatingFile.creationTime().get());
}
@Test
public void testSetCreationTime() {
File mockFile = Mockito.mock(File.class);
DelegatingFile<?> delegatingFile = new TestDelegatingFile(null, mockFile);
Instant now = Instant.now();
delegatingFile.setCreationTime(now);
Mockito.verify(mockFile).setCreationTime(now);
}
@Test
public void testOpenReadable() {
File mockFile = Mockito.mock(File.class);
@@ -122,6 +153,15 @@ public class DelegatingFileTest {
Mockito.verify(mockFile1).copyTo(mockFile2);
}
@Test
public void testDelete() {
File mockFile = Mockito.mock(File.class);
DelegatingFile<?> delegatingFile = new TestDelegatingFile(null, mockFile);
delegatingFile.delete();
Mockito.verify(mockFile).delete();
}
@Test
public void testCompareTo() {
File mockFile1 = Mockito.mock(File.class);

View File

@@ -11,6 +11,7 @@ package org.cryptomator.filesystem.delegating;
import java.time.Instant;
import java.util.Arrays;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;
import java.util.stream.Stream;
@@ -58,13 +59,43 @@ public class DelegatingFolderTest {
@Test
public void testLastModified() {
Folder mockFolder = Mockito.mock(Folder.class);
Instant now = Instant.now();
Mockito.when(mockFolder.lastModified()).thenReturn(now);
DelegatingFolder<?, ?> delegatingFolder = new TestDelegatingFolder(null, mockFolder);
Instant now = Instant.now();
Mockito.when(mockFolder.lastModified()).thenReturn(now);
Assert.assertEquals(now, delegatingFolder.lastModified());
}
@Test
public void testSetLastModified() {
Folder mockFolder = Mockito.mock(Folder.class);
DelegatingFolder<?, ?> delegatingFolder = new TestDelegatingFolder(null, mockFolder);
Instant now = Instant.now();
delegatingFolder.setLastModified(now);
Mockito.verify(mockFolder).setLastModified(now);
}
@Test
public void testCreationTime() {
Folder mockFolder = Mockito.mock(Folder.class);
DelegatingFolder<?, ?> delegatingFolder = new TestDelegatingFolder(null, mockFolder);
Instant now = Instant.now();
Mockito.when(mockFolder.creationTime()).thenReturn(Optional.of(now));
Assert.assertEquals(now, delegatingFolder.creationTime().get());
}
@Test
public void testSetCreationTime() {
Folder mockFolder = Mockito.mock(Folder.class);
DelegatingFolder<?, ?> delegatingFolder = new TestDelegatingFolder(null, mockFolder);
Instant now = Instant.now();
delegatingFolder.setCreationTime(now);
Mockito.verify(mockFolder).setCreationTime(now);
}
@Test
public void testChildren() {
Folder mockFolder = Mockito.mock(Folder.class);
@@ -172,16 +203,4 @@ public class DelegatingFolderTest {
Assert.assertSame(delegatingFolder.folder("mockSubFolder"), delegatingFolder.folder("mockSubFolder"));
Assert.assertSame(delegatingFolder.file("mockSubFile"), delegatingFolder.file("mockSubFile"));
}
@Test
public void testSetCreationTime() {
Folder mockFolder = Mockito.mock(Folder.class);
DelegatingFolder<?, ?> delegatingFolder = new TestDelegatingFolder(null, mockFolder);
Instant now = Instant.now();
delegatingFolder.setCreationTime(now);
Mockito.verify(mockFolder).setCreationTime(now);
}
}

View File

@@ -9,7 +9,6 @@
package org.cryptomator.filesystem.delegating;
import java.nio.ByteBuffer;
import java.time.Instant;
import org.cryptomator.filesystem.WritableFile;
import org.junit.Assert;
@@ -31,49 +30,6 @@ public class DelegatingWritableFileTest {
Assert.assertFalse(delegatingWritableFile.isOpen());
}
@Test
public void testMoveTo() {
WritableFile mockWritableFile1 = Mockito.mock(WritableFile.class);
WritableFile mockWritableFile2 = Mockito.mock(WritableFile.class);
@SuppressWarnings("resource")
DelegatingWritableFile delegatingWritableFile1 = new DelegatingWritableFile(mockWritableFile1);
DelegatingWritableFile delegatingWritableFile2 = new DelegatingWritableFile(mockWritableFile2);
delegatingWritableFile1.moveTo(delegatingWritableFile2);
Mockito.verify(mockWritableFile1).moveTo(mockWritableFile2);
}
@Test(expected = IllegalArgumentException.class)
public void testMoveToDestinationFromDifferentLayer() {
WritableFile mockWritableFile1 = Mockito.mock(WritableFile.class);
WritableFile mockWritableFile2 = Mockito.mock(WritableFile.class);
@SuppressWarnings("resource")
DelegatingWritableFile delegatingWritableFile1 = new DelegatingWritableFile(mockWritableFile1);
delegatingWritableFile1.moveTo(mockWritableFile2);
}
@Test
public void testSetLastModified() {
WritableFile mockWritableFile = Mockito.mock(WritableFile.class);
@SuppressWarnings("resource")
DelegatingWritableFile delegatingWritableFile = new DelegatingWritableFile(mockWritableFile);
Instant now = Instant.now();
delegatingWritableFile.setLastModified(now);
Mockito.verify(mockWritableFile).setLastModified(now);
}
@Test
public void testDelete() {
WritableFile mockWritableFile = Mockito.mock(WritableFile.class);
@SuppressWarnings("resource")
DelegatingWritableFile delegatingWritableFile = new DelegatingWritableFile(mockWritableFile);
delegatingWritableFile.delete();
Mockito.verify(mockWritableFile).delete();
}
@Test
public void testTruncate() {
WritableFile mockWritableFile = Mockito.mock(WritableFile.class);
@@ -115,16 +71,4 @@ public class DelegatingWritableFileTest {
Mockito.verify(mockWritableFile).close();
}
@Test
public void testSetCreationTime() {
WritableFile mockWritableFile = Mockito.mock(WritableFile.class);
@SuppressWarnings("resource")
DelegatingWritableFile delegatingWritableFile = new DelegatingWritableFile(mockWritableFile);
Instant now = Instant.now();
delegatingWritableFile.setCreationTime(now);
Mockito.verify(mockWritableFile).setCreationTime(now);
}
}

View File

@@ -10,7 +10,6 @@ package org.cryptomator.filesystem.blockaligned;
import java.io.UncheckedIOException;
import java.nio.ByteBuffer;
import java.time.Instant;
import java.util.Optional;
import java.util.function.Supplier;
@@ -128,31 +127,6 @@ class BlockAlignedWritableFile implements WritableFile {
return delegate.get().isOpen();
}
@Override
public void moveTo(WritableFile destination) throws UncheckedIOException {
if (destination instanceof BlockAlignedWritableFile) {
final WritableFile delegateDest = ((BlockAlignedWritableFile) destination).delegate.get();
delegate.get().moveTo(delegateDest);
} else {
throw new IllegalArgumentException("Can only move DelegatingWritableFile to a DelegatingWritableFile.");
}
}
@Override
public void setLastModified(Instant instant) throws UncheckedIOException {
delegate.get().setLastModified(instant);
}
@Override
public void setCreationTime(Instant instant) throws UncheckedIOException {
delegate.get().setCreationTime(instant);
}
@Override
public void delete() throws UncheckedIOException {
delegate.get().delete();
}
@Override
public void truncate() throws UncheckedIOException {
delegate.get().truncate();

View File

@@ -60,4 +60,19 @@ public class CryptoFile extends CryptoNode implements File {
return toString().compareTo(o.toString());
}
@Override
public void delete() throws UncheckedIOException {
forceGetPhysicalFile().delete();
}
@Override
public void moveTo(File destination) throws UncheckedIOException {
if (destination instanceof CryptoFile) {
CryptoFile dst = (CryptoFile) destination;
forceGetPhysicalFile().moveTo(dst.forceGetPhysicalFile());
} else {
throw new IllegalArgumentException("Can not move CryptoFile to conventional File.");
}
}
}

View File

@@ -75,14 +75,19 @@ abstract class CryptoNode implements Node {
return forceGetPhysicalFile().lastModified();
}
@Override
public void setLastModified(Instant lastModified) throws UncheckedIOException {
forceGetPhysicalFile().setLastModified(lastModified);
}
@Override
public Optional<Instant> creationTime() throws UncheckedIOException {
return forceGetPhysicalFile().creationTime();
}
@Override
public void setCreationTime(Instant instant) throws UncheckedIOException {
forceGetPhysicalFile().setCreationTime(instant);
public void setCreationTime(Instant creationTime) throws UncheckedIOException {
forceGetPhysicalFile().setCreationTime(creationTime);
}
@Override

View File

@@ -12,7 +12,6 @@ import java.io.IOException;
import java.io.InterruptedIOException;
import java.io.UncheckedIOException;
import java.nio.ByteBuffer;
import java.time.Instant;
import java.util.Optional;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
@@ -73,32 +72,6 @@ class CryptoWritableFile implements WritableFile {
throw new UnsupportedOperationException("Partial write not implemented yet.");
}
@Override
public void moveTo(WritableFile other) {
if (other instanceof CryptoWritableFile) {
CryptoWritableFile dst = (CryptoWritableFile) other;
file.moveTo(dst.file);
} else {
throw new IllegalArgumentException("Can not move CryptoFile to conventional File.");
}
}
@Override
public void setLastModified(Instant instant) {
file.setLastModified(instant);
}
@Override
public void setCreationTime(Instant instant) throws UncheckedIOException {
file.setCreationTime(instant);
}
@Override
public void delete() {
writeTask.cancel(true);
file.delete();
}
@Override
public void truncate() {
contentChanged = true;

View File

@@ -37,6 +37,22 @@ class InMemoryFile extends InMemoryNode implements File {
content.flip();
}
@Override
public void moveTo(File destination) throws UncheckedIOException {
if (destination instanceof InMemoryFile) {
internalMoveTo((InMemoryFile) destination);
} else {
throw new IllegalArgumentException("Can only move an InMemoryFile to another InMemoryFile");
}
}
private void internalMoveTo(InMemoryFile destination) {
this.content.rewind();
destination.create();
destination.content = this.content;
this.delete();
}
@Override
public ReadableFile openReadable() {
if (!exists()) {
@@ -62,21 +78,8 @@ class InMemoryFile extends InMemoryNode implements File {
final WriteLock writeLock = lock.writeLock();
writeLock.lock();
try {
final InMemoryFolder parent = parent().get();
parent.existingChildren.compute(this.name(), (k, v) -> {
if (v != null && v != this) {
// other file or folder with same name already exists.
throw new UncheckedIOException(new FileAlreadyExistsException(k));
} else {
if (v == null) {
assert!content.hasRemaining();
this.creationTime = Instant.now();
}
this.lastModified = Instant.now();
return this;
}
});
final WritableFile result = new InMemoryWritableFile(this::setLastModified, this::setCreationTime, this::getContent, this::setContent, this::delete, writeLock);
create();
final WritableFile result = new InMemoryWritableFile(this::getContent, this::setContent, writeLock);
success = true;
return result;
} finally {
@@ -86,8 +89,21 @@ class InMemoryFile extends InMemoryNode implements File {
}
}
private void setLastModified(Instant lastModified) {
this.lastModified = lastModified;
private void create() {
final InMemoryFolder parent = parent().get();
parent.existingChildren.compute(this.name(), (k, v) -> {
if (v != null && v != this) {
// other file or folder with same name already exists.
throw new UncheckedIOException(new FileAlreadyExistsException(k));
} else {
if (v == null) {
assert!content.hasRemaining();
this.creationTime = Instant.now();
}
this.lastModified = Instant.now();
return this;
}
});
}
private ByteBuffer getContent() {

View File

@@ -16,7 +16,7 @@ import java.util.Optional;
import org.cryptomator.filesystem.Node;
class InMemoryNode implements Node {
abstract class InMemoryNode implements Node {
protected final InMemoryFolder parent;
protected final String name;
@@ -53,6 +53,25 @@ class InMemoryNode implements Node {
return lastModified;
}
@Override
public void setLastModified(Instant lastModified) throws UncheckedIOException {
this.lastModified = lastModified;
}
@Override
public Optional<Instant> creationTime() throws UncheckedIOException {
if (exists()) {
return Optional.of(creationTime);
} else {
throw new UncheckedIOException(new IOException("Node does not exist"));
}
}
@Override
public void setCreationTime(Instant creationTime) throws UncheckedIOException {
this.creationTime = creationTime;
}
@Override
public int hashCode() {
final int prime = 31;
@@ -74,18 +93,4 @@ class InMemoryNode implements Node {
}
}
@Override
public Optional<Instant> creationTime() throws UncheckedIOException {
if (exists()) {
return Optional.of(creationTime);
} else {
throw new UncheckedIOException(new IOException("Node does not exist"));
}
}
@Override
public void setCreationTime(Instant creationTime) throws UncheckedIOException {
this.creationTime = creationTime;
}
}

View File

@@ -10,7 +10,6 @@ package org.cryptomator.filesystem.inmem;
import java.io.UncheckedIOException;
import java.nio.ByteBuffer;
import java.time.Instant;
import java.util.concurrent.locks.ReentrantReadWriteLock.WriteLock;
import java.util.function.Consumer;
import java.util.function.Supplier;
@@ -20,23 +19,17 @@ import org.cryptomator.io.ByteBuffers;
public class InMemoryWritableFile implements WritableFile {
private final Consumer<Instant> lastModifiedSetter;
private final Consumer<Instant> creationTimeSetter;
private final Supplier<ByteBuffer> contentGetter;
private final Consumer<ByteBuffer> contentSetter;
private final Runnable deleter;
private final WriteLock writeLock;
private boolean open = true;
private volatile int position = 0;
public InMemoryWritableFile(Consumer<Instant> lastModifiedSetter, Consumer<Instant> creationTimeSetter, Supplier<ByteBuffer> contentGetter, Consumer<ByteBuffer> contentSetter, Runnable deleter, WriteLock writeLock) {
this.lastModifiedSetter = lastModifiedSetter;
public InMemoryWritableFile(Supplier<ByteBuffer> contentGetter, Consumer<ByteBuffer> contentSetter, WriteLock writeLock) {
this.contentGetter = contentGetter;
this.contentSetter = contentSetter;
this.deleter = deleter;
this.writeLock = writeLock;
this.creationTimeSetter = creationTimeSetter;
}
@Override
@@ -44,37 +37,6 @@ public class InMemoryWritableFile implements WritableFile {
return open;
}
@Override
public void moveTo(WritableFile other) throws UncheckedIOException {
if (other instanceof InMemoryWritableFile) {
internalMoveTo((InMemoryWritableFile) other);
} else {
throw new IllegalArgumentException("Can only move an InMemoryWritableFile to another InMemoryWritableFile");
}
}
private void internalMoveTo(InMemoryWritableFile destination) {
try {
destination.contentSetter.accept(this.contentGetter.get());
destination.contentGetter.get().rewind();
deleter.run();
} finally {
open = false;
destination.open = false;
}
}
@Override
public void setLastModified(Instant instant) throws UncheckedIOException {
lastModifiedSetter.accept(instant);
}
@Override
public void delete() throws UncheckedIOException {
deleter.run();
open = false;
}
@Override
public void truncate() throws UncheckedIOException {
contentSetter.accept(ByteBuffer.allocate(0));
@@ -111,12 +73,6 @@ public class InMemoryWritableFile implements WritableFile {
public void close() throws UncheckedIOException {
open = false;
writeLock.unlock();
lastModifiedSetter.accept(Instant.now());
}
@Override
public void setCreationTime(Instant instant) throws UncheckedIOException {
creationTimeSetter.accept(instant);
}
}

View File

@@ -116,11 +116,7 @@ public class InMemoryFileSystemTest {
// move bar to baz
File bazFile = fs.file("baz.txt");
try (WritableFile src = barFile.openWritable()) {
try (WritableFile dst = bazFile.openWritable()) {
src.moveTo(dst);
}
}
barFile.moveTo(bazFile);
Assert.assertFalse(barFile.exists());
Assert.assertTrue(bazFile.exists());

View File

@@ -12,6 +12,7 @@ import static org.hamcrest.CoreMatchers.is;
import static org.junit.Assert.assertThat;
import java.io.UncheckedIOException;
import java.nio.ByteBuffer;
import java.time.Instant;
import org.cryptomator.filesystem.WritableFile;
@@ -27,38 +28,39 @@ public class InMemoryFileTest {
@Test
public void testCreationTimeOfNonExistingFileThrowsUncheckedIOException() {
InMemoryFileSystem fileSystem = new InMemoryFileSystem();
InMemoryFile inTest = fileSystem.file("foo");
InMemoryFile file = fileSystem.file("foo");
thrown.expect(UncheckedIOException.class);
inTest.creationTime();
file.creationTime();
}
@Test
public void testCreationTimeOfCreatedFileIsSetToInstantDuringCreation() {
InMemoryFileSystem fileSystem = new InMemoryFileSystem();
InMemoryFile inTest = fileSystem.file("foo");
InMemoryFile file = fileSystem.file("foo");
Instant minCreationTime = Instant.now();
Instant maxCreationTime;
try (WritableFile writable = inTest.openWritable()) {
try (WritableFile writable = file.openWritable()) {
maxCreationTime = Instant.now();
}
assertThat(inTest.creationTime().get().isBefore(minCreationTime), is(false));
assertThat(inTest.creationTime().get().isAfter(maxCreationTime), is(false));
assertThat(file.creationTime().get().isBefore(minCreationTime), is(false));
assertThat(file.creationTime().get().isAfter(maxCreationTime), is(false));
}
@Test
public void testCreationTimeSetInWritableFileIsSaved() {
public void testCreationTimeSetIsSaved() {
Instant creationTime = Instant.parse("2015-03-23T21:11:32Z");
InMemoryFileSystem fileSystem = new InMemoryFileSystem();
InMemoryFile inTest = fileSystem.file("foo");
try (WritableFile writable = inTest.openWritable()) {
writable.setCreationTime(creationTime);
InMemoryFile file = fileSystem.file("foo");
try (WritableFile writable = file.openWritable()) {
writable.write(ByteBuffer.allocate(0));
}
assertThat(inTest.creationTime().get(), is(creationTime));
file.setCreationTime(creationTime);
assertThat(file.creationTime().get(), is(creationTime));
}
}

View File

@@ -75,10 +75,7 @@ public class ShorteningFileSystemTest {
Assert.assertFalse(metadataRoot.children().findAny().isPresent());
final File longNamedFolder = fs.file("morethantenchars");
try (WritableFile src = shortNamedFolder.openWritable(); //
WritableFile dst = longNamedFolder.openWritable()) {
src.moveTo(dst);
}
shortNamedFolder.moveTo(longNamedFolder);
Assert.assertTrue(metadataRoot.children().findAny().isPresent());
}

View File

@@ -23,8 +23,8 @@ class DefaultInstanceFactory implements InstanceFactory {
}
@Override
public WritableNioFile writableNioFile(FileSystem fileSystem, Path path, SharedFileChannel channel, Runnable afterCloseCallback, NioAccess nioAccess) {
return new WritableNioFile(fileSystem, path, channel, afterCloseCallback, nioAccess);
public WritableNioFile writableNioFile(FileSystem fileSystem, Path path, SharedFileChannel channel, Runnable afterCloseCallback) {
return new WritableNioFile(fileSystem, path, channel, afterCloseCallback);
}
@Override

View File

@@ -16,7 +16,7 @@ interface InstanceFactory {
SharedFileChannel sharedFileChannel(Path path, NioAccess nioAccess);
WritableNioFile writableNioFile(FileSystem fileSystem, Path path, SharedFileChannel channel, Runnable afterCloseCallback, NioAccess nioAccess);
WritableNioFile writableNioFile(FileSystem fileSystem, Path path, SharedFileChannel channel, Runnable afterCloseCallback);
ReadableNioFile readableNioFile(Path path, SharedFileChannel channel, Runnable afterCloseCallback);

View File

@@ -1,11 +1,15 @@
package org.cryptomator.filesystem.nio;
import static java.lang.String.format;
import static java.nio.file.StandardCopyOption.REPLACE_EXISTING;
import java.io.IOException;
import java.io.UncheckedIOException;
import java.nio.file.Path;
import java.time.Instant;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.locks.ReentrantReadWriteLock;
@@ -16,7 +20,7 @@ import org.cryptomator.filesystem.WritableFile;
class NioFile extends NioNode implements File {
private final ReentrantReadWriteLock lock = new ReentrantReadWriteLock(true);
private SharedFileChannel sharedChannel;
private final SharedFileChannel sharedChannel;
public NioFile(Optional<NioFolder> parent, Path eventuallyNonAbsolutePath, NioAccess nioAccess, InstanceFactory instanceFactory) {
super(parent, eventuallyNonAbsolutePath, nioAccess, instanceFactory);
@@ -47,11 +51,17 @@ class NioFile extends NioNode implements File {
if (lock.getReadHoldCount() > 0) {
throw new IllegalStateException("Current thread is currently reading this file");
}
lock.writeLock().lock();
return instanceFactory.writableNioFile(fileSystem(), path, sharedChannel, this::unlockWriteLock, nioAccess);
lockWriteLock();
return instanceFactory.writableNioFile(fileSystem(), path, sharedChannel, this::unlockWriteLock);
}
private void unlockWriteLock() {
// visible for testing
void lockWriteLock() {
lock.writeLock().lock();
}
// visible for testing
void unlockWriteLock() {
lock.writeLock().unlock();
}
@@ -68,6 +78,63 @@ class NioFile extends NioNode implements File {
return super.lastModified();
}
@Override
public Optional<Instant> creationTime() throws UncheckedIOException {
if (nioAccess.exists(path) && !exists()) {
throw new UncheckedIOException(new IOException(format("%s is a folder", path)));
}
return super.creationTime();
}
@Override
public void moveTo(File destination) throws UncheckedIOException {
if (destination == this) {
return;
} else if (belongsToSameFilesystem(destination)) {
internalMoveTo((NioFile) destination);
} else {
throw new IllegalArgumentException("Can only move to a File from the same FileSystem");
}
}
private void assertMovePreconditionsAreMet(NioFile destination) {
if (nioAccess.isDirectory(path())) {
throw new UncheckedIOException(new IOException(format("Can not move %s to %s. Source is a directory", path(), destination.path())));
}
if (nioAccess.isDirectory(destination.path())) {
throw new UncheckedIOException(new IOException(format("Can not move %s to %s. Target is a directory", path(), destination.path())));
}
}
private void internalMoveTo(NioFile destination) {
assertMovePreconditionsAreMet(destination);
// TODO review deadlock-safety of locking two files. see DeadLockSafeFileOpener
List<NioFile> filesToBeLocked = new ArrayList<>();
filesToBeLocked.add(this);
filesToBeLocked.add(destination);
Collections.sort(filesToBeLocked);
filesToBeLocked.forEach(file -> file.lockWriteLock());
try {
nioAccess.move(path(), destination.path(), REPLACE_EXISTING);
} catch (IOException e) {
throw new UncheckedIOException(e);
} finally {
filesToBeLocked.forEach(file -> file.unlockWriteLock());
}
}
@Override
public void delete() throws UncheckedIOException {
if (!exists()) {
return;
}
try {
nioAccess.delete(path());
} catch (IOException e) {
throw new UncheckedIOException(e);
}
}
@Override
public int compareTo(File o) {
if (belongsToSameFilesystem(o)) {
@@ -82,12 +149,4 @@ class NioFile extends NioNode implements File {
return format("NioFile(%s)", path);
}
@Override
public Optional<Instant> creationTime() throws UncheckedIOException {
if (nioAccess.exists(path) && !exists()) {
throw new UncheckedIOException(new IOException(format("%s is a folder", path)));
}
return super.creationTime();
}
}

View File

@@ -25,6 +25,11 @@ abstract class NioNode implements Node {
this.instanceFactory = instanceFactoy;
}
// visible for testing
Path path() {
return path;
}
@Override
public String name() throws UncheckedIOException {
return path.getFileName().toString();
@@ -35,6 +40,8 @@ abstract class NioNode implements Node {
return parent;
}
private static final Instant JANNUARY_THE_SECOND_NINTEENHUNDRED_SEVENTY = Instant.parse("1970-01-02T00:00:00Z");
@Override
public Instant lastModified() throws UncheckedIOException {
try {
@@ -44,7 +51,14 @@ abstract class NioNode implements Node {
}
}
private static final Instant JANNUARY_THE_SECOND_NINTEENHUNDRED_SEVENTY = Instant.parse("1970-01-02T00:00:00Z");
@Override
public void setLastModified(Instant lastModified) throws UncheckedIOException {
try {
nioAccess.setLastModifiedTime(path, FileTime.from(lastModified));
} catch (IOException e) {
throw new UncheckedIOException(e);
}
}
@Override
public Optional<Instant> creationTime() throws UncheckedIOException {

View File

@@ -1,16 +1,12 @@
package org.cryptomator.filesystem.nio;
import static java.lang.String.format;
import static java.nio.file.StandardCopyOption.REPLACE_EXISTING;
import static org.cryptomator.filesystem.nio.OpenMode.WRITE;
import java.io.IOException;
import java.io.UncheckedIOException;
import java.nio.ByteBuffer;
import java.nio.channels.ClosedChannelException;
import java.nio.file.Path;
import java.nio.file.attribute.FileTime;
import java.time.Instant;
import org.cryptomator.filesystem.FileSystem;
import org.cryptomator.filesystem.WritableFile;
@@ -20,19 +16,17 @@ class WritableNioFile implements WritableFile {
private final FileSystem fileSystem;
private final Path path;
private final SharedFileChannel channel;
private final NioAccess nioAccess;
private Runnable afterCloseCallback;
private final Runnable afterCloseCallback;
private boolean open = true;
private boolean channelOpened = false;
private long position = 0;
public WritableNioFile(FileSystem fileSystem, Path path, SharedFileChannel channel, Runnable afterCloseCallback, NioAccess nioAccess) {
public WritableNioFile(FileSystem fileSystem, Path path, SharedFileChannel channel, Runnable afterCloseCallback) {
this.fileSystem = fileSystem;
this.path = path;
this.channel = channel;
this.afterCloseCallback = afterCloseCallback;
this.nioAccess = nioAccess;
}
@Override
@@ -55,73 +49,6 @@ class WritableNioFile implements WritableFile {
this.position = position;
}
private boolean belongsToSameFilesystem(WritableFile other) {
return other instanceof WritableNioFile && ((WritableNioFile) other).fileSystem() == fileSystem;
}
@Override
public void moveTo(WritableFile other) throws UncheckedIOException {
assertOpen();
if (other == this) {
return;
} else if (belongsToSameFilesystem(other)) {
internalMoveTo((WritableNioFile) other);
} else {
throw new IllegalArgumentException("Can only move to a WritableFile from the same FileSystem");
}
}
private void internalMoveTo(WritableNioFile other) {
other.assertOpen();
assertMovePreconditionsAreMet(other);
try {
closeChannelIfOpened();
other.closeChannelIfOpened();
nioAccess.move(path, other.path(), REPLACE_EXISTING);
} catch (IOException e) {
throw new UncheckedIOException(e);
} finally {
open = false;
other.open = false;
other.invokeAfterCloseCallback();
invokeAfterCloseCallback();
}
}
private void assertMovePreconditionsAreMet(WritableNioFile other) {
if (nioAccess.isDirectory(path)) {
throw new UncheckedIOException(new IOException(format("Can not move %s to %s. Source is a directory", path, other.path())));
}
if (nioAccess.isDirectory(other.path())) {
throw new UncheckedIOException(new IOException(format("Can not move %s to %s. Target is a directory", path, other.path())));
}
}
@Override
public void setLastModified(Instant instant) throws UncheckedIOException {
assertOpen();
ensureChannelIsOpened();
try {
nioAccess.setLastModifiedTime(path, FileTime.from(instant));
} catch (IOException e) {
throw new UncheckedIOException(e);
}
}
@Override
public void delete() throws UncheckedIOException {
assertOpen();
try {
closeChannelIfOpened();
nioAccess.delete(path);
} catch (IOException e) {
throw new UncheckedIOException(e);
} finally {
open = false;
invokeAfterCloseCallback();
}
}
@Override
public void truncate() throws UncheckedIOException {
assertOpen();
@@ -129,17 +56,6 @@ class WritableNioFile implements WritableFile {
channel.truncate(0);
}
@Override
public void setCreationTime(Instant instant) throws UncheckedIOException {
assertOpen();
ensureChannelIsOpened();
try {
nioAccess.setCreationTime(path, FileTime.from(instant));
} catch (IOException e) {
throw new UncheckedIOException(e);
}
}
@Override
public void close() throws UncheckedIOException {
if (!open) {

View File

@@ -23,7 +23,7 @@ public class DefaultInstanceFactoryTest {
private NioAccess nioAccess;
private SharedFileChannel channel;
private DefaultInstanceFactory inTest = new DefaultInstanceFactory();
private final DefaultInstanceFactory inTest = new DefaultInstanceFactory();
@Before
public void setUp() {
@@ -67,14 +67,13 @@ public class DefaultInstanceFactoryTest {
@Test
public void testWritableNioFileCreatesWritableNioFile() throws IOException {
Runnable afterCloseCallback = mock(Runnable.class);
WritableNioFile result = inTest.writableNioFile(fileSystem, path, channel, afterCloseCallback, nioAccess);
WritableNioFile result = inTest.writableNioFile(fileSystem, path, channel, afterCloseCallback);
assertThat(result.path(), is(path));
assertThat(result.channel(), is(channel));
assertThat(result.fileSystem(), is(fileSystem));
result.delete();
verify(nioAccess).delete(path);
result.close();
verify(afterCloseCallback).run();
}

View File

@@ -1,13 +1,17 @@
package org.cryptomator.filesystem.nio;
import static java.lang.String.format;
import static java.nio.file.StandardCopyOption.REPLACE_EXISTING;
import static org.cryptomator.common.test.matcher.OptionalMatcher.emptyOptional;
import static org.hamcrest.CoreMatchers.is;
import static org.junit.Assert.assertThat;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.same;
import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.inOrder;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyZeroInteractions;
import static org.mockito.Mockito.when;
import java.io.IOException;
@@ -27,6 +31,7 @@ import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.InOrder;
import de.bechte.junit.runners.context.HierarchicalContextRunner;
@@ -128,7 +133,7 @@ public class NioFileTest {
@Test
public void testOpenReadableInvokedAfterInvokingAfterCloseOperationWorks() {
ArgumentCaptor<Runnable> captor = ArgumentCaptor.forClass(Runnable.class);
when(instanceFactory.writableNioFile(same(fileSystem), same(path), same(channel), captor.capture(), same(nioAccess))).thenReturn(null);
when(instanceFactory.writableNioFile(same(fileSystem), same(path), same(channel), captor.capture())).thenReturn(null);
inTest.openWritable();
captor.getValue().run();
@@ -138,7 +143,7 @@ public class NioFileTest {
@Test
public void testOpenWritableCreatesWritableNioFileFromNioFileAndNioAccessUsingInstanceFactory() {
WritableNioFile writableNioFile = mock(WritableNioFile.class);
when(instanceFactory.writableNioFile(same(fileSystem), same(path), same(channel), any(), same(nioAccess))).thenReturn(writableNioFile);
when(instanceFactory.writableNioFile(same(fileSystem), same(path), same(channel), any())).thenReturn(writableNioFile);
WritableFile writableFile = inTest.openWritable();
@@ -149,7 +154,7 @@ public class NioFileTest {
public void testOpenWritableInvokedAfterAfterCloseOperationCreatesNewWritableFile() {
WritableNioFile writableNioFile = mock(WritableNioFile.class);
ArgumentCaptor<Runnable> captor = ArgumentCaptor.forClass(Runnable.class);
when(instanceFactory.writableNioFile(same(fileSystem), same(path), same(channel), captor.capture(), same(nioAccess))).thenReturn(null, writableNioFile);
when(instanceFactory.writableNioFile(same(fileSystem), same(path), same(channel), captor.capture())).thenReturn(null, writableNioFile);
inTest.openWritable();
captor.getValue().run();
@@ -190,6 +195,74 @@ public class NioFileTest {
}
public class MoveTo {
private NioFile target;
private Path pathOfTarget;
@Before
public void setUp() {
target = mock(NioFile.class);
pathOfTarget = mock(Path.class);
when(target.fileSystem()).thenReturn(fileSystem);
when(target.path()).thenReturn(pathOfTarget);
}
@Test
public void testMoveToFailsIfTargetIsNoWritableNioFile() {
thrown.expect(IllegalArgumentException.class);
thrown.expectMessage("Can only move to a File from the same FileSystem");
inTest.moveTo(mock(File.class));
}
@Test
public void testMoveToFailsIfTargetIsNotFromSameFileSystem() {
NioFile targetFromOtherFileSystem = mock(NioFile.class);
when(targetFromOtherFileSystem.fileSystem()).thenReturn(mock(FileSystem.class));
thrown.expect(IllegalArgumentException.class);
thrown.expectMessage("Can only move to a File from the same FileSystem");
inTest.moveTo(targetFromOtherFileSystem);
}
@Test
public void testMoveToFailsIfTargetIsDirectory() {
when(nioAccess.isDirectory(path)).thenReturn(false);
when(nioAccess.isDirectory(pathOfTarget)).thenReturn(true);
thrown.expect(UncheckedIOException.class);
thrown.expectMessage("Target is a directory");
thrown.expectMessage(path.toString());
thrown.expectMessage(pathOfTarget.toString());
inTest.moveTo(target);
}
@Test
public void testMoveToDoesNothingIfTargetIsSameInstance() {
inTest.moveTo(inTest);
verifyZeroInteractions(nioAccess);
}
@Test
public void testMoveToWrapsIOExceptionFromMoveInUncheckedIOException() throws IOException {
when(nioAccess.isDirectory(path)).thenReturn(false);
when(nioAccess.isDirectory(pathOfTarget)).thenReturn(false);
IOException exceptionFromMove = new IOException();
doThrow(exceptionFromMove).when(nioAccess).move(path, pathOfTarget, REPLACE_EXISTING);
thrown.expect(UncheckedIOException.class);
thrown.expectCause(is(exceptionFromMove));
inTest.moveTo(target);
}
}
public class Exists {
@Test
@@ -259,39 +332,30 @@ public class NioFileTest {
}
public class CompareTo {
public class SetLastModified {
private Path otherPath;
private NioFile otherInTest;
@Test
public void testSetLastModifiedOpensChannelIfClosedAndSetsLastModifiedTime() throws IOException {
Instant instant = Instant.parse("2016-01-05T13:51:00Z");
FileTime time = FileTime.from(instant);
@Before
public void setUp() {
otherPath = mock(Path.class);
inTest.setLastModified(instant);
Path maybeNonAbsolutePath = mock(Path.class);
when(maybeNonAbsolutePath.toAbsolutePath()).thenReturn(otherPath);
otherInTest = new NioFile(parent, maybeNonAbsolutePath, nioAccess, instanceFactory);
InOrder inOrder = inOrder(channel, nioAccess);
inOrder.verify(nioAccess).setLastModifiedTime(path, time);
}
@Test
public void testCompareToFileFromOtherFileSystemThrowsIllegalArgumentException() {
File other = mock(File.class);
when(other.fileSystem()).thenReturn(mock(FileSystem.class));
public void testSetLastModifiedWrapsIOExceptionFromSetLastModifiedInUncheckedIOException() throws IOException {
IOException exceptionFromSetLastModified = new IOException();
Instant instant = Instant.parse("2016-01-05T13:51:00Z");
FileTime time = FileTime.from(instant);
doThrow(exceptionFromSetLastModified).when(nioAccess).setLastModifiedTime(path, time);
thrown.expect(IllegalArgumentException.class);
thrown.expect(UncheckedIOException.class);
thrown.expectCause(is(exceptionFromSetLastModified));
inTest.compareTo(other);
}
@Test
public void testCompareToReturnsResultOfPathsCompareTo() {
int expectedResult = 2873;
when(path.compareTo(otherPath)).thenReturn(expectedResult);
int result = inTest.compareTo(otherInTest);
assertThat(result, is(expectedResult));
inTest.setLastModified(instant);
}
}
@@ -345,6 +409,69 @@ public class NioFileTest {
}
public class SetCreationTime {
@Test
public void testSetCreationTimeOpensChannelIfClosedAndInvokesNioAccessSetCreationTimeAfterwards() throws IOException {
Instant instant = Instant.parse("2016-01-08T22:32:00Z");
inTest.setCreationTime(instant);
InOrder inOrder = inOrder(nioAccess, channel);
inOrder.verify(nioAccess).setCreationTime(path, FileTime.from(instant));
}
@Test
public void testSetCreationTimeWrapsIOExceptionFromSetCreationTimeInUncheckedIOException() throws IOException {
IOException exceptionFromSetCreationTime = new IOException();
Instant irrelevant = Instant.now();
doThrow(exceptionFromSetCreationTime).when(nioAccess).setCreationTime(same(path), any());
thrown.expect(UncheckedIOException.class);
thrown.expectCause(is(exceptionFromSetCreationTime));
inTest.setCreationTime(irrelevant);
}
}
public class CompareTo {
private Path otherPath;
private NioFile otherInTest;
@Before
public void setUp() {
otherPath = mock(Path.class);
Path maybeNonAbsolutePath = mock(Path.class);
when(maybeNonAbsolutePath.toAbsolutePath()).thenReturn(otherPath);
otherInTest = new NioFile(parent, maybeNonAbsolutePath, nioAccess, instanceFactory);
}
@Test
public void testCompareToFileFromOtherFileSystemThrowsIllegalArgumentException() {
File other = mock(File.class);
when(other.fileSystem()).thenReturn(mock(FileSystem.class));
thrown.expect(IllegalArgumentException.class);
inTest.compareTo(other);
}
@Test
public void testCompareToReturnsResultOfPathsCompareTo() {
int expectedResult = 2873;
when(path.compareTo(otherPath)).thenReturn(expectedResult);
int result = inTest.compareTo(otherInTest);
assertThat(result, is(expectedResult));
}
}
@Test
public void testNameReturnsFileNameOfPath() {
Path fileName = mock(Path.class);

View File

@@ -1,13 +1,10 @@
package org.cryptomator.filesystem.nio;
import static java.lang.String.format;
import static java.nio.file.StandardCopyOption.REPLACE_EXISTING;
import static org.cryptomator.filesystem.nio.OpenMode.WRITE;
import static org.hamcrest.CoreMatchers.is;
import static org.junit.Assert.assertThat;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyInt;
import static org.mockito.Matchers.same;
import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.inOrder;
import static org.mockito.Mockito.mock;
@@ -15,15 +12,11 @@ import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyZeroInteractions;
import static org.mockito.Mockito.when;
import java.io.IOException;
import java.io.UncheckedIOException;
import java.nio.ByteBuffer;
import java.nio.file.Path;
import java.nio.file.attribute.FileTime;
import java.time.Instant;
import org.cryptomator.filesystem.FileSystem;
import org.cryptomator.filesystem.WritableFile;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
@@ -47,8 +40,6 @@ public class WritableNioFileTest {
private Runnable afterCloseCallback;
private NioAccess nioAccess;
private WritableNioFile inTest;
@Before
@@ -57,8 +48,7 @@ public class WritableNioFileTest {
channel = mock(SharedFileChannel.class);
path = mock(Path.class);
afterCloseCallback = mock(Runnable.class);
nioAccess = mock(NioAccess.class);
inTest = new WritableNioFile(fileSystem, path, channel, afterCloseCallback, nioAccess);
inTest = new WritableNioFile(fileSystem, path, channel, afterCloseCallback);
}
public class ConstructorTests {
@@ -147,238 +137,6 @@ public class WritableNioFileTest {
}
public class MoveToTests {
private WritableNioFile target;
private Path pathOfTarget;
@Before
public void setUp() {
target = mock(WritableNioFile.class);
pathOfTarget = mock(Path.class);
when(target.fileSystem()).thenReturn(fileSystem);
when(target.path()).thenReturn(pathOfTarget);
}
@Test
public void testMoveToFailsIfTargetIsNoWritableNioFile() {
thrown.expect(IllegalArgumentException.class);
thrown.expectMessage("Can only move to a WritableFile from the same FileSystem");
inTest.moveTo(mock(WritableFile.class));
}
@Test
public void testMoveToFailsIfTargetIsNotFromSameFileSystem() {
WritableNioFile targetFromOtherFileSystem = mock(WritableNioFile.class);
when(targetFromOtherFileSystem.fileSystem()).thenReturn(mock(FileSystem.class));
thrown.expect(IllegalArgumentException.class);
thrown.expectMessage("Can only move to a WritableFile from the same FileSystem");
inTest.moveTo(mock(WritableFile.class));
}
@Test
public void testMoveToFailsIfSourceIsDirectory() {
when(nioAccess.isDirectory(path)).thenReturn(true);
thrown.expect(UncheckedIOException.class);
thrown.expectMessage("Source is a directory");
thrown.expectMessage(path.toString());
thrown.expectMessage(pathOfTarget.toString());
inTest.moveTo(target);
}
@Test
public void testMoveToFailsIfTargetIsDirectory() {
when(nioAccess.isDirectory(path)).thenReturn(false);
when(nioAccess.isDirectory(pathOfTarget)).thenReturn(true);
thrown.expect(UncheckedIOException.class);
thrown.expectMessage("Target is a directory");
thrown.expectMessage(path.toString());
thrown.expectMessage(pathOfTarget.toString());
inTest.moveTo(target);
}
@Test
public void testMoveToDoesNothingIfTargetIsSameInstance() {
inTest.moveTo(inTest);
verifyZeroInteractions(nioAccess);
}
@Test
public void testMoveToAssertsTargetIsOpenClosesChannelsIfOpenMovesFileAndInvokesAfterCloseCallbacks() throws IOException {
when(nioAccess.isDirectory(path)).thenReturn(false);
when(nioAccess.isDirectory(pathOfTarget)).thenReturn(false);
inTest.ensureChannelIsOpened();
inTest.moveTo(target);
InOrder inOrder = inOrder(target, nioAccess, channel, afterCloseCallback);
inOrder.verify(target).assertOpen();
inOrder.verify(channel).close();
inOrder.verify(target).closeChannelIfOpened();
inOrder.verify(nioAccess).move(path, pathOfTarget, REPLACE_EXISTING);
inOrder.verify(target).invokeAfterCloseCallback();
inOrder.verify(afterCloseCallback).run();
}
@Test
public void testMoveToWrapsIOExceptionFromMoveInUncheckedIOException() throws IOException {
when(nioAccess.isDirectory(path)).thenReturn(false);
when(nioAccess.isDirectory(pathOfTarget)).thenReturn(false);
IOException exceptionFromMove = new IOException();
doThrow(exceptionFromMove).when(nioAccess).move(path, pathOfTarget, REPLACE_EXISTING);
thrown.expect(UncheckedIOException.class);
thrown.expectCause(is(exceptionFromMove));
inTest.moveTo(target);
}
@Test
public void testMoveToInvokesAfterCloseCallbacksEvenIfMoveToThrowsException() throws IOException {
when(nioAccess.isDirectory(path)).thenReturn(false);
when(nioAccess.isDirectory(pathOfTarget)).thenReturn(false);
IOException exceptionFromMove = new IOException();
doThrow(exceptionFromMove).when(nioAccess).move(path, pathOfTarget, REPLACE_EXISTING);
thrown.expect(UncheckedIOException.class);
thrown.expectCause(is(exceptionFromMove));
inTest.moveTo(target);
verify(target).invokeAfterCloseCallback();
verify(afterCloseCallback).run();
}
}
public class SetLastModifiedTests {
@Test
public void testSetLastModifiedOpensChannelIfClosedAndSetsLastModifiedTime() throws IOException {
Instant instant = Instant.parse("2016-01-05T13:51:00Z");
FileTime time = FileTime.from(instant);
inTest.setLastModified(instant);
InOrder inOrder = inOrder(channel, nioAccess);
inOrder.verify(channel).open(OpenMode.WRITE);
inOrder.verify(nioAccess).setLastModifiedTime(path, time);
}
@Test
public void testSetLastModifiedWrapsIOExceptionFromSetLastModifiedInUncheckedIOException() throws IOException {
IOException exceptionFromSetLastModified = new IOException();
Instant instant = Instant.parse("2016-01-05T13:51:00Z");
FileTime time = FileTime.from(instant);
doThrow(exceptionFromSetLastModified).when(nioAccess).setLastModifiedTime(path, time);
thrown.expect(UncheckedIOException.class);
thrown.expectCause(is(exceptionFromSetLastModified));
inTest.setLastModified(instant);
}
}
public class SetCreationTimeTests {
@Test
public void testSetCreationTimeFailsIfNotOpen() {
Instant irrelevant = null;
inTest.close();
thrown.expect(UncheckedIOException.class);
thrown.expectMessage("already closed");
inTest.setCreationTime(irrelevant);
}
@Test
public void testSetCreationTimeOpensChannelIfClosedAndInvokesNioAccessSetCreationTimeAfterwards() throws IOException {
Instant instant = Instant.parse("2016-01-08T22:32:00Z");
inTest.setCreationTime(instant);
InOrder inOrder = inOrder(nioAccess, channel);
inOrder.verify(channel).open(OpenMode.WRITE);
inOrder.verify(nioAccess).setCreationTime(path, FileTime.from(instant));
}
@Test
public void testSetCreationTimeWrapsIOExceptionFromSetCreationTimeInUncheckedIOException() throws IOException {
IOException exceptionFromSetCreationTime = new IOException();
Instant irrelevant = Instant.now();
doThrow(exceptionFromSetCreationTime).when(nioAccess).setCreationTime(same(path), any());
thrown.expect(UncheckedIOException.class);
thrown.expectCause(is(exceptionFromSetCreationTime));
inTest.setCreationTime(irrelevant);
}
}
public class DeleteTests {
@Test
public void testDeleteClosesChannelIfOpenAndDeletesFileAndInvokesAfterCloseCallback() throws IOException {
inTest.ensureChannelIsOpened();
inTest.delete();
InOrder inOrder = inOrder(channel, nioAccess, afterCloseCallback);
inOrder.verify(channel).close();
inOrder.verify(nioAccess).delete(path);
inOrder.verify(afterCloseCallback).run();
}
@Test
public void testDeleteClosesWritableNioFile() {
inTest.delete();
assertThat(inTest.isOpen(), is(false));
}
@Test
public void testDeleteClosesWritableNioFileEvenIfDeleteFails() throws IOException {
IOException exceptionFromDelete = new IOException();
doThrow(exceptionFromDelete).when(nioAccess).delete(path);
thrown.expect(UncheckedIOException.class);
thrown.expectCause(is(exceptionFromDelete));
try {
inTest.delete();
} finally {
assertThat(inTest.isOpen(), is(false));
}
}
@Test
public void testDeleteInvokesAfterCloseCallbackEvenIfDeleteFails() throws IOException {
IOException exceptionFromDelete = new IOException();
doThrow(exceptionFromDelete).when(nioAccess).delete(path);
thrown.expect(UncheckedIOException.class);
thrown.expectCause(is(exceptionFromDelete));
try {
inTest.delete();
} finally {
verify(afterCloseCallback).run();
}
}
}
public class TruncateTests {
@Test
@@ -491,36 +249,6 @@ public class WritableNioFileTest {
inTest.truncate();
}
@Test
public void testMoveToFailsIfClosed() {
inTest.close();
thrown.expect(UncheckedIOException.class);
thrown.expectMessage("already closed");
inTest.moveTo(null);
}
@Test
public void testSetLastModifiedFailsIfClosed() {
inTest.close();
thrown.expect(UncheckedIOException.class);
thrown.expectMessage("already closed");
inTest.setLastModified(null);
}
@Test
public void testDeleteFailsIfClosed() {
inTest.close();
thrown.expect(UncheckedIOException.class);
thrown.expectMessage("already closed");
inTest.delete();
}
}
@Test

View File

@@ -31,7 +31,6 @@ import org.apache.jackrabbit.webdav.property.DefaultDavProperty;
import org.cryptomator.filesystem.File;
import org.cryptomator.filesystem.Folder;
import org.cryptomator.filesystem.ReadableFile;
import org.cryptomator.filesystem.WritableFile;
import org.cryptomator.filesystem.jackrabbit.FileLocator;
import com.google.common.io.ByteStreams;
@@ -155,17 +154,13 @@ class DavFile extends DavNode<FileLocator> {
}
@Override
protected void setModificationTime(Instant instant) {
try (WritableFile writable = node.openWritable()) {
writable.setLastModified(instant);
}
protected void setModificationTime(Instant lastModified) {
node.setLastModified(lastModified);
}
@Override
protected void setCreationTime(Instant instant) {
try (WritableFile writable = node.openWritable()) {
writable.setCreationTime(instant);
}
protected void setCreationTime(Instant creationTime) {
node.setCreationTime(creationTime);
}
@Override

View File

@@ -29,7 +29,6 @@ import org.apache.jackrabbit.webdav.lock.LockManager;
import org.apache.jackrabbit.webdav.property.DavPropertyName;
import org.apache.jackrabbit.webdav.property.DefaultDavProperty;
import org.apache.jackrabbit.webdav.property.ResourceType;
import org.cryptomator.filesystem.File;
import org.cryptomator.filesystem.Folder;
import org.cryptomator.filesystem.Node;
import org.cryptomator.filesystem.WritableFile;
@@ -98,17 +97,7 @@ class DavFolder extends DavNode<FolderLocator> {
@Override
public void removeMember(DavResource member) throws DavException {
final Node child = getMemberNode(member.getDisplayName());
if (child instanceof Folder) {
Folder folder = (Folder) child;
folder.delete();
} else if (child instanceof File) {
File file = (File) child;
try (WritableFile writable = file.openWritable()) {
writable.delete();
}
} else {
throw new IllegalStateException("Unexpected node type: " + child.getClass().getName());
}
child.delete();
}
/**