From cb50b2011f0f9e210c1f1d3adf5906d025839b16 Mon Sep 17 00:00:00 2001 From: Sebastian Stenzel Date: Tue, 6 Aug 2019 17:03:05 +0200 Subject: [PATCH] Lock remaining vaults on shutdown (references #838) --- .../cryptomator/ui/quit/QuitController.java | 106 ++++++++++++++---- main/ui/src/main/resources/fxml/quit.fxml | 3 +- 2 files changed, 87 insertions(+), 22 deletions(-) diff --git a/main/ui/src/main/java/org/cryptomator/ui/quit/QuitController.java b/main/ui/src/main/java/org/cryptomator/ui/quit/QuitController.java index 9a0f6c836..cae265e60 100644 --- a/main/ui/src/main/java/org/cryptomator/ui/quit/QuitController.java +++ b/main/ui/src/main/java/org/cryptomator/ui/quit/QuitController.java @@ -1,16 +1,24 @@ package org.cryptomator.ui.quit; -import javafx.beans.property.ObjectProperty; -import javafx.beans.property.SimpleObjectProperty; +import javafx.application.Platform; +import javafx.collections.ObservableList; +import javafx.concurrent.ScheduledService; +import javafx.concurrent.Task; import javafx.fxml.FXML; +import javafx.scene.control.Button; import javafx.scene.control.ContentDisplay; import javafx.stage.Stage; +import org.cryptomator.common.vaults.Vault; +import org.cryptomator.common.vaults.Volume; import org.cryptomator.ui.common.FxController; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javax.inject.Inject; import java.awt.desktop.QuitResponse; +import java.util.Iterator; +import java.util.List; +import java.util.concurrent.Executor; import java.util.concurrent.ExecutorService; @QuitScoped @@ -20,15 +28,16 @@ public class QuitController implements FxController { private final Stage window; private final QuitResponse response; + private final ObservableList unlockedVaults; private final ExecutorService executor; - private final ObjectProperty quitButtonState; + public Button lockAndQuitButton; @Inject - QuitController(@QuitWindow Stage window, QuitResponse response, ExecutorService executor) { + QuitController(@QuitWindow Stage window, QuitResponse response, ObservableList vaults, ExecutorService executor) { this.window = window; this.response = response; + this.unlockedVaults = vaults.filtered(Vault::isUnlocked); this.executor = executor; - this.quitButtonState = new SimpleObjectProperty<>(ContentDisplay.TEXT_ONLY); } @FXML @@ -39,26 +48,83 @@ public class QuitController implements FxController { } @FXML - public void quit() { - LOG.warn("Quit not yet implemented."); - window.close(); - response.cancelQuit(); + public void lockAndQuit() { + lockAndQuitButton.setDisable(true); + lockAndQuitButton.setContentDisplay(ContentDisplay.LEFT); + + Iterator toBeLocked = List.copyOf(unlockedVaults).iterator(); + ScheduledService lockAllService = new LockAllVaultsService(executor, toBeLocked); + lockAllService.setOnSucceeded(evt -> { + if (!toBeLocked.hasNext()) { + window.close(); + response.performQuit(); + } + }); + lockAllService.setOnFailed(evt -> { + lockAndQuitButton.setDisable(false); + lockAndQuitButton.setContentDisplay(ContentDisplay.TEXT_ONLY); + // TODO: show force lock or force quit scene (and DO NOT cancelQuit() here!) + response.cancelQuit(); + }); + lockAllService.start(); } - @FXML - public void forceQuit() { - LOG.warn("Force Quit not yet implemented."); - window.close(); - response.cancelQuit(); + /** + * @param vault The vault to lock + * @return Task that tries to lock the given vault gracefully. + */ + private Task createGracefulLockTask(Vault vault) { + Task task = new Task() { + @Override + protected Void call() throws Volume.VolumeException { + vault.lock(false); + LOG.info("Locked {}", vault.getDisplayableName()); + return null; + } + }; + task.setOnSucceeded(evt -> { + vault.setState(Vault.State.LOCKED); + }); + task.setOnFailed(evt -> { + LOG.warn("Failed to lock vault", vault); + }); + return task; } - /* Observable Properties */ - - public ObjectProperty quitButtonStateProperty() { - return quitButtonState; + /** + * @return Task that succeeds immediately + */ + private Task createNoopTask() { + return new Task<>() { + @Override + protected Void call() { + return null; + } + }; } + + private class LockAllVaultsService extends ScheduledService { - public ContentDisplay getQuitButtonState() { - return quitButtonState.get(); + private final Iterator vaultsToLock; + + public LockAllVaultsService(Executor executor, Iterator vaultsToLock) { + this.vaultsToLock = vaultsToLock; + setExecutor(executor); + setRestartOnFailure(false); + } + + @Override + protected Task createTask() { + assert Platform.isFxApplicationThread(); + if (vaultsToLock.hasNext()) { + return createGracefulLockTask(vaultsToLock.next()); + } else { + // This should be unreachable code, since vaultsToLock is only accessed on the FX App Thread. + // But if quitting the application takes longer for any reason, this service should shut down properly + reset(); + return createNoopTask(); + } + } } + } diff --git a/main/ui/src/main/resources/fxml/quit.fxml b/main/ui/src/main/resources/fxml/quit.fxml index 5e9f6017c..4806f82bc 100644 --- a/main/ui/src/main/resources/fxml/quit.fxml +++ b/main/ui/src/main/resources/fxml/quit.fxml @@ -23,8 +23,7 @@