Introduced new scope @FxApplicationScoped below @Singleton.

This allows us to initialize Dagger before we start the JavaFX application, which will in turn allow us to access Environment from within the IPC classes.

Affects #663
This commit is contained in:
Sebastian Stenzel
2019-02-19 00:11:58 +01:00
parent d7dda7d249
commit 79306ea498
34 changed files with 372 additions and 405 deletions
@@ -1,20 +0,0 @@
/*******************************************************************************
* Copyright (c) 2017 Skymatic UG (haftungsbeschränkt).
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the accompanying LICENSE file.
*******************************************************************************/
package org.cryptomator.launcher;
import java.util.Optional;
public class ApplicationVersion {
public static String orElse(String other) {
return get().orElse(other);
}
public static Optional<String> get() {
return Optional.ofNullable(Cryptomator.class.getPackage().getImplementationVersion());
}
}
@@ -5,42 +5,83 @@
*******************************************************************************/
package org.cryptomator.launcher;
import java.io.IOException;
import java.nio.file.Path;
import java.util.Arrays;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import javafx.application.Application;
import javafx.stage.Stage;
import org.apache.commons.lang3.SystemUtils;
import org.cryptomator.ui.controllers.MainController;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javafx.application.Application;
import java.io.IOException;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Arrays;
import java.util.Optional;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
public class Cryptomator {
private static final Logger LOG = LoggerFactory.getLogger(Cryptomator.class);
static final BlockingQueue<Path> FILE_OPEN_REQUESTS = new ArrayBlockingQueue<>(10);
private static final CryptomatorComponent CRYPTOMATOR_COMPONENT = DaggerCryptomatorComponent.create();
private static final Path DEFAULT_IPC_PATH = Paths.get(".ipcPort.tmp");
// We need a separate FX Application class.
// If org.cryptomator.launcher.Cryptomator simply extended Application, the module system magically kicks in and throws exceptions
public static class MainApp extends Application {
private Stage primaryStage;
@Override
public void start(Stage primaryStage) {
LOG.info("JavaFX application started.");
this.primaryStage = primaryStage;
primaryStage.setMinWidth(652.0);
primaryStage.setMinHeight(440.0);
FxApplicationComponent fxApplicationComponent = CRYPTOMATOR_COMPONENT.fxApplicationComponent() //
.fxApplication(this) //
.mainWindow(primaryStage) //
.build();
fxApplicationComponent.debugMode().initialize();
MainController mainCtrl = fxApplicationComponent.fxmlLoader().load("/fxml/main.fxml");
mainCtrl.initStage(primaryStage);
primaryStage.show();
}
@Override
public void stop() {
assert primaryStage != null;
primaryStage.hide();
LOG.info("JavaFX application stopped.");
}
}
public static void main(String[] args) {
LOG.info("Starting Cryptomator {} on {} {} ({})", ApplicationVersion.orElse("SNAPSHOT"), SystemUtils.OS_NAME, SystemUtils.OS_VERSION, SystemUtils.OS_ARCH);
LOG.info("Starting Cryptomator {} on {} {} ({})", CRYPTOMATOR_COMPONENT.applicationVersion().orElse("SNAPSHOT"), SystemUtils.OS_NAME, SystemUtils.OS_VERSION, SystemUtils.OS_ARCH);
FileOpenRequestHandler fileOpenRequestHandler = new FileOpenRequestHandler(FILE_OPEN_REQUESTS);
try (InterProcessCommunicator communicator = InterProcessCommunicator.start(new IpcProtocolImpl(fileOpenRequestHandler))) {
FileOpenRequestHandler fileOpenRequestHandler = CRYPTOMATOR_COMPONENT.fileOpenRequestHanlder();
Path ipcPortPath = CRYPTOMATOR_COMPONENT.environment().getIpcPortPath().findFirst().orElse(DEFAULT_IPC_PATH);
try (InterProcessCommunicator communicator = InterProcessCommunicator.start(ipcPortPath, new IpcProtocolImpl(fileOpenRequestHandler))) {
if (communicator.isServer()) {
fileOpenRequestHandler.handleLaunchArgs(args);
CleanShutdownPerformer.registerShutdownHook();
Application.launch(MainApplication.class, args);
Application.launch(MainApp.class, args);
} else {
communicator.handleLaunchArgs(args);
LOG.info("Found running application instance. Shutting down.");
}
System.exit(0); // end remaining non-daemon threads.
} catch (IOException e) {
LOG.error("Failed to initiate inter-process communication.", e);
System.exit(2);
} catch (Throwable e) {
LOG.error("Error during startup", e);
System.exit(1);
}
System.exit(0); // end remaining non-daemon threads.
}
private static class IpcProtocolImpl implements InterProcessCommunicationProtocol {
@@ -0,0 +1,26 @@
package org.cryptomator.launcher;
import dagger.Component;
import org.cryptomator.common.CommonsModule;
import org.cryptomator.common.Environment;
import javax.inject.Named;
import javax.inject.Singleton;
import java.nio.file.Path;
import java.util.Optional;
import java.util.concurrent.BlockingQueue;
@Singleton
@Component(modules = {CryptomatorModule.class, CommonsModule.class})
public interface CryptomatorComponent {
Environment environment();
FileOpenRequestHandler fileOpenRequestHanlder();
@Named("applicationVersion")
Optional<String> applicationVersion();
FxApplicationComponent.Builder fxApplicationComponent();
}
@@ -0,0 +1,30 @@
package org.cryptomator.launcher;
import dagger.Module;
import dagger.Provides;
import javax.inject.Named;
import javax.inject.Singleton;
import java.nio.file.Path;
import java.util.Optional;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
@Module
class CryptomatorModule {
@Provides
@Singleton
@Named("fileOpenRequests")
BlockingQueue<Path> provideFileOpenRequests() {
return new ArrayBlockingQueue<>(10);
}
@Provides
@Singleton
@Named("applicationVersion")
Optional<String> provideApplicationVersion() {
return Optional.ofNullable(Cryptomator.class.getPackage().getImplementationVersion());
}
}
@@ -7,6 +7,7 @@
package org.cryptomator.launcher;
import java.awt.Desktop;
import java.awt.desktop.OpenFilesEvent;
import java.io.File;
import java.nio.file.FileSystem;
import java.nio.file.FileSystems;
@@ -17,22 +18,30 @@ import java.util.concurrent.BlockingQueue;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.inject.Inject;
import javax.inject.Named;
import javax.inject.Singleton;
@Singleton
class FileOpenRequestHandler {
private static final Logger LOG = LoggerFactory.getLogger(FileOpenRequestHandler.class);
private final BlockingQueue<Path> fileOpenRequests;
public FileOpenRequestHandler(BlockingQueue<Path> fileOpenRequests) {
@Inject
public FileOpenRequestHandler(@Named("fileOpenRequests") BlockingQueue<Path> fileOpenRequests) {
this.fileOpenRequests = fileOpenRequests;
try {
Desktop.getDesktop().setOpenFileHandler(e -> {
e.getFiles().stream().map(File::toPath).forEach(fileOpenRequests::add);
});
Desktop.getDesktop().setOpenFileHandler(this::openFiles);
} catch (UnsupportedOperationException e) {
LOG.info("Unable to setOpenFileHandler, probably not supported on this OS.");
}
}
private void openFiles(final OpenFilesEvent evt) {
evt.getFiles().stream().map(File::toPath).forEach(fileOpenRequests::add);
}
public void handleLaunchArgs(String[] args) {
handleLaunchArgs(FileSystems.getDefault(), args);
}
@@ -5,19 +5,35 @@
*******************************************************************************/
package org.cryptomator.launcher;
import javax.inject.Singleton;
import dagger.BindsInstance;
import dagger.Subcomponent;
import javafx.application.Application;
import javafx.stage.Stage;
import org.cryptomator.common.FxApplicationScoped;
import org.cryptomator.logging.DebugMode;
import org.cryptomator.ui.controllers.ViewControllerLoader;
import dagger.Component;
import javax.inject.Named;
@Singleton
@Component(modules = LauncherModule.class)
interface LauncherComponent {
@FxApplicationScoped
@Subcomponent(modules = FxApplicationModule.class)
interface FxApplicationComponent {
ViewControllerLoader fxmlLoader();
DebugMode debugMode();
@Subcomponent.Builder
interface Builder {
@BindsInstance
Builder fxApplication(Application application);
@BindsInstance
Builder mainWindow(@Named("mainWindow") Stage mainWindow);
FxApplicationComponent build();
}
}
@@ -0,0 +1,26 @@
/*******************************************************************************
* Copyright (c) 2017 Skymatic UG (haftungsbeschränkt).
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the accompanying LICENSE file.
*******************************************************************************/
package org.cryptomator.launcher;
import dagger.Module;
import dagger.Provides;
import org.cryptomator.common.FxApplicationScoped;
import org.cryptomator.ui.UiModule;
import javax.inject.Named;
import java.util.function.Consumer;
@Module(includes = {UiModule.class})
class FxApplicationModule {
@Provides
@FxApplicationScoped
@Named("shutdownTaskScheduler")
Consumer<Runnable> provideShutdownTaskScheduler() {
return CleanShutdownPerformer::scheduleShutdownTask;
}
}
@@ -48,16 +48,12 @@ abstract class InterProcessCommunicator implements InterProcessCommunicationProt
public abstract boolean isServer();
/**
* @param portFilePath Path to a file containing the IPC port
* @param endpoint The server-side communication endpoint.
* @return Either a client or a server communicator.
* @throws IOException In case of communication errors.
*/
public static InterProcessCommunicator start(InterProcessCommunicationProtocol endpoint) throws IOException {
return start(getIpcPortPath(), endpoint);
}
// visible for testing
static InterProcessCommunicator start(Path portFilePath, InterProcessCommunicationProtocol endpoint) throws IOException {
public static InterProcessCommunicator start(Path portFilePath, InterProcessCommunicationProtocol endpoint) throws IOException {
System.setProperty("java.rmi.server.hostname", "localhost");
try {
// try to connect to existing server:
@@ -76,24 +72,6 @@ abstract class InterProcessCommunicator implements InterProcessCommunicationProt
return server;
}
private static Path getIpcPortPath() {
final String settingsPathProperty = System.getProperty("cryptomator.ipcPortPath");
if (settingsPathProperty == null) {
LOG.warn("System property cryptomator.ipcPortPath not set.");
return Paths.get(".ipcPort.tmp");
} else {
return Paths.get(replaceHomeDir(settingsPathProperty));
}
}
private static String replaceHomeDir(String path) {
if (path.startsWith("~/")) {
return SystemUtils.USER_HOME + path.substring(1);
} else {
return path;
}
}
public static class ClientCommunicator extends InterProcessCommunicator {
private final IpcProtocolRemote remote;
@@ -1,68 +0,0 @@
/*******************************************************************************
* Copyright (c) 2017 Skymatic UG (haftungsbeschränkt).
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the accompanying LICENSE file.
*******************************************************************************/
package org.cryptomator.launcher;
import java.nio.file.Path;
import java.util.Optional;
import java.util.concurrent.BlockingQueue;
import java.util.function.Consumer;
import javax.inject.Named;
import javax.inject.Singleton;
import org.cryptomator.ui.UiModule;
import dagger.Module;
import dagger.Provides;
import javafx.application.Application;
import javafx.stage.Stage;
@Module(includes = {UiModule.class})
class LauncherModule {
private final Application application;
private final Stage mainWindow;
public LauncherModule(Application application, Stage mainWindow) {
this.application = application;
this.mainWindow = mainWindow;
}
@Provides
@Singleton
Application provideApplication() {
return application;
}
@Provides
@Singleton
@Named("applicationVersion")
Optional<String> provideApplicationVersion() {
return ApplicationVersion.get();
}
@Provides
@Singleton
@Named("mainWindow")
Stage provideMainWindow() {
return mainWindow;
}
@Provides
@Singleton
@Named("fileOpenRequests")
BlockingQueue<Path> provideFileOpenRequests() {
return Cryptomator.FILE_OPEN_REQUESTS;
}
@Provides
@Singleton
@Named("shutdownTaskScheduler")
Consumer<Runnable> provideShutdownTaskScheduler() {
return CleanShutdownPerformer::scheduleShutdownTask;
}
}
@@ -1,45 +0,0 @@
/*******************************************************************************
* Copyright (c) 2017 Skymatic UG (haftungsbeschränkt).
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the accompanying LICENSE file.
*******************************************************************************/
package org.cryptomator.launcher;
import javafx.application.Application;
import javafx.stage.Stage;
import org.cryptomator.ui.controllers.MainController;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class MainApplication extends Application {
private static final Logger LOG = LoggerFactory.getLogger(MainApplication.class);
private Stage primaryStage;
@Override
public void start(Stage primaryStage) {
LOG.info("JavaFX application started.");
this.primaryStage = primaryStage;
primaryStage.setMinWidth(652.0);
primaryStage.setMinHeight(440.0);
LauncherModule launcherModule = new LauncherModule(this, primaryStage);
LauncherComponent launcherComponent = DaggerLauncherComponent.builder() //
.launcherModule(launcherModule) //
.build();
launcherComponent.debugMode().initialize();
MainController mainCtrl = launcherComponent.fxmlLoader().load("/fxml/main.fxml");
mainCtrl.initStage(primaryStage);
primaryStage.show();
}
@Override
public void stop() {
assert primaryStage != null;
primaryStage.hide();
LOG.info("JavaFX application stopped.");
}
}
@@ -5,22 +5,20 @@
*******************************************************************************/
package org.cryptomator.logging;
import static java.util.Arrays.asList;
import java.util.Collection;
import javax.inject.Inject;
import javax.inject.Singleton;
import ch.qos.logback.classic.Level;
import ch.qos.logback.classic.Logger;
import ch.qos.logback.classic.LoggerContext;
import org.cryptomator.common.FxApplicationScoped;
import org.cryptomator.common.settings.Settings;
import org.slf4j.ILoggerFactory;
import org.slf4j.LoggerFactory;
import ch.qos.logback.classic.Level;
import ch.qos.logback.classic.Logger;
import ch.qos.logback.classic.LoggerContext;
import javax.inject.Inject;
import java.util.Collection;
@Singleton
import static java.util.Arrays.asList;
@FxApplicationScoped
public class DebugMode {
private static final org.slf4j.Logger LOG = LoggerFactory.getLogger(DebugMode.class);