first experiments with jackrabbit-filesystem-adapter

This commit is contained in:
Sebastian Stenzel
2015-12-24 00:32:07 +01:00
parent dc5d3e21f3
commit e67c8f2816
12 changed files with 1010 additions and 0 deletions

View File

@@ -0,0 +1,78 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
Copyright (c) 2015 Sebastian Stenzel
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
-->
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.cryptomator</groupId>
<artifactId>main</artifactId>
<version>0.11.0-SNAPSHOT</version>
</parent>
<artifactId>jackrabbit-filesystem-adapter</artifactId>
<name>Jackrabbit filesystem-WebDAV-adapter</name>
<description>WebDAV servlet based on Apache Jackrabbit serving files from a filesystem</description>
<properties>
<jackrabbit.version>2.11.0</jackrabbit.version>
</properties>
<dependencies>
<!-- Filesystem -->
<dependency>
<groupId>org.cryptomator</groupId>
<artifactId>filesystem-api</artifactId>
</dependency>
<!-- Jackrabbit -->
<dependency>
<groupId>org.apache.jackrabbit</groupId>
<artifactId>jackrabbit-webdav</artifactId>
<version>${jackrabbit.version}</version>
</dependency>
<!-- Commons -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
</dependency>
<!-- Test -->
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-server</artifactId>
<version>9.3.3.v20150827</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-webapp</artifactId>
<version>9.3.3.v20150827</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>commons-httpclient</groupId>
<artifactId>commons-httpclient</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.cryptomator</groupId>
<artifactId>filesystem-inmemory</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.jacoco</groupId>
<artifactId>jacoco-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>

View File

@@ -0,0 +1,98 @@
/*******************************************************************************
* 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.webdav.jackrabbit;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.Channels;
import java.nio.channels.WritableByteChannel;
import java.time.Instant;
import org.apache.jackrabbit.webdav.DavException;
import org.apache.jackrabbit.webdav.DavResource;
import org.apache.jackrabbit.webdav.DavResourceIterator;
import org.apache.jackrabbit.webdav.DavResourceLocator;
import org.apache.jackrabbit.webdav.DavSession;
import org.apache.jackrabbit.webdav.io.InputContext;
import org.apache.jackrabbit.webdav.io.OutputContext;
import org.apache.jackrabbit.webdav.lock.LockManager;
import org.cryptomator.filesystem.File;
import org.cryptomator.filesystem.ReadableFile;
import org.cryptomator.filesystem.WritableFile;
class DavFile extends DavNode<File> {
public DavFile(FilesystemResourceFactory factory, LockManager lockManager, DavSession session, DavResourceLocator locator, File node) {
super(factory, lockManager, session, locator, node);
}
@Override
public boolean isCollection() {
return false;
}
@Override
public void spool(OutputContext outputContext) throws IOException {
outputContext.setModificationTime(node.lastModified().toEpochMilli());
if (!outputContext.hasStream()) {
return;
}
try (ReadableFile src = node.openReadable(); WritableByteChannel dst = Channels.newChannel(outputContext.getOutputStream())) {
// TODO filesize before sending content
outputContext.setContentLength(-1l);
ByteBuffer buf = ByteBuffer.allocate(1337);
do {
buf.clear();
src.read(buf);
buf.flip();
} while (dst.write(buf) > 0);
}
}
@Override
public void addMember(DavResource resource, InputContext inputContext) throws DavException {
throw new UnsupportedOperationException();
}
@Override
public DavResourceIterator getMembers() {
throw new UnsupportedOperationException();
}
@Override
public void removeMember(DavResource member) throws DavException {
throw new UnsupportedOperationException();
}
@Override
public void move(DavResource destination) throws DavException {
// TODO Auto-generated method stub
}
@Override
public void copy(DavResource destination, boolean shallow) throws DavException {
// TODO Auto-generated method stub
}
@Override
protected void setModificationTime(Instant instant) {
try (WritableFile writable = node.openWritable()) {
writable.setLastModified(instant);
}
}
@Override
protected void setCreationTime(Instant instant) {
// TODO Auto-generated method stub
}
}

View File

@@ -0,0 +1,104 @@
/*******************************************************************************
* 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.webdav.jackrabbit;
import java.io.IOException;
import java.time.Instant;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.apache.jackrabbit.webdav.DavException;
import org.apache.jackrabbit.webdav.DavResource;
import org.apache.jackrabbit.webdav.DavResourceIterator;
import org.apache.jackrabbit.webdav.DavResourceIteratorImpl;
import org.apache.jackrabbit.webdav.DavResourceLocator;
import org.apache.jackrabbit.webdav.DavSession;
import org.apache.jackrabbit.webdav.io.InputContext;
import org.apache.jackrabbit.webdav.io.OutputContext;
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;
class DavFolder extends DavNode<Folder> {
public DavFolder(FilesystemResourceFactory factory, LockManager lockManager, DavSession session, DavResourceLocator locator, Folder folder) {
super(factory, lockManager, session, locator, folder);
properties.add(new ResourceType(ResourceType.COLLECTION));
properties.add(new DefaultDavProperty<Integer>(DavPropertyName.ISCOLLECTION, 1));
}
@Override
public boolean isCollection() {
return true;
}
@Override
public void spool(OutputContext outputContext) throws IOException {
// no-op
}
@Override
public void addMember(DavResource resource, InputContext inputContext) throws DavException {
// TODO Auto-generated method stub
}
@Override
public DavResourceIterator getMembers() {
final Stream<DavFolder> folders = node.folders().map(this::getMemberFolder);
final Stream<DavFile> files = node.files().map(this::getMemberFile);
return new DavResourceIteratorImpl(Stream.concat(folders, files).collect(Collectors.toList()));
}
private DavFolder getMemberFolder(Folder memberFolder) {
final String subFolderResourcePath = locator.getResourcePath() + memberFolder.name() + '/';
final DavResourceLocator subFolderLocator = locator.getFactory().createResourceLocator(locator.getPrefix(), locator.getWorkspacePath(), subFolderResourcePath);
return factory.createFolder(memberFolder, subFolderLocator, session);
}
private DavFile getMemberFile(File memberFile) {
final String subFolderResourcePath = locator.getResourcePath() + memberFile.name();
final DavResourceLocator subFolderLocator = locator.getFactory().createResourceLocator(locator.getPrefix(), locator.getWorkspacePath(), subFolderResourcePath);
return factory.createFile(memberFile, subFolderLocator, session);
}
@Override
public void removeMember(DavResource member) throws DavException {
// TODO Auto-generated method stub
}
@Override
public void move(DavResource destination) throws DavException {
// TODO Auto-generated method stub
}
@Override
public void copy(DavResource destination, boolean shallow) throws DavException {
// TODO Auto-generated method stub
}
@Override
protected void setModificationTime(Instant instant) {
// TODO Auto-generated method stub
}
@Override
protected void setCreationTime(Instant instant) {
// TODO Auto-generated method stub
}
}

View File

@@ -0,0 +1,228 @@
/*******************************************************************************
* 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.webdav.jackrabbit;
import java.io.UncheckedIOException;
import java.time.Instant;
import java.time.format.DateTimeFormatter;
import java.util.Arrays;
import java.util.List;
import org.apache.commons.io.FilenameUtils;
import org.apache.jackrabbit.webdav.DavException;
import org.apache.jackrabbit.webdav.DavResource;
import org.apache.jackrabbit.webdav.DavResourceLocator;
import org.apache.jackrabbit.webdav.DavSession;
import org.apache.jackrabbit.webdav.MultiStatusResponse;
import org.apache.jackrabbit.webdav.lock.ActiveLock;
import org.apache.jackrabbit.webdav.lock.LockInfo;
import org.apache.jackrabbit.webdav.lock.LockManager;
import org.apache.jackrabbit.webdav.lock.Scope;
import org.apache.jackrabbit.webdav.lock.Type;
import org.apache.jackrabbit.webdav.property.DavProperty;
import org.apache.jackrabbit.webdav.property.DavPropertyName;
import org.apache.jackrabbit.webdav.property.DavPropertyNameSet;
import org.apache.jackrabbit.webdav.property.DavPropertySet;
import org.apache.jackrabbit.webdav.property.PropEntry;
import org.cryptomator.filesystem.Node;
abstract class DavNode<T extends Node> implements DavResource {
private static final String DAV_COMPLIANCE_CLASSES = "1, 2";
private static final String[] DAV_CREATIONDATE_PROPNAMES = {DavPropertyName.CREATIONDATE.getName(), "Win32CreationTime"};
private static final String[] DAV_MODIFIEDDATE_PROPNAMES = {DavPropertyName.GETLASTMODIFIED.getName(), "Win32LastModifiedTime"};
protected final FilesystemResourceFactory factory;
protected final LockManager lockManager;
protected final DavSession session;
protected final DavResourceLocator locator;
protected final T node;
protected final DavPropertySet properties;
public DavNode(FilesystemResourceFactory factory, LockManager lockManager, DavSession session, DavResourceLocator locator, T node) {
this.factory = factory;
this.lockManager = lockManager;
this.session = session;
this.locator = locator;
this.node = node;
this.properties = new DavPropertySet();
}
@Override
public String getComplianceClass() {
return DAV_COMPLIANCE_CLASSES;
}
@Override
public String getSupportedMethods() {
return METHODS;
}
@Override
public boolean exists() {
return node.exists();
}
@Override
public String getDisplayName() {
return node.name();
}
@Override
public DavResourceLocator getLocator() {
return locator;
}
@Override
public String getResourcePath() {
return locator.getResourcePath();
}
@Override
public String getHref() {
return locator.getHref(this.isCollection());
}
@Override
public long getModificationTime() {
try {
return node.lastModified().toEpochMilli();
} catch (UncheckedIOException e) {
return -1l;
}
}
protected abstract void setModificationTime(Instant instant);
protected abstract void setCreationTime(Instant instant);
@Override
public DavPropertyName[] getPropertyNames() {
return getProperties().getPropertyNames();
}
@Override
public DavProperty<?> getProperty(DavPropertyName name) {
return getProperties().get(name);
}
@Override
public DavPropertySet getProperties() {
return properties;
}
@Override
public void setProperty(DavProperty<?> property) throws DavException {
getProperties().add(property);
final String namespacelessPropertyName = property.getName().getName();
if (Arrays.asList(DAV_CREATIONDATE_PROPNAMES).contains(namespacelessPropertyName) && property.getValue() instanceof String) {
final String createDateStr = (String) property.getValue();
final Instant createTime = Instant.from(DateTimeFormatter.RFC_1123_DATE_TIME.parse(createDateStr));
this.setCreationTime(createTime);
} else if (Arrays.asList(DAV_MODIFIEDDATE_PROPNAMES).contains(namespacelessPropertyName) && property.getValue() instanceof String) {
final String lastModifiedTimeStr = (String) property.getValue();
final Instant createTime = Instant.from(DateTimeFormatter.RFC_1123_DATE_TIME.parse(lastModifiedTimeStr));
this.setCreationTime(createTime);
}
}
@Override
public void removeProperty(DavPropertyName propertyName) throws DavException {
getProperties().remove(propertyName);
}
@Override
public MultiStatusResponse alterProperties(List<? extends PropEntry> changeList) throws DavException {
final DavPropertyNameSet names = new DavPropertyNameSet();
for (final PropEntry entry : changeList) {
if (entry instanceof DavProperty) {
final DavProperty<?> prop = (DavProperty<?>) entry;
this.setProperty(prop);
names.add(prop.getName());
} else if (entry instanceof DavPropertyName) {
final DavPropertyName name = (DavPropertyName) entry;
this.removeProperty(name);
names.add(name);
}
}
return new MultiStatusResponse(this, names);
}
@Override
public DavResource getCollection() {
if (locator.isRootLocation()) {
return null;
}
final String parentResource = FilenameUtils.getPathNoEndSeparator(locator.getResourcePath());
final DavResourceLocator parentLocator = locator.getFactory().createResourceLocator(locator.getPrefix(), locator.getWorkspacePath(), parentResource);
try {
return factory.createResource(parentLocator, session);
} catch (DavException e) {
throw new IllegalStateException("Unable to get parent resource with path " + parentLocator.getResourcePath(), e);
}
}
@Override
public boolean isLockable(Type type, Scope scope) {
return true;
}
@Override
public boolean hasLock(Type type, Scope scope) {
return getLock(type, scope) != null;
}
@Override
public ActiveLock getLock(Type type, Scope scope) {
return lockManager.getLock(type, scope, this);
}
@Override
public ActiveLock[] getLocks() {
final ActiveLock exclusiveWriteLock = getLock(Type.WRITE, Scope.EXCLUSIVE);
if (exclusiveWriteLock != null) {
return new ActiveLock[] {exclusiveWriteLock};
} else {
return new ActiveLock[0];
}
}
@Override
public ActiveLock lock(LockInfo reqLockInfo) throws DavException {
return lockManager.createLock(reqLockInfo, this);
}
@Override
public ActiveLock refreshLock(LockInfo reqLockInfo, String lockToken) throws DavException {
return lockManager.refreshLock(reqLockInfo, lockToken, this);
}
@Override
public void unlock(String lockToken) throws DavException {
lockManager.releaseLock(lockToken, this);
}
@Override
public void addLockManager(LockManager lockmgr) {
throw new UnsupportedOperationException("Locks are managed");
}
@Override
public FilesystemResourceFactory getFactory() {
return factory;
}
@Override
public DavSession getSession() {
return session;
}
}

View File

@@ -0,0 +1,45 @@
/*******************************************************************************
* Copyright (c) 2014, 2015 Sebastian Stenzel
* 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.webdav.jackrabbit;
import java.util.HashSet;
import org.apache.jackrabbit.webdav.DavSession;
class DavSessionImpl implements DavSession {
private final HashSet<String> lockTokens = new HashSet<String>();
private final HashSet<Object> references = new HashSet<Object>();
@Override
public void addReference(Object reference) {
references.add(reference);
}
@Override
public void removeReference(Object reference) {
references.remove(reference);
}
@Override
public void addLockToken(String token) {
lockTokens.add(token);
}
@Override
public String[] getLockTokens() {
return lockTokens.toArray(new String[lockTokens.size()]);
}
@Override
public void removeLockToken(String token) {
lockTokens.remove(token);
}
}

View File

@@ -0,0 +1,36 @@
/*******************************************************************************
* Copyright (c) 2014, 2015 Sebastian Stenzel
* 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.webdav.jackrabbit;
import org.apache.jackrabbit.webdav.DavException;
import org.apache.jackrabbit.webdav.DavSession;
import org.apache.jackrabbit.webdav.DavSessionProvider;
import org.apache.jackrabbit.webdav.WebdavRequest;
class DavSessionProviderImpl implements DavSessionProvider {
@Override
public boolean attachSession(WebdavRequest request) throws DavException {
// every request gets a new session
final DavSession session = new DavSessionImpl();
session.addReference(request);
request.setDavSession(session);
return true;
}
@Override
public void releaseSession(WebdavRequest request) {
final DavSession session = request.getDavSession();
if (session != null) {
session.removeReference(request);
request.setDavSession(null);
}
}
}

View File

@@ -0,0 +1,92 @@
/*******************************************************************************
* 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.webdav.jackrabbit;
import java.util.Arrays;
import java.util.Iterator;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.jackrabbit.webdav.DavException;
import org.apache.jackrabbit.webdav.DavResource;
import org.apache.jackrabbit.webdav.DavResourceFactory;
import org.apache.jackrabbit.webdav.DavResourceLocator;
import org.apache.jackrabbit.webdav.DavServletRequest;
import org.apache.jackrabbit.webdav.DavServletResponse;
import org.apache.jackrabbit.webdav.DavSession;
import org.apache.jackrabbit.webdav.lock.LockManager;
import org.apache.jackrabbit.webdav.lock.SimpleLockManager;
import org.cryptomator.filesystem.File;
import org.cryptomator.filesystem.FileSystem;
import org.cryptomator.filesystem.Folder;
import org.cryptomator.filesystem.Node;
class FilesystemResourceFactory implements DavResourceFactory {
private static final Class<Folder> FOLDER = Folder.class;
private static final Class<File> FILE = File.class;
private final FileSystem filesystem;
private final LockManager lockManager;
public FilesystemResourceFactory(FileSystem filesystem) {
this.filesystem = filesystem;
this.lockManager = new SimpleLockManager();
}
@Override
public DavResource createResource(DavResourceLocator locator, DavServletRequest request, DavServletResponse response) throws DavException {
return createResource(locator, request.getDavSession());
}
@Override
public DavResource createResource(DavResourceLocator locator, DavSession session) throws DavException {
final String path = locator.getResourcePath();
if (path.endsWith("/")) {
Folder folder = this.resolve(path, FOLDER);
return createFolder(folder, locator, session);
} else {
File file = this.resolve(path, FILE);
return createFile(file, locator, session);
}
}
DavFolder createFolder(Folder folder, DavResourceLocator locator, DavSession session) {
return new DavFolder(this, lockManager, session, locator, folder);
}
public DavFile createFile(File file, DavResourceLocator locator, DavSession session) {
return new DavFile(this, lockManager, session, locator, file);
}
private <T extends Node> T resolve(String path, Class<T> expectedNodeType) {
final String[] pathFragments = StringUtils.split(path, '/');
if (ArrayUtils.isEmpty(pathFragments)) {
assert expectedNodeType.isAssignableFrom(Folder.class);
return expectedNodeType.cast(filesystem);
} else {
return resolve(filesystem, Arrays.stream(pathFragments).iterator(), expectedNodeType);
}
}
private <T extends Node> T resolve(Folder parent, Iterator<String> pathIterator, Class<T> expectedNodeType) {
assert pathIterator.hasNext();
final String childName = pathIterator.next();
if (pathIterator.hasNext()) {
return resolve(parent.folder(childName), pathIterator, expectedNodeType);
} else if (expectedNodeType.isAssignableFrom(Folder.class)) {
return expectedNodeType.cast(parent.folder(childName));
} else if (expectedNodeType.isAssignableFrom(File.class)) {
return expectedNodeType.cast(parent.file(childName));
} else {
throw new IllegalArgumentException("Supported expectedNodeTypes are File or Folder.");
}
}
}

View File

@@ -0,0 +1,140 @@
/*******************************************************************************
* 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.webdav.jackrabbit;
import java.net.URI;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.jackrabbit.webdav.DavLocatorFactory;
import org.apache.jackrabbit.webdav.DavResourceLocator;
import org.apache.jackrabbit.webdav.util.EncodeUtil;
/**
* A LocatorFactory constructing Locators, whose {@link DavResourceLocator#getResourcePath() resourcePath} and {@link DavResourceLocator#getRepositoryPath() repositoryPath} are equal.
* These paths will be plain, case-sensitive, absolute, unencoded Strings with Unix-style path separators.
*/
class IdentityLocatorFactory implements DavLocatorFactory {
private final String pathPrefix;
public IdentityLocatorFactory(URI contextRootUri) {
this.pathPrefix = StringUtils.removeEnd(contextRootUri.toString(), "/");
}
@Override
public DavResourceLocator createResourceLocator(String prefix, String href) {
final String fullPrefix = StringUtils.removeEnd(prefix, "/");
final String remainingHref = StringUtils.removeStart(href, fullPrefix);
final String unencodedRemaingingHref = EncodeUtil.unescape(remainingHref);
assert unencodedRemaingingHref.startsWith("/");
return new IdentityLocator(unencodedRemaingingHref);
}
@Override
public DavResourceLocator createResourceLocator(String prefix, String workspacePath, String resourcePath) {
assert resourcePath.startsWith("/");
return new IdentityLocator(resourcePath);
}
@Override
public DavResourceLocator createResourceLocator(String prefix, String workspacePath, String path, boolean isResourcePath) {
assert path.startsWith("/");
return new IdentityLocator(path);
}
private class IdentityLocator implements DavResourceLocator {
private final String absPath;
private IdentityLocator(String absPath) {
assert absPath.startsWith("/");
this.absPath = FilenameUtils.normalize(absPath, true);
}
@Override
public String getPrefix() {
return pathPrefix;
}
@Override
public String getResourcePath() {
return absPath;
}
@Override
public String getWorkspacePath() {
return null;
}
@Override
public String getWorkspaceName() {
return null;
}
@Override
public boolean isSameWorkspace(DavResourceLocator locator) {
return false;
}
@Override
public boolean isSameWorkspace(String workspaceName) {
return false;
}
@Override
public String getHref(boolean isCollection) {
final String encodedResourcePath = EncodeUtil.escapePath(absPath);
if (isRootLocation()) {
return pathPrefix + "/";
} else {
assert isCollection ? encodedResourcePath.endsWith("/") : true;
return pathPrefix + encodedResourcePath;
}
}
@Override
public boolean isRootLocation() {
return "/".equals(absPath);
}
@Override
public DavLocatorFactory getFactory() {
return IdentityLocatorFactory.this;
}
@Override
public String getRepositoryPath() {
return absPath;
}
@Override
public String toString() {
return "Locator: " + absPath + " (Prefix: " + pathPrefix + ")";
}
@Override
public int hashCode() {
return absPath.hashCode();
}
@Override
public boolean equals(Object obj) {
if (obj instanceof IdentityLocator) {
final IdentityLocator other = (IdentityLocator) obj;
final boolean samePrefix = this.getPrefix() == null && other.getPrefix() == null || this.getPrefix().equals(other.getPrefix());
final boolean sameRelativeCleartextPath = this.absPath == null && other.absPath == null || this.absPath.equals(other.absPath);
return samePrefix && sameRelativeCleartextPath;
} else {
return false;
}
}
}
}

View File

@@ -0,0 +1,70 @@
/*******************************************************************************
* 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.webdav.jackrabbit;
import java.net.URI;
import org.apache.jackrabbit.webdav.DavLocatorFactory;
import org.apache.jackrabbit.webdav.DavResource;
import org.apache.jackrabbit.webdav.DavResourceFactory;
import org.apache.jackrabbit.webdav.DavSessionProvider;
import org.apache.jackrabbit.webdav.WebdavRequest;
import org.apache.jackrabbit.webdav.server.AbstractWebdavServlet;
import org.cryptomator.filesystem.FileSystem;
public class WebDavServlet extends AbstractWebdavServlet {
private static final long serialVersionUID = -6632687979352625020L;
private final DavSessionProvider davSessionProvider;
private final DavLocatorFactory davLocatorFactory;
private final DavResourceFactory davResourceFactory;
public WebDavServlet(URI contextRootUri, FileSystem filesystem) {
davSessionProvider = new DavSessionProviderImpl();
davLocatorFactory = new IdentityLocatorFactory(contextRootUri);
davResourceFactory = new FilesystemResourceFactory(filesystem);
}
@Override
protected boolean isPreconditionValid(WebdavRequest request, DavResource resource) {
return !resource.exists() || request.matchesIfHeader(resource);
}
@Override
public DavSessionProvider getDavSessionProvider() {
return davSessionProvider;
}
@Override
public void setDavSessionProvider(DavSessionProvider davSessionProvider) {
throw new UnsupportedOperationException("Setting davSessionProvider not supported.");
}
@Override
public DavLocatorFactory getLocatorFactory() {
return davLocatorFactory;
}
@Override
public void setLocatorFactory(DavLocatorFactory locatorFactory) {
throw new UnsupportedOperationException("Setting locatorFactory not supported.");
}
@Override
public DavResourceFactory getResourceFactory() {
return davResourceFactory;
}
@Override
public void setResourceFactory(DavResourceFactory resourceFactory) {
throw new UnsupportedOperationException("Setting resourceFactory not supported.");
}
}

View File

@@ -0,0 +1,34 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!--
Copyright (c) 2014 Markus Kreusch
This file is licensed under the terms of the MIT license.
See the LICENSE.txt file for more info.
Contributors:
Sebastian Stenzel - log4j config for WebDAV unit tests
-->
<Configuration status="WARN">
<Appenders>
<Console name="Console" target="SYSTEM_OUT">
<PatternLayout pattern="%16d %-5p [%c{1}:%L] %m%n" />
<ThresholdFilter level="WARN" onMatch="DENY" onMismatch="ACCEPT" />
</Console>
<Console name="StdErr" target="SYSTEM_ERR">
<PatternLayout pattern="%16d %-5p [%c{1}:%L] %m%n" />
<ThresholdFilter level="WARN" onMatch="ACCEPT" onMismatch="DENY" />
</Console>
</Appenders>
<Loggers>
<!-- show our own debug messages: -->
<Logger name="org.cryptomator" level="DEBUG" />
<Logger name="org.eclipse.jetty.server.Server" level="DEBUG" />
<!-- mute dependencies: -->
<Root level="INFO">
<AppenderRef ref="Console" />
<AppenderRef ref="StdErr" />
</Root>
</Loggers>
</Configuration>

View File

@@ -0,0 +1,84 @@
/*******************************************************************************
* 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.webdav.jackrabbit;
import java.net.URI;
import java.net.URISyntaxException;
import java.nio.ByteBuffer;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
import org.cryptomator.filesystem.FileSystem;
import org.cryptomator.filesystem.FolderCreateMode;
import org.cryptomator.filesystem.WritableFile;
import org.cryptomator.filesystem.inmem.InMemoryFileSystem;
import org.eclipse.jetty.server.Connector;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.ServerConnector;
import org.eclipse.jetty.server.handler.ContextHandlerCollection;
import org.eclipse.jetty.servlet.ServletContextHandler;
import org.eclipse.jetty.servlet.ServletHolder;
import org.eclipse.jetty.util.thread.QueuedThreadPool;
import org.eclipse.jetty.util.thread.ThreadPool;
public class InMemoryWebDavServer {
private final Server server;
private final ServerConnector localConnector;
private final ContextHandlerCollection servletCollection;
private final FileSystem inMemoryFileSystem = new InMemoryFileSystem();
private InMemoryWebDavServer() {
final BlockingQueue<Runnable> queue = new LinkedBlockingQueue<>(10);
final ThreadPool tp = new QueuedThreadPool(4, 1, 1, queue);
server = new Server(tp);
localConnector = new ServerConnector(server);
localConnector.setHost("localhost");
localConnector.setPort(8080);
servletCollection = new ContextHandlerCollection();
URI servletContextRootUri;
try {
servletContextRootUri = new URI("http", null, "localhost", 8080, "/", null, null);
} catch (URISyntaxException e) {
throw new IllegalStateException(e);
}
final ServletContextHandler servletContext = new ServletContextHandler(servletCollection, "/", ServletContextHandler.SESSIONS);
final ServletHolder servletHolder = new ServletHolder("InMemory-WebDAV-Servlet", new WebDavServlet(servletContextRootUri, inMemoryFileSystem));
servletContext.addServlet(servletHolder, "/*");
servletCollection.mapContexts();
server.setConnectors(new Connector[] {localConnector});
server.setHandler(servletCollection);
}
private void start() throws Exception {
server.start();
}
private void stop() throws Exception {
server.stop();
}
public static void main(String[] args) throws Exception {
final InMemoryWebDavServer server = new InMemoryWebDavServer();
server.inMemoryFileSystem.folder("mamals").folder("cats").create(FolderCreateMode.INCLUDING_PARENTS);
server.inMemoryFileSystem.folder("mamals").folder("dogs").create(FolderCreateMode.INCLUDING_PARENTS);
try (WritableFile writable = server.inMemoryFileSystem.folder("mamals").folder("cats").file("Garfield.txt").openWritable()) {
writable.write(ByteBuffer.wrap("meow".getBytes()));
}
server.start();
System.out.println("Server started. Press any key to stop it...");
System.in.read();
server.stop();
}
}

View File

@@ -222,6 +222,7 @@
<module>crypto-aes</module>
<module>core</module>
<module>ui</module>
<module>jackrabbit-filesystem-adapter</module>
</modules>
<profiles>