Injecting Cryptor using Guice

This commit is contained in:
Sebastian Stenzel 2015-02-20 21:30:33 +01:00
parent b68cf71494
commit 8ba89a3bf5
14 changed files with 213 additions and 163 deletions

View File

@ -18,6 +18,7 @@
<name>Cryptomator cryptographic module API</name>
<dependencies>
<!-- commons -->
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>

View File

@ -103,6 +103,13 @@
<artifactId>commons-codec</artifactId>
<version>${commons-codec.version}</version>
</dependency>
<!-- DI -->
<dependency>
<groupId>com.google.inject</groupId>
<artifactId>guice</artifactId>
<version>3.0</version>
</dependency>
<!-- JSON -->
<dependency>

View File

@ -19,9 +19,8 @@
<properties>
<javafx.application.name>Cryptomator</javafx.application.name>
<exec.mainClass>org.cryptomator.ui.Main</exec.mainClass>
<exec.mainClass>org.cryptomator.ui.Cryptomator</exec.mainClass>
<javafx.tools.ant.jar>${java.home}/../lib/ant-javafx.jar</javafx.tools.ant.jar>
<controlsfx.version>8.20.8</controlsfx.version>
</properties>
<dependencies>
@ -50,23 +49,10 @@
<artifactId>commons-lang3</artifactId>
</dependency>
<!-- UI
<dependency>
<groupId>org.controlsfx</groupId>
<artifactId>controlsfx</artifactId>
<version>${controlsfx.version}</version>
</dependency>
<dependency>
<groupId>com.github.axet</groupId>
<artifactId>desktop</artifactId>
<version>2.2.3</version>
</dependency> -->
<!-- DI -->
<dependency>
<groupId>com.google.inject</groupId>
<artifactId>guice</artifactId>
<version>3.0</version>
</dependency>
</dependencies>

View File

@ -28,7 +28,7 @@ import org.eclipse.jetty.util.ConcurrentHashSet;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class Main {
public class Cryptomator {
public static final Logger LOG = LoggerFactory.getLogger(MainApplication.class);
public static final CompletableFuture<Consumer<File>> OPEN_FILE_HANDLER = new CompletableFuture<>();

View File

@ -18,13 +18,14 @@ import java.util.concurrent.ExecutorService;
import javafx.application.Application;
import javafx.application.Platform;
import javafx.fxml.FXMLLoader;
import javafx.fxml.JavaFXBuilderFactory;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;
import org.apache.commons.lang3.SystemUtils;
import org.cryptomator.ui.model.Vault;
import org.cryptomator.ui.MainModule.ControllerFactory;
import org.cryptomator.ui.model.Vault;
import org.cryptomator.ui.util.ActiveWindowStyleSupport;
import org.cryptomator.ui.util.DeferredCloser;
import org.cryptomator.ui.util.SingleInstanceManager;
@ -32,6 +33,7 @@ import org.cryptomator.ui.util.SingleInstanceManager.LocalInstance;
import org.cryptomator.ui.util.TrayIconUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.inject.Guice;
import com.google.inject.Injector;
@ -42,11 +44,8 @@ public class MainApplication extends Application {
private static final Logger LOG = LoggerFactory.getLogger(MainApplication.class);
private final CleanShutdownPerformer cleanShutdownPerformer = new CleanShutdownPerformer();
private final ExecutorService executorService;
private final ControllerFactory controllerFactory;
private final DeferredCloser closer;
public MainApplication() {
@ -62,9 +61,7 @@ public class MainApplication extends Application {
}
public MainApplication(Injector injector) {
this(injector.getInstance(ExecutorService.class),
injector.getInstance(ControllerFactory.class),
injector.getInstance(DeferredCloser.class));
this(injector.getInstance(ExecutorService.class), injector.getInstance(ControllerFactory.class), injector.getInstance(DeferredCloser.class));
}
public MainApplication(ExecutorService executorService, ControllerFactory controllerFactory, DeferredCloser closer) {
@ -91,7 +88,7 @@ public class MainApplication extends Application {
chooseNativeStylesheet();
final ResourceBundle rb = ResourceBundle.getBundle("localization");
final FXMLLoader loader = new FXMLLoader(getClass().getResource("/fxml/main.fxml"), rb);
final FXMLLoader loader = new FXMLLoader(getClass().getResource("/fxml/main.fxml"), rb, new JavaFXBuilderFactory(MainApplication.class.getClassLoader()));
loader.setControllerFactory(controllerFactory);
final Parent root = loader.load();
final MainController ctrl = loader.getController();
@ -112,11 +109,10 @@ public class MainApplication extends Application {
}
if (SystemUtils.IS_OS_MAC_OSX) {
Main.OPEN_FILE_HANDLER.complete(file -> handleCommandLineArg(ctrl, file.getAbsolutePath()));
Cryptomator.OPEN_FILE_HANDLER.complete(file -> handleCommandLineArg(ctrl, file.getAbsolutePath()));
}
LocalInstance cryptomatorGuiInstance = closer.closeLater(
SingleInstanceManager.startLocalInstance(APPLICATION_KEY, executorService), LocalInstance::close).get().get();
LocalInstance cryptomatorGuiInstance = closer.closeLater(SingleInstanceManager.startLocalInstance(APPLICATION_KEY, executorService), LocalInstance::close).get().get();
cryptomatorGuiInstance.registerListener(arg -> handleCommandLineArg(ctrl, arg));
}

View File

@ -26,6 +26,7 @@ import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.fxml.Initializable;
import javafx.fxml.JavaFXBuilderFactory;
import javafx.geometry.Side;
import javafx.scene.Parent;
import javafx.scene.control.ContextMenu;
@ -37,7 +38,9 @@ import javafx.scene.layout.Pane;
import javafx.stage.FileChooser;
import javafx.stage.Stage;
import javafx.stage.WindowEvent;
import javafx.util.BuilderFactory;
import org.cryptomator.crypto.Cryptor;
import org.cryptomator.ui.InitializeController.InitializationListener;
import org.cryptomator.ui.MainModule.ControllerFactory;
import org.cryptomator.ui.UnlockController.UnlockListener;
@ -49,6 +52,7 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.inject.Inject;
import com.google.inject.Provider;
public class MainController implements Initializable, InitializationListener, UnlockListener, LockListener {
@ -74,17 +78,19 @@ public class MainController implements Initializable, InitializationListener, Un
@FXML
private Pane contentPane;
private final ControllerFactory controllerFactory;
private final Settings settings;
private final Provider<Cryptor> cryptorProvider;
private final BuilderFactory builderFactory = new JavaFXBuilderFactory(MainController.class.getClassLoader());
private ResourceBundle rb;
private final ControllerFactory controllerFactory;
private final Settings settings;
@Inject
public MainController(ControllerFactory controllerFactory, Settings settings) {
public MainController(ControllerFactory controllerFactory, Settings settings, Provider<Cryptor> cryptorProvider) {
super();
this.controllerFactory = controllerFactory;
this.settings = settings;
this.cryptorProvider = cryptorProvider;
}
@Override
@ -164,7 +170,7 @@ public class MainController implements Initializable, InitializationListener, Un
return;
}
final Vault vault = new Vault(vaultPath);
final Vault vault = new Vault(vaultPath, cryptorProvider.get());
if (!directoryList.getItems().contains(vault)) {
directoryList.getItems().add(vault);
}
@ -222,7 +228,7 @@ public class MainController implements Initializable, InitializationListener, Un
private <T> T showView(String fxml) {
try {
final FXMLLoader loader = new FXMLLoader(getClass().getResource(fxml), rb);
final FXMLLoader loader = new FXMLLoader(getClass().getResource(fxml), rb, builderFactory);
loader.setControllerFactory(controllerFactory);
final Parent root = loader.load();
contentPane.getChildren().clear();

View File

@ -11,21 +11,29 @@ package org.cryptomator.ui;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import javafx.util.Callback;
import javax.inject.Singleton;
import org.cryptomator.crypto.Cryptor;
import org.cryptomator.crypto.SamplingDecorator;
import org.cryptomator.crypto.aes256.Aes256Cryptor;
import org.cryptomator.ui.model.VaultObjectMapperProvider;
import org.cryptomator.ui.settings.Settings;
import org.cryptomator.ui.settings.SettingsProvider;
import org.cryptomator.ui.util.DeferredCloser;
import org.cryptomator.ui.util.DeferredCloser.Closer;
import org.cryptomator.webdav.WebDavServer;
import javafx.util.Callback;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.inject.AbstractModule;
import com.google.inject.Injector;
import com.google.inject.Provides;
import com.google.inject.name.Names;
public class MainModule extends AbstractModule {
DeferredCloser deferredCloser = new DeferredCloser();
private final DeferredCloser deferredCloser = new DeferredCloser();
public static interface ControllerFactory extends Callback<Class<?>, Object> {
@ -34,6 +42,8 @@ public class MainModule extends AbstractModule {
@Override
protected void configure() {
bind(DeferredCloser.class).toInstance(deferredCloser);
bind(ObjectMapper.class).annotatedWith(Names.named("VaultJsonMapper")).toProvider(VaultObjectMapperProvider.class);
bind(Settings.class).toProvider(SettingsProvider.class);
}
@Provides
@ -49,9 +59,8 @@ public class MainModule extends AbstractModule {
}
@Provides
@Singleton
Settings getSettings() {
return closeLater(Settings.load(), Settings::save);
Cryptor getCryptor() {
return SamplingDecorator.decorate(new Aes256Cryptor());
}
@Provides

View File

@ -13,8 +13,6 @@ import javafx.beans.property.SimpleObjectProperty;
import org.apache.commons.lang3.StringUtils;
import org.cryptomator.crypto.Cryptor;
import org.cryptomator.crypto.SamplingDecorator;
import org.cryptomator.crypto.aes256.Aes256Cryptor;
import org.cryptomator.ui.util.DeferredClosable;
import org.cryptomator.ui.util.DeferredCloser;
import org.cryptomator.ui.util.MasterKeyFilter;
@ -26,11 +24,6 @@ import org.cryptomator.webdav.WebDavServer.ServletLifeCycleAdapter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
@JsonSerialize(using = VaultSerializer.class)
@JsonDeserialize(using = VaultDeserializer.class)
public class Vault implements Serializable {
private static final long serialVersionUID = 3754487289683599469L;
@ -38,18 +31,19 @@ public class Vault implements Serializable {
public static final String VAULT_FILE_EXTENSION = ".cryptomator";
private final Cryptor cryptor = SamplingDecorator.decorate(new Aes256Cryptor());
private final Cryptor cryptor;
private final ObjectProperty<Boolean> unlocked = new SimpleObjectProperty<Boolean>(this, "unlocked", Boolean.FALSE);
private final Path path;
private String mountName;
private DeferredClosable<ServletLifeCycleAdapter> webDavServlet = DeferredClosable.empty();
private DeferredClosable<WebDavMount> webDavMount = DeferredClosable.empty();
public Vault(final Path vaultDirectoryPath) {
public Vault(final Path vaultDirectoryPath, final Cryptor cryptor) {
if (!Files.isDirectory(vaultDirectoryPath) || !vaultDirectoryPath.getFileName().toString().endsWith(VAULT_FILE_EXTENSION)) {
throw new IllegalArgumentException("Not a valid vault directory: " + vaultDirectoryPath);
}
this.path = vaultDirectoryPath;
this.cryptor = cryptor;
try {
setMountName(getName());

View File

@ -1,27 +0,0 @@
package org.cryptomator.ui.model;
import java.io.IOException;
import java.nio.file.FileSystems;
import java.nio.file.Path;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonDeserializer;
import com.fasterxml.jackson.databind.JsonNode;
public class VaultDeserializer extends JsonDeserializer<Vault> {
@Override
public Vault deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException, JsonProcessingException {
final JsonNode node = jp.readValueAsTree();
final String pathStr = node.get("path").asText();
final Path path = FileSystems.getDefault().getPath(pathStr);
final Vault dir = new Vault(path);
if (node.has("mountName")) {
dir.setMountName(node.get("mountName").asText());
}
return dir;
}
}

View File

@ -0,0 +1,70 @@
package org.cryptomator.ui.model;
import java.io.IOException;
import java.nio.file.FileSystems;
import java.nio.file.Path;
import javax.inject.Inject;
import org.cryptomator.crypto.Cryptor;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonDeserializer;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.google.inject.Provider;
public class VaultObjectMapperProvider implements Provider<ObjectMapper> {
private final Provider<Cryptor> cryptorProvider;
@Inject
public VaultObjectMapperProvider(final Provider<Cryptor> cryptorProvider) {
this.cryptorProvider = cryptorProvider;
}
@Override
public ObjectMapper get() {
final ObjectMapper om = new ObjectMapper();
final SimpleModule module = new SimpleModule("VaultJsonMapper");
module.addSerializer(Vault.class, new VaultSerializer());
module.addDeserializer(Vault.class, new VaultDeserializer());
om.registerModule(module);
return om;
}
private class VaultSerializer extends JsonSerializer<Vault> {
@Override
public void serialize(Vault value, JsonGenerator jgen, SerializerProvider provider) throws IOException, JsonProcessingException {
jgen.writeStartObject();
jgen.writeStringField("path", value.getPath().toString());
jgen.writeStringField("mountName", value.getMountName().toString());
jgen.writeEndObject();
}
}
private class VaultDeserializer extends JsonDeserializer<Vault> {
@Override
public Vault deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException, JsonProcessingException {
final JsonNode node = jp.readValueAsTree();
final String pathStr = node.get("path").asText();
final Path path = FileSystems.getDefault().getPath(pathStr);
final Vault dir = new Vault(path, cryptorProvider.get());
if (node.has("mountName")) {
dir.setMountName(node.get("mountName").asText());
}
return dir;
}
}
}

View File

@ -1,20 +0,0 @@
package org.cryptomator.ui.model;
import java.io.IOException;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider;
public class VaultSerializer extends JsonSerializer<Vault> {
@Override
public void serialize(Vault value, JsonGenerator jgen, SerializerProvider provider) throws IOException, JsonProcessingException {
jgen.writeStartObject();
jgen.writeStringField("path", value.getPath().toString());
jgen.writeStringField("mountName", value.getMountName().toString());
jgen.writeEndObject();
}
}

View File

@ -8,82 +8,26 @@
******************************************************************************/
package org.cryptomator.ui.settings;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.Serializable;
import java.nio.file.FileSystem;
import java.nio.file.FileSystems;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardOpenOption;
import java.util.ArrayList;
import java.util.List;
import org.apache.commons.lang3.SystemUtils;
import org.cryptomator.ui.model.Vault;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.fasterxml.jackson.annotation.JsonPropertyOrder;
import com.fasterxml.jackson.databind.ObjectMapper;
@JsonPropertyOrder(value = {"directories"})
public class Settings implements Serializable {
private static final long serialVersionUID = 7609959894417878744L;
private static final Logger LOG = LoggerFactory.getLogger(Settings.class);
private static final Path SETTINGS_DIR;
private static final String SETTINGS_FILE = "settings.json";
private static final ObjectMapper JSON_OM = new ObjectMapper();
static {
final String appdata = System.getenv("APPDATA");
final FileSystem fs = FileSystems.getDefault();
if (SystemUtils.IS_OS_WINDOWS && appdata != null) {
SETTINGS_DIR = fs.getPath(appdata, "Cryptomator");
} else if (SystemUtils.IS_OS_WINDOWS && appdata == null) {
SETTINGS_DIR = fs.getPath(SystemUtils.USER_HOME, ".Cryptomator");
} else if (SystemUtils.IS_OS_MAC_OSX) {
SETTINGS_DIR = fs.getPath(SystemUtils.USER_HOME, "Library/Application Support/Cryptomator");
} else {
// (os.contains("solaris") || os.contains("sunos") || os.contains("linux") || os.contains("unix"))
SETTINGS_DIR = fs.getPath(SystemUtils.USER_HOME, ".Cryptomator");
}
}
private List<Vault> directories;
private Settings() {
// private constructor
}
/**
* Package-private constructor; use {@link SettingsProvider}.
*/
Settings() {
public static synchronized Settings load() {
try {
Files.createDirectories(SETTINGS_DIR);
final Path settingsFile = SETTINGS_DIR.resolve(SETTINGS_FILE);
final InputStream in = Files.newInputStream(settingsFile, StandardOpenOption.READ);
return JSON_OM.readValue(in, Settings.class);
} catch (IOException e) {
LOG.warn("Failed to load settings, creating new one.");
return Settings.defaultSettings();
}
}
public synchronized void save() {
try {
Files.createDirectories(SETTINGS_DIR);
final Path settingsFile = SETTINGS_DIR.resolve(SETTINGS_FILE);
final OutputStream out = Files.newOutputStream(settingsFile, StandardOpenOption.WRITE, StandardOpenOption.TRUNCATE_EXISTING, StandardOpenOption.CREATE);
JSON_OM.writeValue(out, this);
} catch (IOException e) {
LOG.error("Failed to save settings.", e);
}
}
private static Settings defaultSettings() {
return new Settings();
}
/* Getter/Setter */

View File

@ -0,0 +1,84 @@
package org.cryptomator.ui.settings;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.file.FileSystem;
import java.nio.file.FileSystems;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardOpenOption;
import javax.inject.Inject;
import javax.inject.Named;
import org.apache.commons.lang3.SystemUtils;
import org.cryptomator.ui.util.DeferredCloser;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.inject.Provider;
public class SettingsProvider implements Provider<Settings> {
private static final Logger LOG = LoggerFactory.getLogger(Settings.class);
private static final Path SETTINGS_DIR;
private static final String SETTINGS_FILE = "settings.json";
static {
final String appdata = System.getenv("APPDATA");
final FileSystem fs = FileSystems.getDefault();
if (SystemUtils.IS_OS_WINDOWS && appdata != null) {
SETTINGS_DIR = fs.getPath(appdata, "Cryptomator");
} else if (SystemUtils.IS_OS_WINDOWS && appdata == null) {
SETTINGS_DIR = fs.getPath(SystemUtils.USER_HOME, ".Cryptomator");
} else if (SystemUtils.IS_OS_MAC_OSX) {
SETTINGS_DIR = fs.getPath(SystemUtils.USER_HOME, "Library/Application Support/Cryptomator");
} else {
// (os.contains("solaris") || os.contains("sunos") || os.contains("linux") || os.contains("unix"))
SETTINGS_DIR = fs.getPath(SystemUtils.USER_HOME, ".Cryptomator");
}
}
private final DeferredCloser deferredCloser;
private final ObjectMapper objectMapper;
@Inject
public SettingsProvider(DeferredCloser deferredCloser, @Named("VaultJsonMapper") ObjectMapper objectMapper) {
this.deferredCloser = deferredCloser;
this.objectMapper = objectMapper;
}
@Override
public Settings get() {
Settings settings = null;
try {
Files.createDirectories(SETTINGS_DIR);
final Path settingsFile = SETTINGS_DIR.resolve(SETTINGS_FILE);
final InputStream in = Files.newInputStream(settingsFile, StandardOpenOption.READ);
settings = objectMapper.readValue(in, Settings.class);
} catch (IOException e) {
LOG.warn("Failed to load settings, creating new one.");
settings = new Settings();
}
deferredCloser.closeLater(settings, this::save);
return settings;
}
private void save(Settings settings) {
if (settings == null) {
return;
}
try {
Files.createDirectories(SETTINGS_DIR);
final Path settingsFile = SETTINGS_DIR.resolve(SETTINGS_FILE);
final OutputStream out = Files.newOutputStream(settingsFile, StandardOpenOption.WRITE, StandardOpenOption.TRUNCATE_EXISTING, StandardOpenOption.CREATE);
objectMapper.writeValue(out, settings);
} catch (IOException e) {
LOG.error("Failed to save settings.", e);
}
}
}

View File

@ -30,7 +30,7 @@ import java.util.concurrent.ExecutorService;
import java.util.prefs.Preferences;
import org.apache.commons.io.IOUtils;
import org.cryptomator.ui.Main;
import org.cryptomator.ui.Cryptomator;
import org.cryptomator.ui.util.ListenerRegistry.ListenerRegistration;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -193,7 +193,7 @@ public class SingleInstanceManager {
IOUtils.closeQuietly(selector);
IOUtils.closeQuietly(channel);
if (getSavedPort(applicationKey).orElse(-1).equals(port)) {
Preferences.userNodeForPackage(Main.class).remove(applicationKey);
Preferences.userNodeForPackage(Cryptomator.class).remove(applicationKey);
}
}
@ -226,7 +226,7 @@ public class SingleInstanceManager {
/**
* Checks if there is a valid port at
* {@link Preferences#userNodeForPackage(Class)} for {@link Main} under the
* {@link Preferences#userNodeForPackage(Class)} for {@link Cryptomator} under the
* given applicationKey, tries to connect to the port at the loopback
* address and checks if the port identifies with the applicationKey.
*
@ -284,7 +284,7 @@ public class SingleInstanceManager {
}
static Optional<Integer> getSavedPort(String applicationKey) {
int port = Preferences.userNodeForPackage(Main.class).getInt(applicationKey, -1);
int port = Preferences.userNodeForPackage(Cryptomator.class).getInt(applicationKey, -1);
if (port == -1) {
LOG.info("no running instance found");
@ -296,7 +296,7 @@ public class SingleInstanceManager {
/**
* Creates a server socket on a free port and saves the port in
* {@link Preferences#userNodeForPackage(Class)} for {@link Main} under the
* {@link Preferences#userNodeForPackage(Class)} for {@link Cryptomator} under the
* given applicationKey.
*
* @param applicationKey
@ -312,7 +312,7 @@ public class SingleInstanceManager {
channel.bind(new InetSocketAddress(InetAddress.getLoopbackAddress(), 0));
final int port = ((InetSocketAddress) channel.getLocalAddress()).getPort();
Preferences.userNodeForPackage(Main.class).putInt(applicationKey, port);
Preferences.userNodeForPackage(Cryptomator.class).putInt(applicationKey, port);
LOG.info("InstanceManager bound to port {}", port);
Selector selector = Selector.open();