Ver Fonte

Injecting Cryptor using Guice

Sebastian Stenzel há 10 anos atrás
pai
commit
8ba89a3bf5

+ 1 - 0
main/crypto-api/pom.xml

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

+ 7 - 0
main/pom.xml

@@ -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>

+ 2 - 16
main/ui/pom.xml

@@ -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>
 

+ 1 - 1
main/ui/src/main/java/org/cryptomator/ui/Main.java

@@ -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<>();

+ 7 - 11
main/ui/src/main/java/org/cryptomator/ui/MainApplication.java

@@ -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));
 	}

+ 12 - 6
main/ui/src/main/java/org/cryptomator/ui/MainController.java

@@ -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 ResourceBundle rb;
-
 	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;
 
 	@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();

+ 15 - 6
main/ui/src/main/java/org/cryptomator/ui/MainModule.java

@@ -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

+ 3 - 9
main/ui/src/main/java/org/cryptomator/ui/model/Vault.java

@@ -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());

+ 0 - 27
main/ui/src/main/java/org/cryptomator/ui/model/VaultDeserializer.java

@@ -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;
-	}
-
-}

+ 70 - 0
main/ui/src/main/java/org/cryptomator/ui/model/VaultObjectMapperProvider.java

@@ -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;
+		}
+
+	}
+
+}

+ 0 - 20
main/ui/src/main/java/org/cryptomator/ui/model/VaultSerializer.java

@@ -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();
-	}
-
-}

+ 4 - 60
main/ui/src/main/java/org/cryptomator/ui/settings/Settings.java

@@ -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
-	}
-
-	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);
-		}
-	}
+	/**
+	 * Package-private constructor; use {@link SettingsProvider}.
+	 */
+	Settings() {
 
-	private static Settings defaultSettings() {
-		return new Settings();
 	}
 
 	/* Getter/Setter */

+ 84 - 0
main/ui/src/main/java/org/cryptomator/ui/settings/SettingsProvider.java

@@ -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);
+		}
+	}
+
+}

+ 6 - 6
main/ui/src/main/java/org/cryptomator/ui/util/SingleInstanceManager.java

@@ -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();