Integrated BlacklistingFileSystem into ShorteningFileSystem

This commit is contained in:
Markus Kreusch
2016-03-02 21:57:43 +01:00
parent c306151980
commit e57ee67208
11 changed files with 114 additions and 314 deletions

View File

@@ -60,12 +60,12 @@ class FileSystemFactories implements Iterable<FileSystemFactory> {
private FileSystem createShorteningFileSystemNio() {
FileSystem delegate = createNioFileSystem();
return new ShorteningFileSystem(delegate.folder("d"), delegate.folder("m"), 3);
return new ShorteningFileSystem(delegate, "m", 3);
}
private FileSystem createShorteningFileSystemInMemory() {
FileSystem delegate = createInMemoryFileSystem();
return new ShorteningFileSystem(delegate.folder("d"), delegate.folder("m"), 3);
return new ShorteningFileSystem(delegate, "m", 3);
}
private void add(String name, FileSystemFactory factory) {

View File

@@ -1,20 +0,0 @@
/*******************************************************************************
* Copyright (c) 2016 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.blacklisting;
import org.cryptomator.filesystem.File;
import org.cryptomator.filesystem.delegating.DelegatingFile;
class BlacklistingFile extends DelegatingFile<BlacklistingFolder> {
public BlacklistingFile(BlacklistingFolder parent, File delegate) {
super(parent, delegate);
}
}

View File

@@ -1,28 +0,0 @@
/*******************************************************************************
* Copyright (c) 2016 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.blacklisting;
import java.util.function.Predicate;
import org.cryptomator.filesystem.Folder;
import org.cryptomator.filesystem.Node;
import org.cryptomator.filesystem.delegating.DelegatingFileSystem;
class BlacklistingFileSystem extends BlacklistingFolder implements DelegatingFileSystem {
public BlacklistingFileSystem(Folder root, Predicate<Node> hiddenNodes) {
super(null, root, hiddenNodes);
}
@Override
public Folder getDelegate() {
return delegate;
}
}

View File

@@ -1,30 +0,0 @@
/*******************************************************************************
* Copyright (c) 2016 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.blacklisting;
import java.util.function.Predicate;
import javax.inject.Inject;
import javax.inject.Singleton;
import org.cryptomator.filesystem.FileSystem;
import org.cryptomator.filesystem.Folder;
import org.cryptomator.filesystem.Node;
@Singleton
public class BlacklistingFileSystemFactory {
@Inject
public BlacklistingFileSystemFactory() {
}
public FileSystem get(Folder root, Predicate<Node> hiddenFiles) {
return new BlacklistingFileSystem(root, hiddenFiles);
}
}

View File

@@ -1,61 +0,0 @@
/*******************************************************************************
* Copyright (c) 2016 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.blacklisting;
import java.io.UncheckedIOException;
import java.nio.file.FileAlreadyExistsException;
import java.util.function.Predicate;
import java.util.stream.Stream;
import org.cryptomator.filesystem.File;
import org.cryptomator.filesystem.Folder;
import org.cryptomator.filesystem.Node;
import org.cryptomator.filesystem.delegating.DelegatingFolder;
class BlacklistingFolder extends DelegatingFolder<BlacklistingFolder, BlacklistingFile> {
private final Predicate<Node> hiddenNodes;
public BlacklistingFolder(BlacklistingFolder parent, Folder delegate, Predicate<Node> hiddenNodes) {
super(parent, delegate);
this.hiddenNodes = hiddenNodes;
}
@Override
public Stream<? extends Node> children() {
return Stream.concat(folders(), files());
}
@Override
public Stream<BlacklistingFolder> folders() {
return delegate.folders().filter(hiddenNodes.negate()).map(this::newFolder);
}
@Override
public Stream<BlacklistingFile> files() {
return delegate.files().filter(hiddenNodes.negate()).map(this::newFile);
}
@Override
protected BlacklistingFile newFile(File delegate) {
if (hiddenNodes.test(delegate)) {
throw new UncheckedIOException("'" + delegate.name() + "' is a reserved name.", new FileAlreadyExistsException(delegate.name()));
}
return new BlacklistingFile(this, delegate);
}
@Override
protected BlacklistingFolder newFolder(Folder delegate) {
if (hiddenNodes.test(delegate)) {
throw new UncheckedIOException("'" + delegate.name() + "' is a reserved name.", new FileAlreadyExistsException(delegate.name()));
}
return new BlacklistingFolder(this, delegate, hiddenNodes);
}
}

View File

@@ -1,34 +0,0 @@
/*******************************************************************************
* Copyright (c) 2016 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.blacklisting;
import java.util.Objects;
import java.util.function.Predicate;
import org.cryptomator.filesystem.Node;
public class SamePathPredicate implements Predicate<Node> {
private final Node node;
private SamePathPredicate(Node node) {
Objects.requireNonNull(node);
this.node = node;
}
@Override
public boolean test(Node other) {
return node.parent().equals(other.parent()) && node.name().equals(other.name());
}
public static SamePathPredicate forNode(Node node) {
return new SamePathPredicate(node);
}
}

View File

@@ -8,16 +8,49 @@
*******************************************************************************/
package org.cryptomator.filesystem.shortening;
import java.io.IOException;
import java.io.UncheckedIOException;
import java.util.stream.Stream;
import org.cryptomator.filesystem.Folder;
import org.cryptomator.filesystem.Node;
import org.cryptomator.filesystem.delegating.DelegatingFileSystem;
public class ShorteningFileSystem extends ShorteningFolder implements DelegatingFileSystem {
public ShorteningFileSystem(Folder root, Folder metadataRoot, int threshold) {
super(null, root, "", new FilenameShortener(metadataRoot, threshold));
private final String metadataFolderName;
public ShorteningFileSystem(Folder root, String metadataFolderName, int threshold) {
super(null, root, "", new FilenameShortener(root.resolveFolder(metadataFolderName), threshold));
this.metadataFolderName = metadataFolderName;
create();
}
@Override
public Stream<ShorteningFolder> folders() {
return super.folders().filter(this::nameIsNotNameOfMetadataFolder);
}
@Override
public ShorteningFile file(String name) throws UncheckedIOException {
if (metadataFolderName.equals(name)) {
throw new UncheckedIOException(new IOException("'" + name + "' is a reserved name."));
}
return super.file(name);
}
@Override
public ShorteningFolder folder(String name) throws UncheckedIOException {
if (metadataFolderName.equals(name)) {
throw new UncheckedIOException(new IOException("'" + name + "' is a reserved name."));
}
return super.folder(name);
}
private boolean nameIsNotNameOfMetadataFolder(Node node) {
return !metadataFolderName.equals(node.name());
}
@Override
public Folder getDelegate() {
return delegate;

View File

@@ -13,8 +13,6 @@ import javax.inject.Singleton;
import org.cryptomator.filesystem.FileSystem;
import org.cryptomator.filesystem.Folder;
import org.cryptomator.filesystem.blacklisting.BlacklistingFileSystemFactory;
import org.cryptomator.filesystem.blacklisting.SamePathPredicate;
@Singleton
public class ShorteningFileSystemFactory {
@@ -22,16 +20,11 @@ public class ShorteningFileSystemFactory {
private static final int SHORTENING_THRESHOLD = 129; // 128 + "_"
private static final String METADATA_FOLDER_NAME = "m";
private final BlacklistingFileSystemFactory blacklistingFileSystemFactory;
@Inject
public ShorteningFileSystemFactory(BlacklistingFileSystemFactory blacklistingFileSystemFactory) {
this.blacklistingFileSystemFactory = blacklistingFileSystemFactory;
public ShorteningFileSystemFactory() {
}
public FileSystem get(Folder root) {
final Folder metadataFolder = root.folder(METADATA_FOLDER_NAME);
final FileSystem metadataHidingFs = blacklistingFileSystemFactory.get(root, SamePathPredicate.forNode(metadataFolder));
return new ShorteningFileSystem(metadataHidingFs, metadataFolder, SHORTENING_THRESHOLD);
return new ShorteningFileSystem(root, METADATA_FOLDER_NAME, SHORTENING_THRESHOLD);
}
}

View File

@@ -1,67 +0,0 @@
/*******************************************************************************
* Copyright (c) 2016 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.blacklisting;
import java.io.IOException;
import java.io.UncheckedIOException;
import java.nio.ByteBuffer;
import java.nio.channels.WritableByteChannel;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import org.cryptomator.filesystem.File;
import org.cryptomator.filesystem.FileSystem;
import org.cryptomator.filesystem.Folder;
import org.cryptomator.filesystem.Node;
import org.cryptomator.filesystem.inmem.InMemoryFileSystem;
import org.junit.Assert;
import org.junit.Test;
public class BlacklistingFileSystemTest {
@Test(expected = UncheckedIOException.class)
public void testPreventCreationOfBlacklistedFolder() {
final FileSystem underlyingFs = new InMemoryFileSystem();
final Node blacklisted = underlyingFs.folder("qwe");
final FileSystem fs = new BlacklistingFileSystem(underlyingFs, SamePathPredicate.forNode(blacklisted));
fs.folder("qwe");
}
@Test(expected = UncheckedIOException.class)
public void testPreventCreationOBlacklistedFile() {
final FileSystem underlyingFs = new InMemoryFileSystem();
final Node blacklisted = underlyingFs.folder("qwe");
final FileSystem fs = new BlacklistingFileSystem(underlyingFs, SamePathPredicate.forNode(blacklisted));
fs.file("qwe");
}
@Test
public void testBlacklistingOfFilesAndFolders() throws IOException {
final FileSystem underlyingFs = new InMemoryFileSystem();
final Folder hiddenFolder = underlyingFs.folder("asd");
final File hiddenFile = underlyingFs.file("qwe");
final Folder visibleFolder = underlyingFs.folder("sdf");
final File visibleFile = underlyingFs.file("wer");
final Predicate<Node> hiddenPredicate = SamePathPredicate.forNode(hiddenFolder).or(SamePathPredicate.forNode(hiddenFile));
final FileSystem fs = new BlacklistingFileSystem(underlyingFs, hiddenPredicate);
hiddenFolder.create();
try (WritableByteChannel writable = hiddenFile.openWritable()) {
writable.write(ByteBuffer.allocate(0));
}
visibleFolder.create();
try (WritableByteChannel writable = visibleFile.openWritable()) {
writable.write(ByteBuffer.allocate(0));
}
Assert.assertArrayEquals(new String[] {"sdf"}, fs.folders().map(Node::name).collect(Collectors.toList()).toArray());
Assert.assertArrayEquals(new String[] {"wer"}, fs.files().map(Node::name).collect(Collectors.toList()).toArray());
Assert.assertArrayEquals(new String[] {"sdf", "wer"}, fs.children().map(Node::name).sorted().collect(Collectors.toList()).toArray());
}
}

View File

@@ -1,44 +0,0 @@
/*******************************************************************************
* Copyright (c) 2016 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.blacklisting;
import org.cryptomator.filesystem.File;
import org.cryptomator.filesystem.FileSystem;
import org.cryptomator.filesystem.Folder;
import org.cryptomator.filesystem.inmem.InMemoryFileSystem;
import org.junit.Assert;
import org.junit.Test;
public class SamePathPredicateTest {
@Test
public void testFileAndFolderWithSameNameWithSameParentConsideredSame() {
FileSystem fs = new InMemoryFileSystem();
File file1 = fs.file("foo");
Folder folder1 = fs.folder("foo");
Assert.assertTrue(SamePathPredicate.forNode(file1).test(folder1));
}
@Test
public void testFilesWithDifferentParentConsideredDifferent() {
FileSystem fs = new InMemoryFileSystem();
File file1 = fs.file("foo");
File file2 = fs.folder("bar").file("foo");
Assert.assertFalse(SamePathPredicate.forNode(file1).test(file2));
}
@Test
public void testFilesWithDifferentNamesConsideredDifferent() {
FileSystem fs = new InMemoryFileSystem();
File file1 = fs.file("foo");
File file2 = fs.file("bar");
Assert.assertFalse(SamePathPredicate.forNode(file1).test(file2));
}
}

View File

@@ -8,36 +8,93 @@
*******************************************************************************/
package org.cryptomator.filesystem.shortening;
import static java.lang.String.format;
import static java.util.stream.Collectors.toList;
import static org.cryptomator.common.test.matcher.ContainsMatcher.contains;
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 java.util.concurrent.TimeoutException;
import org.cryptomator.common.test.matcher.PropertyMatcher;
import org.cryptomator.filesystem.File;
import org.cryptomator.filesystem.FileSystem;
import org.cryptomator.filesystem.Folder;
import org.cryptomator.filesystem.ReadableFile;
import org.cryptomator.filesystem.WritableFile;
import org.cryptomator.filesystem.inmem.InMemoryFileSystem;
import org.hamcrest.Matcher;
import org.junit.Assert;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
public class ShorteningFileSystemTest {
@Rule
public ExpectedException thrown = ExpectedException.none();
private static final String METADATA_DIR_NAME = "m";
private static final int THRESHOLD = 10;
private static final String NAME_LONGER_THAN_THRESHOLD = "morethantenchars";
@Test
public void testImplicitCreationOfMetadataFolder() {
final FileSystem underlyingFs = new InMemoryFileSystem();
final Folder metadataRoot = underlyingFs.folder("m");
final FileSystem fs = new ShorteningFileSystem(underlyingFs, metadataRoot, 10);
fs.folder("morethantenchars").create();
final Folder metadataRoot = underlyingFs.folder(METADATA_DIR_NAME);
final FileSystem fs = new ShorteningFileSystem(underlyingFs, METADATA_DIR_NAME, THRESHOLD);
fs.folder(NAME_LONGER_THAN_THRESHOLD).create();
Assert.assertTrue(metadataRoot.exists());
}
@Test
public void testMetadataFolderIsNotIncludedInFolderListing() {
final FileSystem underlyingFs = new InMemoryFileSystem();
final FileSystem fs = new ShorteningFileSystem(underlyingFs, METADATA_DIR_NAME, THRESHOLD);
fs.folder(NAME_LONGER_THAN_THRESHOLD).create();
assertThat(fs.folders().collect(toList()), contains(folderWithName(NAME_LONGER_THAN_THRESHOLD)));
}
@Test
public void testMetadataFolderIsNotIncludedInChildrenListing() {
final FileSystem underlyingFs = new InMemoryFileSystem();
final FileSystem fs = new ShorteningFileSystem(underlyingFs, METADATA_DIR_NAME, THRESHOLD);
fs.folder(NAME_LONGER_THAN_THRESHOLD).create();
assertThat(fs.children().collect(toList()), contains(folderWithName(NAME_LONGER_THAN_THRESHOLD)));
}
@Test
public void testCanNotObtainFolderWithNameOfMetadataFolder() {
final FileSystem underlyingFs = new InMemoryFileSystem();
final FileSystem fs = new ShorteningFileSystem(underlyingFs, METADATA_DIR_NAME, THRESHOLD);
thrown.expect(UncheckedIOException.class);
thrown.expectMessage(format("'%s' is a reserved name", METADATA_DIR_NAME));
fs.folder(METADATA_DIR_NAME);
}
@Test
public void testCanNotObtainFileWithNameOfMetadataFolder() {
final FileSystem underlyingFs = new InMemoryFileSystem();
final FileSystem fs = new ShorteningFileSystem(underlyingFs, METADATA_DIR_NAME, THRESHOLD);
thrown.expect(UncheckedIOException.class);
thrown.expectMessage(format("'%s' is a reserved name", METADATA_DIR_NAME));
fs.file(METADATA_DIR_NAME);
}
@Test
public void testDeflate() {
final FileSystem underlyingFs = new InMemoryFileSystem();
final Folder metadataRoot = underlyingFs.folder("m");
final FileSystem fs = new ShorteningFileSystem(underlyingFs, metadataRoot, 10);
final Folder metadataRoot = underlyingFs.folder(METADATA_DIR_NAME);
final FileSystem fs = new ShorteningFileSystem(underlyingFs, METADATA_DIR_NAME, 10);
final Folder longNamedFolder = fs.folder("morethantenchars"); // base32(sha1(morethantenchars)) = QMJL5GQUETRX2YRV6XDTJQ6NNM7IEUHP
final File correspondingMetadataFile = metadataRoot.folder("QM").folder("JL").file("QMJL5GQUETRX2YRV6XDTJQ6NNM7IEUHP.lng");
longNamedFolder.create();
@@ -48,9 +105,9 @@ public class ShorteningFileSystemTest {
@Test
public void testMoveLongFolders() {
final FileSystem underlyingFs = new InMemoryFileSystem();
final Folder metadataRoot = underlyingFs.folder("m");
final Folder metadataRoot = underlyingFs.folder(METADATA_DIR_NAME);
metadataRoot.create();
final FileSystem fs = new ShorteningFileSystem(underlyingFs, metadataRoot, 10);
final FileSystem fs = new ShorteningFileSystem(underlyingFs, METADATA_DIR_NAME, THRESHOLD);
final Folder shortNamedFolder = fs.folder("test");
shortNamedFolder.create();
@@ -64,9 +121,9 @@ public class ShorteningFileSystemTest {
@Test
public void testMoveLongFiles() throws UncheckedIOException, TimeoutException {
final FileSystem underlyingFs = new InMemoryFileSystem();
final Folder metadataRoot = underlyingFs.folder("m");
final Folder metadataRoot = underlyingFs.folder(METADATA_DIR_NAME);
metadataRoot.create();
final FileSystem fs = new ShorteningFileSystem(underlyingFs, metadataRoot, 10);
final FileSystem fs = new ShorteningFileSystem(underlyingFs, METADATA_DIR_NAME, THRESHOLD);
final File shortNamedFolder = fs.file("test");
try (WritableFile file = shortNamedFolder.openWritable()) {
@@ -82,12 +139,11 @@ public class ShorteningFileSystemTest {
@Test
public void testDeflateAndInflateFolder() {
final FileSystem underlyingFs = new InMemoryFileSystem();
final Folder metadataRoot = underlyingFs.folder("m");
final FileSystem fs1 = new ShorteningFileSystem(underlyingFs, metadataRoot, 10);
final FileSystem fs1 = new ShorteningFileSystem(underlyingFs, METADATA_DIR_NAME, THRESHOLD);
final Folder longNamedFolder1 = fs1.folder("morethantenchars");
longNamedFolder1.create();
final FileSystem fs2 = new ShorteningFileSystem(underlyingFs, metadataRoot, 10);
final FileSystem fs2 = new ShorteningFileSystem(underlyingFs, METADATA_DIR_NAME, THRESHOLD);
final Folder longNamedFolder2 = fs2.folder("morethantenchars");
Assert.assertTrue(longNamedFolder2.exists());
}
@@ -95,17 +151,16 @@ public class ShorteningFileSystemTest {
@Test
public void testDeflateAndInflateFolderAndFile() throws UncheckedIOException, TimeoutException {
final FileSystem underlyingFs = new InMemoryFileSystem();
final Folder metadataRoot = underlyingFs.folder("m");
// write:
final FileSystem fs1 = new ShorteningFileSystem(underlyingFs, metadataRoot, 10);
final FileSystem fs1 = new ShorteningFileSystem(underlyingFs, METADATA_DIR_NAME, THRESHOLD);
fs1.folder("morethantenchars").create();
try (WritableFile file = fs1.folder("morethantenchars").file("morethanelevenchars.txt").openWritable()) {
file.write(ByteBuffer.wrap("hello world".getBytes()));
}
// read
final FileSystem fs2 = new ShorteningFileSystem(underlyingFs, metadataRoot, 10);
final FileSystem fs2 = new ShorteningFileSystem(underlyingFs, METADATA_DIR_NAME, THRESHOLD);
try (ReadableFile file = fs2.folder("morethantenchars").file("morethanelevenchars.txt").openReadable()) {
ByteBuffer buf = ByteBuffer.allocate(11);
file.read(buf);
@@ -116,8 +171,7 @@ public class ShorteningFileSystemTest {
@Test
public void testPassthroughShortNamedFiles() throws UncheckedIOException, TimeoutException, InterruptedException {
final FileSystem underlyingFs = new InMemoryFileSystem();
final Folder metadataRoot = underlyingFs.folder("m");
final FileSystem fs = new ShorteningFileSystem(underlyingFs, metadataRoot, 10);
final FileSystem fs = new ShorteningFileSystem(underlyingFs, METADATA_DIR_NAME, THRESHOLD);
final Instant testStart = Instant.now();
@@ -151,4 +205,8 @@ public class ShorteningFileSystemTest {
Assert.assertTrue(fs.folder("foo").file("test2.txt").lastModified().isAfter(testStart));
}
public static Matcher<Folder> folderWithName(String name) {
return new PropertyMatcher<>(Folder.class, Folder::name, "name", is(name));
}
}