Skip to content

Commit

Permalink
small api changes regarding servlet creation, added mounting capabili…
Browse files Browse the repository at this point in the history
…ties
  • Loading branch information
overheadhunter committed Dec 20, 2016
1 parent 27ae93b commit a1c8105
Show file tree
Hide file tree
Showing 16 changed files with 470 additions and 70 deletions.
33 changes: 11 additions & 22 deletions src/main/java/org/cryptomator/frontend/webdav/WebDavServer.java
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@
import org.cryptomator.frontend.webdav.WebDavServerModule.BindAddr;
import org.cryptomator.frontend.webdav.WebDavServerModule.CatchAll;
import org.cryptomator.frontend.webdav.WebDavServerModule.ServerPort;
import org.cryptomator.frontend.webdav.servlet.WebDavServletController;
import org.cryptomator.frontend.webdav.servlet.WebDavServletComponent;
import org.eclipse.jetty.server.Connector;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.ServerConnector;
Expand All @@ -40,15 +42,14 @@ public class WebDavServer {

private final Server server;
private final ServerConnector localConnector;
private final ContextHandlerCollection servletCollection;
private final WebDavServletContextFactory servletContextFactory;
private final WebDavServletFactory servletFactory;

@Inject
WebDavServer(@ServerPort int port, @BindAddr String bindAddr, WebDavServletContextFactory servletContextFactory, @CatchAll ServletContextHandler catchAllServletHandler, ThreadPool threadPool) {
WebDavServer(@ServerPort int port, @BindAddr String bindAddr, ContextHandlerCollection servletCollection, WebDavServletFactory servletContextFactory, @CatchAll ServletContextHandler catchAllServletHandler,
ThreadPool threadPool) {
this.server = new Server(threadPool);
this.localConnector = new ServerConnector(server);
this.servletCollection = new ContextHandlerCollection();
this.servletContextFactory = servletContextFactory;
this.servletFactory = servletContextFactory;
localConnector.setHost(bindAddr);
localConnector.setPort(port);
servletCollection.addHandler(catchAllServletHandler);
Expand Down Expand Up @@ -112,21 +113,16 @@ public synchronized void stop() throws ServerLifecycleException {
}

/**
* Registers and starts a new WebDAV servlet.
* Creates a new WebDAV servlet (without starting it yet).
*
* @param rootPath The path to the directory which should be served as root resource.
* @param contextPath The servlet context path, i.e. the path of the root resource.
* @throws ServerLifecycleException If the servlet could not be started for any unexpected reason.
* @return The controller object for this new servlet
*/
public void startWebDavServlet(Path rootPath, String contextPath) throws ServerLifecycleException {
public WebDavServletController createWebDavServlet(Path rootPath, String contextPath) throws ServerLifecycleException {
final URI uri = createUriForContextPath(contextPath);
final ServletContextHandler handler = prepareWebDavServlet(uri, rootPath);
try {
handler.start();
LOG.info("WebDavServlet available under " + uri);
} catch (Exception e) {
throw new ServerLifecycleException("Servlet couldn't be started", e);
}
WebDavServletComponent servletComp = servletFactory.create(uri, rootPath);
return servletComp.servlet();
}

private URI createUriForContextPath(String contextPath) {
Expand All @@ -137,11 +133,4 @@ private URI createUriForContextPath(String contextPath) {
}
}

private ServletContextHandler prepareWebDavServlet(URI contextRoot, Path rootPath) {
ServletContextHandler handler = servletContextFactory.create(contextRoot, rootPath);
servletCollection.addHandler(handler);
servletCollection.mapContexts();
return handler;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@
import javax.inject.Qualifier;
import javax.inject.Singleton;

import org.cryptomator.frontend.webdav.mount.MounterModule;
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.ExecutorThreadPool;
Expand All @@ -28,7 +30,7 @@
import dagger.Module;
import dagger.Provides;

@Module
@Module(includes = {MounterModule.class})
class WebDavServerModule {

private static final int MAX_PENDING_REQUESTS = 400;
Expand Down Expand Up @@ -75,6 +77,13 @@ ThreadPool provideServerThreadPool() {
}

@Provides
@Singleton
ContextHandlerCollection providesContextHandlerCollection() {
return new ContextHandlerCollection();
}

@Provides
@Singleton
@CatchAll
ServletContextHandler createServletContextHandler(DefaultServlet servlet) {
final ServletContextHandler servletContext = new ServletContextHandler(null, ROOT_PATH, ServletContextHandler.NO_SESSIONS);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,15 +16,14 @@

import org.cryptomator.frontend.webdav.servlet.WebDavServletComponent;
import org.cryptomator.frontend.webdav.servlet.WebDavServletModule;
import org.eclipse.jetty.servlet.ServletContextHandler;

@Singleton
class WebDavServletContextFactory {
class WebDavServletFactory {

private final WebDavServerComponent component;

@Inject
public WebDavServletContextFactory(WebDavServerComponent component) {
public WebDavServletFactory(WebDavServerComponent component) {
this.component = component;
}

Expand All @@ -39,13 +38,11 @@ public WebDavServletContextFactory(WebDavServerComponent component) {
*
* @param contextRoot The URI of the context root. Its path will be used as the servlet's context path.
* @param rootPath The location within a filesystem that shall be served via WebDAV.
* @return A new Jetty servlet context handler.
* @return A new WebDAV servlet component.
*/
public ServletContextHandler create(URI contextRoot, Path rootPath) {
final WebDavServletModule webDavServletModule = new WebDavServletModule(contextRoot, rootPath);
final WebDavServletComponent webDavServletComponent = component.newWebDavServletComponent(webDavServletModule);
final ServletContextHandler servletContext = webDavServletComponent.servletContext();
return servletContext;
public WebDavServletComponent create(URI contextRoot, Path rootPath) {
WebDavServletModule webDavServletModule = new WebDavServletModule(contextRoot, rootPath);
return component.newWebDavServletComponent(webDavServletModule);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package org.cryptomator.frontend.webdav.mount;

import java.net.URI;
import java.util.Map;

import javax.inject.Inject;
import javax.inject.Singleton;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

@Singleton
class FallbackMounter implements MounterStrategy {

private static final Logger LOG = LoggerFactory.getLogger(FallbackMounter.class);

@Inject
FallbackMounter() {
}

@Override
public Mount mount(URI uri, Map<MountParam, String> mountParams) throws CommandFailedException {
LOG.warn("Attempted to mount {}, but no applicable strategy has been found for your system. Try using the URI with a WebDAV client of your choice.", uri);
throw new CommandFailedException("No mounting strategy found.");
}

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

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package org.cryptomator.frontend.webdav.mount;

import java.io.IOException;
import java.net.URI;
import java.util.Map;

import javax.inject.Inject;
import javax.inject.Singleton;

import org.apache.commons.lang3.SystemUtils;

@Singleton
class LinuxGvfsMounter implements MounterStrategy {

@Inject
LinuxGvfsMounter() {
}

@Override
public boolean isApplicable() {
if (!SystemUtils.IS_OS_LINUX) {
return false;
}

// check if gvfs is installed:
assert SystemUtils.IS_OS_LINUX;
try {
ProcessBuilder checkDependencies = new ProcessBuilder("which", "gvfs-mount", "xdg-open");
Process checkDependenciesProcess = checkDependencies.start();
ProcessUtil.assertExitValue(checkDependenciesProcess, 0);
return true;
} catch (CommandFailedException | IOException e) {
return false;
}
}

@Override
public Mount mount(URI uri, Map<MountParam, String> mountParams) throws CommandFailedException {
// TODO Auto-generated method stub
throw new CommandFailedException("not yet implemented.");
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
package org.cryptomator.frontend.webdav.mount;

import java.io.IOException;
import java.net.URI;
import java.nio.charset.StandardCharsets;
import java.util.Map;
import java.util.concurrent.TimeUnit;

import javax.inject.Inject;
import javax.inject.Singleton;

import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.SystemUtils;

@Singleton
class MacAppleScriptMounter implements MounterStrategy {

@Inject
MacAppleScriptMounter() {
}

@Override
public boolean isApplicable() {
return SystemUtils.IS_OS_MAC_OSX && // since macOS 10.10+
!SystemUtils.IS_OS_MAC_OSX_MAVERICKS && // 10.9
!SystemUtils.IS_OS_MAC_OSX_MOUNTAIN_LION; // 10.8; older version not supported by Java 8
}

@Override
public Mount mount(URI uri, Map<MountParam, String> mountParams) throws CommandFailedException {
try {
String mountAppleScript = String.format("mount volume \"%s\"", uri.toString());
ProcessBuilder mount = new ProcessBuilder("/usr/bin/osascript", "-e", mountAppleScript);
Process mountProcess = mount.start();
String stdout = ProcessUtil.toString(mountProcess.getInputStream(), StandardCharsets.UTF_8);
ProcessUtil.waitFor(mountProcess, 1, TimeUnit.SECONDS);
ProcessUtil.assertExitValue(mountProcess, 0);
String volumeIdentifier = StringUtils.trim(StringUtils.removeStart(stdout, "file "));
String waitAppleScript1 = String.format("tell application \"Finder\" to repeat while not (\"%s\" exists)", volumeIdentifier);
String waitAppleScript2 = "delay 0.1";
String waitAppleScript3 = "end repeat";
ProcessBuilder wait = new ProcessBuilder("/usr/bin/osascript", "-e", waitAppleScript1, "-e", waitAppleScript2, "-e", waitAppleScript3);
Process waitProcess = wait.start();
ProcessUtil.waitFor(waitProcess, 5, TimeUnit.SECONDS);
ProcessUtil.assertExitValue(waitProcess, 0);
return new MountImpl(volumeIdentifier);
} catch (IOException e) {
throw new CommandFailedException(e);
}
}

private static class MountImpl implements Mount {

private final ProcessBuilder revealCommand;
private final ProcessBuilder unmountCommand;

private MountImpl(String volumeIdentifier) {
String openAppleScript = String.format("tell application \"Finder\" to open \"%s\"", volumeIdentifier);
String activateAppleScript = String.format("tell application \"Finder\" to activate \"%s\"", volumeIdentifier);
String ejectAppleScript = String.format("tell application \"Finder\" to if \"%s\" exists then eject \"%s\"", volumeIdentifier, volumeIdentifier);

this.revealCommand = new ProcessBuilder("/usr/bin/osascript", "-e", openAppleScript, "-e", activateAppleScript);
this.unmountCommand = new ProcessBuilder("/usr/bin/osascript", "-e", ejectAppleScript);
}

@Override
public void unmount() throws CommandFailedException {
try {
Process proc = unmountCommand.start();
ProcessUtil.waitFor(proc, 1, TimeUnit.SECONDS);
ProcessUtil.assertExitValue(proc, 0);
} catch (IOException e) {
throw new CommandFailedException(e);
}
}

@Override
public void reveal() throws CommandFailedException {
try {
Process proc = revealCommand.start();
ProcessUtil.waitFor(proc, 2, TimeUnit.SECONDS);
ProcessUtil.assertExitValue(proc, 0);
} catch (IOException e) {
throw new CommandFailedException(e);
}
}

}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package org.cryptomator.frontend.webdav.mount;

import java.net.URI;
import java.util.Map;

import javax.inject.Inject;
import javax.inject.Singleton;

import org.apache.commons.lang3.SystemUtils;

@Singleton
class MacShellScriptMounter implements MounterStrategy {

@Inject
MacShellScriptMounter() {
}

@Override
public boolean isApplicable() {
return SystemUtils.IS_OS_MAC_OSX_MAVERICKS || SystemUtils.IS_OS_MAC_OSX_MOUNTAIN_LION;
}

@Override
public Mount mount(URI uri, Map<MountParam, String> mountParams) throws CommandFailedException {
// TODO Auto-generated method stub
throw new CommandFailedException("not yet implemented.");
}

}
44 changes: 44 additions & 0 deletions src/main/java/org/cryptomator/frontend/webdav/mount/Mounter.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package org.cryptomator.frontend.webdav.mount;

import java.net.URI;
import java.util.Map;

public interface Mounter {

/**
* Tries to mount a given webdav share.
*
* @param uri URI of the webdav share
* @param mountParams additional mount parameters, that might be needed by certain OS-specific mount commands.
* @return a {@link Mount} representing the mounted share
* @throws CommandFailedException if the mount operation fails
* @throws IllegalArgumentException if mountParams is missing expected options
*/
Mount mount(URI uri, Map<MountParam, String> mountParams) throws CommandFailedException;

public enum MountParam {
MOUNT_NAME, WIN_DRIVE_LETTER, PREFERRED_GVFS_SCHEME;
}

/**
* Represents a single mounted volume and allows certain interactions with it.
*/
public interface Mount {
void unmount() throws CommandFailedException;

void reveal() throws CommandFailedException;
}

public class CommandFailedException extends Exception {

public CommandFailedException(String message) {
super(message);
}

public CommandFailedException(Throwable cause) {
super(cause);
}

}

}
Loading

0 comments on commit a1c8105

Please sign in to comment.