diff --git a/main/filesystem-api/src/main/java/org/cryptomator/filesystem/File.java b/main/filesystem-api/src/main/java/org/cryptomator/filesystem/File.java index 45c1f0d90..ddcefd11a 100644 --- a/main/filesystem-api/src/main/java/org/cryptomator/filesystem/File.java +++ b/main/filesystem-api/src/main/java/org/cryptomator/filesystem/File.java @@ -73,23 +73,9 @@ public interface File extends Node, Comparable { Copier.copy(this, destination); } - default void moveTo(File destination) { - Mover.move(this, destination); - } - /** - *

- * Deletes the file if it exists. - *

- * Does nothign if the file does not exist. + * Moves this file including content to a new location specified by destination. */ - default void delete() { - if (!exists()) { - return; - } - try (WritableFile writableFile = openWritable()) { - writableFile.delete(); - } - } + void moveTo(File destination) throws UncheckedIOException; } diff --git a/main/filesystem-api/src/main/java/org/cryptomator/filesystem/Folder.java b/main/filesystem-api/src/main/java/org/cryptomator/filesystem/Folder.java index 72bcde132..3bf62065f 100644 --- a/main/filesystem-api/src/main/java/org/cryptomator/filesystem/Folder.java +++ b/main/filesystem-api/src/main/java/org/cryptomator/filesystem/Folder.java @@ -98,14 +98,6 @@ public interface Folder extends Node { Copier.copy(this, target); } - /** - *

- * Deletes the directory including all child elements. - *

- * 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. diff --git a/main/filesystem-api/src/main/java/org/cryptomator/filesystem/Mover.java b/main/filesystem-api/src/main/java/org/cryptomator/filesystem/Mover.java deleted file mode 100644 index e01cdf3e1..000000000 --- a/main/filesystem-api/src/main/java/org/cryptomator/filesystem/Mover.java +++ /dev/null @@ -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)); - } - } - -} diff --git a/main/filesystem-api/src/main/java/org/cryptomator/filesystem/Node.java b/main/filesystem-api/src/main/java/org/cryptomator/filesystem/Node.java index a5fab7d2c..016c4906c 100644 --- a/main/filesystem-api/src/main/java/org/cryptomator/filesystem/Node.java +++ b/main/filesystem-api/src/main/java/org/cryptomator/filesystem/Node.java @@ -30,10 +30,35 @@ public interface Node { */ Optional parent() throws UncheckedIOException; + /** + * @return true if the node exists. + */ boolean exists() throws UncheckedIOException; + /** + *

+ * Deletes the node if it exists. + *

+ * Does nothing if the node does not exist. + */ + void delete() throws UncheckedIOException; + + /** + *

+ * Determines the last modified date of this node. + * + * @returns the last modified date of the file + */ Instant lastModified() throws UncheckedIOException; + /** + *

+ * Sets the last modified date of the file. + * + * @param lastModified the time to set as creation time + */ + void setLastModified(Instant lastModified) throws UncheckedIOException; + /** *

* 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")); } diff --git a/main/filesystem-api/src/main/java/org/cryptomator/filesystem/WritableFile.java b/main/filesystem-api/src/main/java/org/cryptomator/filesystem/WritableFile.java index b22ae96ea..d5080ae79 100644 --- a/main/filesystem-api/src/main/java/org/cryptomator/filesystem/WritableFile.java +++ b/main/filesystem-api/src/main/java/org/cryptomator/filesystem/WritableFile.java @@ -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 { - /** - *

- * Moves this file including content to another. - *

- * 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; - - /** - *

- * Sets the creation time of the file. - *

- * 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")); - } - - /** - *

- * Deletes this file from the file system. - *

- * 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; /** diff --git a/main/filesystem-api/src/main/java/org/cryptomator/filesystem/delegating/DelegatingFile.java b/main/filesystem-api/src/main/java/org/cryptomator/filesystem/delegating/DelegatingFile.java index 4d8db50ef..49bc9eb6d 100644 --- a/main/filesystem-api/src/main/java/org/cryptomator/filesystem/delegating/DelegatingFile.java +++ b/main/filesystem-api/src/main/java/org/cryptomator/filesystem/delegating/DelegatingFile.java @@ -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> 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> extends D } } - @Override - public Optional creationTime() throws UncheckedIOException { - return delegate.creationTime(); - } - - @Override - public void setCreationTime(Instant instant) throws UncheckedIOException { - delegate.setCreationTime(instant); - } - } diff --git a/main/filesystem-api/src/main/java/org/cryptomator/filesystem/delegating/DelegatingFolder.java b/main/filesystem-api/src/main/java/org/cryptomator/filesystem/delegating/DelegatingFolder.java index 4842e7e82..475acadd7 100644 --- a/main/filesystem-api/src/main/java/org/cryptomator/filesystem/delegating/DelegatingFolder.java +++ b/main/filesystem-api/src/main/java/org/cryptomator/filesystem/delegating/DelegatingFolder.java @@ -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, F exten } } - @Override - public Optional creationTime() throws UncheckedIOException { - return delegate.creationTime(); - } - - @Override - public void setCreationTime(Instant instant) throws UncheckedIOException { - delegate.setCreationTime(instant); - } - } diff --git a/main/filesystem-api/src/main/java/org/cryptomator/filesystem/delegating/DelegatingNode.java b/main/filesystem-api/src/main/java/org/cryptomator/filesystem/delegating/DelegatingNode.java index 48e0fd189..c5e11cb77 100644 --- a/main/filesystem-api/src/main/java/org/cryptomator/filesystem/delegating/DelegatingNode.java +++ b/main/filesystem-api/src/main/java/org/cryptomator/filesystem/delegating/DelegatingNode.java @@ -40,11 +40,21 @@ public abstract class DelegatingNode implements Node { return delegate.lastModified(); } + @Override + public void setLastModified(Instant instant) throws UncheckedIOException { + delegate.setLastModified(instant); + } + @Override public Optional creationTime() throws UncheckedIOException { return delegate.creationTime(); } + @Override + public void setCreationTime(Instant instant) throws UncheckedIOException { + delegate.setCreationTime(instant); + } + @Override public int hashCode() { return delegate.hashCode(); diff --git a/main/filesystem-api/src/main/java/org/cryptomator/filesystem/delegating/DelegatingWritableFile.java b/main/filesystem-api/src/main/java/org/cryptomator/filesystem/delegating/DelegatingWritableFile.java index 66bd05712..8abe702a5 100644 --- a/main/filesystem-api/src/main/java/org/cryptomator/filesystem/delegating/DelegatingWritableFile.java +++ b/main/filesystem-api/src/main/java/org/cryptomator/filesystem/delegating/DelegatingWritableFile.java @@ -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); - } - } diff --git a/main/filesystem-api/src/test/java/org/cryptomator/filesystem/delegating/DelegatingFileTest.java b/main/filesystem-api/src/test/java/org/cryptomator/filesystem/delegating/DelegatingFileTest.java index d7114c816..2e20b1de4 100644 --- a/main/filesystem-api/src/test/java/org/cryptomator/filesystem/delegating/DelegatingFileTest.java +++ b/main/filesystem-api/src/test/java/org/cryptomator/filesystem/delegating/DelegatingFileTest.java @@ -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); diff --git a/main/filesystem-api/src/test/java/org/cryptomator/filesystem/delegating/DelegatingFolderTest.java b/main/filesystem-api/src/test/java/org/cryptomator/filesystem/delegating/DelegatingFolderTest.java index c50069c97..f7cc443d7 100644 --- a/main/filesystem-api/src/test/java/org/cryptomator/filesystem/delegating/DelegatingFolderTest.java +++ b/main/filesystem-api/src/test/java/org/cryptomator/filesystem/delegating/DelegatingFolderTest.java @@ -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); - } - } diff --git a/main/filesystem-api/src/test/java/org/cryptomator/filesystem/delegating/DelegatingWritableFileTest.java b/main/filesystem-api/src/test/java/org/cryptomator/filesystem/delegating/DelegatingWritableFileTest.java index 856049d75..67aa3a1b9 100644 --- a/main/filesystem-api/src/test/java/org/cryptomator/filesystem/delegating/DelegatingWritableFileTest.java +++ b/main/filesystem-api/src/test/java/org/cryptomator/filesystem/delegating/DelegatingWritableFileTest.java @@ -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); - } - } diff --git a/main/filesystem-crypto/src/main/java/org/cryptomator/filesystem/blockaligned/BlockAlignedWritableFile.java b/main/filesystem-crypto/src/main/java/org/cryptomator/filesystem/blockaligned/BlockAlignedWritableFile.java index 7df2c6257..a80fb87b5 100644 --- a/main/filesystem-crypto/src/main/java/org/cryptomator/filesystem/blockaligned/BlockAlignedWritableFile.java +++ b/main/filesystem-crypto/src/main/java/org/cryptomator/filesystem/blockaligned/BlockAlignedWritableFile.java @@ -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(); diff --git a/main/filesystem-crypto/src/main/java/org/cryptomator/filesystem/crypto/CryptoFile.java b/main/filesystem-crypto/src/main/java/org/cryptomator/filesystem/crypto/CryptoFile.java index 9cdcecfbf..dd1863d04 100644 --- a/main/filesystem-crypto/src/main/java/org/cryptomator/filesystem/crypto/CryptoFile.java +++ b/main/filesystem-crypto/src/main/java/org/cryptomator/filesystem/crypto/CryptoFile.java @@ -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."); + } + } + } diff --git a/main/filesystem-crypto/src/main/java/org/cryptomator/filesystem/crypto/CryptoNode.java b/main/filesystem-crypto/src/main/java/org/cryptomator/filesystem/crypto/CryptoNode.java index da2db90d6..47993924a 100644 --- a/main/filesystem-crypto/src/main/java/org/cryptomator/filesystem/crypto/CryptoNode.java +++ b/main/filesystem-crypto/src/main/java/org/cryptomator/filesystem/crypto/CryptoNode.java @@ -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 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 diff --git a/main/filesystem-crypto/src/main/java/org/cryptomator/filesystem/crypto/CryptoWritableFile.java b/main/filesystem-crypto/src/main/java/org/cryptomator/filesystem/crypto/CryptoWritableFile.java index 969d871ec..0c4e57d07 100644 --- a/main/filesystem-crypto/src/main/java/org/cryptomator/filesystem/crypto/CryptoWritableFile.java +++ b/main/filesystem-crypto/src/main/java/org/cryptomator/filesystem/crypto/CryptoWritableFile.java @@ -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; diff --git a/main/filesystem-inmemory/src/main/java/org/cryptomator/filesystem/inmem/InMemoryFile.java b/main/filesystem-inmemory/src/main/java/org/cryptomator/filesystem/inmem/InMemoryFile.java index f5ea36a9e..009652859 100644 --- a/main/filesystem-inmemory/src/main/java/org/cryptomator/filesystem/inmem/InMemoryFile.java +++ b/main/filesystem-inmemory/src/main/java/org/cryptomator/filesystem/inmem/InMemoryFile.java @@ -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() { diff --git a/main/filesystem-inmemory/src/main/java/org/cryptomator/filesystem/inmem/InMemoryNode.java b/main/filesystem-inmemory/src/main/java/org/cryptomator/filesystem/inmem/InMemoryNode.java index 55dd2d012..3aa40cc82 100644 --- a/main/filesystem-inmemory/src/main/java/org/cryptomator/filesystem/inmem/InMemoryNode.java +++ b/main/filesystem-inmemory/src/main/java/org/cryptomator/filesystem/inmem/InMemoryNode.java @@ -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 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 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; - } - } diff --git a/main/filesystem-inmemory/src/main/java/org/cryptomator/filesystem/inmem/InMemoryWritableFile.java b/main/filesystem-inmemory/src/main/java/org/cryptomator/filesystem/inmem/InMemoryWritableFile.java index d9559fc90..514862b96 100644 --- a/main/filesystem-inmemory/src/main/java/org/cryptomator/filesystem/inmem/InMemoryWritableFile.java +++ b/main/filesystem-inmemory/src/main/java/org/cryptomator/filesystem/inmem/InMemoryWritableFile.java @@ -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 lastModifiedSetter; - private final Consumer creationTimeSetter; private final Supplier contentGetter; private final Consumer contentSetter; - private final Runnable deleter; private final WriteLock writeLock; private boolean open = true; private volatile int position = 0; - public InMemoryWritableFile(Consumer lastModifiedSetter, Consumer creationTimeSetter, Supplier contentGetter, Consumer contentSetter, Runnable deleter, WriteLock writeLock) { - this.lastModifiedSetter = lastModifiedSetter; + public InMemoryWritableFile(Supplier contentGetter, Consumer 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); } } diff --git a/main/filesystem-inmemory/src/test/java/org/cryptomator/filesystem/inmem/InMemoryFileSystemTest.java b/main/filesystem-inmemory/src/test/java/org/cryptomator/filesystem/inmem/InMemoryFileSystemTest.java index a3aac2f18..7b913b0ba 100644 --- a/main/filesystem-inmemory/src/test/java/org/cryptomator/filesystem/inmem/InMemoryFileSystemTest.java +++ b/main/filesystem-inmemory/src/test/java/org/cryptomator/filesystem/inmem/InMemoryFileSystemTest.java @@ -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()); diff --git a/main/filesystem-inmemory/src/test/java/org/cryptomator/filesystem/inmem/InMemoryFileTest.java b/main/filesystem-inmemory/src/test/java/org/cryptomator/filesystem/inmem/InMemoryFileTest.java index f3879fd04..d70f8f242 100644 --- a/main/filesystem-inmemory/src/test/java/org/cryptomator/filesystem/inmem/InMemoryFileTest.java +++ b/main/filesystem-inmemory/src/test/java/org/cryptomator/filesystem/inmem/InMemoryFileTest.java @@ -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)); } } diff --git a/main/filesystem-nameshortening/src/test/java/org/cryptomator/filesystem/shortening/ShorteningFileSystemTest.java b/main/filesystem-nameshortening/src/test/java/org/cryptomator/filesystem/shortening/ShorteningFileSystemTest.java index 3ccc6281b..d6655d9d5 100644 --- a/main/filesystem-nameshortening/src/test/java/org/cryptomator/filesystem/shortening/ShorteningFileSystemTest.java +++ b/main/filesystem-nameshortening/src/test/java/org/cryptomator/filesystem/shortening/ShorteningFileSystemTest.java @@ -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()); } diff --git a/main/filesystem-nio/src/main/java/org/cryptomator/filesystem/nio/DefaultInstanceFactory.java b/main/filesystem-nio/src/main/java/org/cryptomator/filesystem/nio/DefaultInstanceFactory.java index a53aace0e..3b450a21a 100644 --- a/main/filesystem-nio/src/main/java/org/cryptomator/filesystem/nio/DefaultInstanceFactory.java +++ b/main/filesystem-nio/src/main/java/org/cryptomator/filesystem/nio/DefaultInstanceFactory.java @@ -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 diff --git a/main/filesystem-nio/src/main/java/org/cryptomator/filesystem/nio/InstanceFactory.java b/main/filesystem-nio/src/main/java/org/cryptomator/filesystem/nio/InstanceFactory.java index d0b9deb69..4a5fee15a 100644 --- a/main/filesystem-nio/src/main/java/org/cryptomator/filesystem/nio/InstanceFactory.java +++ b/main/filesystem-nio/src/main/java/org/cryptomator/filesystem/nio/InstanceFactory.java @@ -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); diff --git a/main/filesystem-nio/src/main/java/org/cryptomator/filesystem/nio/NioFile.java b/main/filesystem-nio/src/main/java/org/cryptomator/filesystem/nio/NioFile.java index d3affa509..5e38a8e25 100644 --- a/main/filesystem-nio/src/main/java/org/cryptomator/filesystem/nio/NioFile.java +++ b/main/filesystem-nio/src/main/java/org/cryptomator/filesystem/nio/NioFile.java @@ -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 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 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 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 creationTime() throws UncheckedIOException { - if (nioAccess.exists(path) && !exists()) { - throw new UncheckedIOException(new IOException(format("%s is a folder", path))); - } - return super.creationTime(); - } - } diff --git a/main/filesystem-nio/src/main/java/org/cryptomator/filesystem/nio/NioNode.java b/main/filesystem-nio/src/main/java/org/cryptomator/filesystem/nio/NioNode.java index f7ed1ac8c..6a82c7af9 100644 --- a/main/filesystem-nio/src/main/java/org/cryptomator/filesystem/nio/NioNode.java +++ b/main/filesystem-nio/src/main/java/org/cryptomator/filesystem/nio/NioNode.java @@ -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 creationTime() throws UncheckedIOException { diff --git a/main/filesystem-nio/src/main/java/org/cryptomator/filesystem/nio/WritableNioFile.java b/main/filesystem-nio/src/main/java/org/cryptomator/filesystem/nio/WritableNioFile.java index d0415f582..6db159bf6 100644 --- a/main/filesystem-nio/src/main/java/org/cryptomator/filesystem/nio/WritableNioFile.java +++ b/main/filesystem-nio/src/main/java/org/cryptomator/filesystem/nio/WritableNioFile.java @@ -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) { diff --git a/main/filesystem-nio/src/test/java/org/cryptomator/filesystem/nio/DefaultInstanceFactoryTest.java b/main/filesystem-nio/src/test/java/org/cryptomator/filesystem/nio/DefaultInstanceFactoryTest.java index 99343de30..0aeb80c99 100644 --- a/main/filesystem-nio/src/test/java/org/cryptomator/filesystem/nio/DefaultInstanceFactoryTest.java +++ b/main/filesystem-nio/src/test/java/org/cryptomator/filesystem/nio/DefaultInstanceFactoryTest.java @@ -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(); } diff --git a/main/filesystem-nio/src/test/java/org/cryptomator/filesystem/nio/NioFileTest.java b/main/filesystem-nio/src/test/java/org/cryptomator/filesystem/nio/NioFileTest.java index 401020e2b..489849f24 100644 --- a/main/filesystem-nio/src/test/java/org/cryptomator/filesystem/nio/NioFileTest.java +++ b/main/filesystem-nio/src/test/java/org/cryptomator/filesystem/nio/NioFileTest.java @@ -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 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 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); diff --git a/main/filesystem-nio/src/test/java/org/cryptomator/filesystem/nio/WritableNioFileTest.java b/main/filesystem-nio/src/test/java/org/cryptomator/filesystem/nio/WritableNioFileTest.java index 27577e833..ab9ebd0b1 100644 --- a/main/filesystem-nio/src/test/java/org/cryptomator/filesystem/nio/WritableNioFileTest.java +++ b/main/filesystem-nio/src/test/java/org/cryptomator/filesystem/nio/WritableNioFileTest.java @@ -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 diff --git a/main/frontend-webdav/src/main/java/org/cryptomator/frontend/webdav/jackrabbitservlet/DavFile.java b/main/frontend-webdav/src/main/java/org/cryptomator/frontend/webdav/jackrabbitservlet/DavFile.java index 951476555..2122dc7eb 100644 --- a/main/frontend-webdav/src/main/java/org/cryptomator/frontend/webdav/jackrabbitservlet/DavFile.java +++ b/main/frontend-webdav/src/main/java/org/cryptomator/frontend/webdav/jackrabbitservlet/DavFile.java @@ -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 { } @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 diff --git a/main/frontend-webdav/src/main/java/org/cryptomator/frontend/webdav/jackrabbitservlet/DavFolder.java b/main/frontend-webdav/src/main/java/org/cryptomator/frontend/webdav/jackrabbitservlet/DavFolder.java index 1d3cbf313..33bd5c51f 100644 --- a/main/frontend-webdav/src/main/java/org/cryptomator/frontend/webdav/jackrabbitservlet/DavFolder.java +++ b/main/frontend-webdav/src/main/java/org/cryptomator/frontend/webdav/jackrabbitservlet/DavFolder.java @@ -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 { @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(); } /**