From f95b2baad5407bf7ff46585a0cda9954bc86ddf7 Mon Sep 17 00:00:00 2001 From: Sebastian Stenzel Date: Wed, 11 Dec 2019 15:57:14 +0100 Subject: [PATCH] "lock" deduplication --- .../cryptomator/ui/common/VaultService.java | 118 ++++++++++++++++++ .../VaultDetailUnlockedController.java | 21 +--- .../cryptomator/ui/quit/QuitController.java | 81 ++---------- 3 files changed, 133 insertions(+), 87 deletions(-) create mode 100644 main/ui/src/main/java/org/cryptomator/ui/common/VaultService.java diff --git a/main/ui/src/main/java/org/cryptomator/ui/common/VaultService.java b/main/ui/src/main/java/org/cryptomator/ui/common/VaultService.java new file mode 100644 index 000000000..13059236e --- /dev/null +++ b/main/ui/src/main/java/org/cryptomator/ui/common/VaultService.java @@ -0,0 +1,118 @@ +package org.cryptomator.ui.common; + +import javafx.application.Platform; +import javafx.concurrent.ScheduledService; +import javafx.concurrent.Service; +import javafx.concurrent.Task; +import org.cryptomator.common.vaults.Vault; +import org.cryptomator.common.vaults.VaultState; +import org.cryptomator.common.vaults.Volume; +import org.cryptomator.ui.fxapp.FxApplicationScoped; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.inject.Inject; +import java.util.Collection; +import java.util.Iterator; +import java.util.concurrent.ExecutorService; + +@FxApplicationScoped +public class VaultService { + + private static final Logger LOG = LoggerFactory.getLogger(VaultService.class); + + private final ExecutorService executorService; + + @Inject + public VaultService(ExecutorService executorService) { + this.executorService = executorService; + } + + /** + * Locks a vault in a background thread. + * + * @param vault The vault to lock + * @param forced Whether to attempt a forced lock + */ + public void lock(Vault vault, boolean forced) { + Task task = createLockTask(vault, forced); + task.setOnSucceeded(evt -> LOG.info("Locked {}", vault.getDisplayableName())); + task.setOnFailed(evt -> LOG.error("Failed to lock vault " + vault.getDisplayableName(), evt.getSource().getException())); + executorService.execute(task); + } + + public void lockAll(Collection vaults, boolean forced) { + Service service = createLockAllService(vaults, forced); + service.setOnFailed(evt -> LOG.error("Failed to lock vault", evt.getSource().getException())); + service.setExecutor(executorService); + service.start(); + } + + /** + * Creates but doesn't start a lock-all service that can be run on a background thread. + * + * @param vaults The list of vaults to be locked. Must not be concurrently modified + * @param forced Whether to attempt a forced lock + * @return Service that tries to lock all given vaults + */ + public Service createLockAllService(Collection vaults, boolean forced) { + Iterator iter = vaults.iterator(); + return new ScheduledService<>() { + + @Override + protected Task createTask() { + assert Platform.isFxApplicationThread(); + if (iter.hasNext()) { + return createLockTask(iter.next(), forced); + } else { + // This should be unreachable code, since iter 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(); + } + } + }; + } + + /** + * Creates but doesn't start a lock task that can be run on a background thread. + * + * @param vault The vault to lock + * @param forced Whether to attempt a forced lock + * @return Task that tries to lock the given vault + */ + public Task createLockTask(Vault vault, boolean forced) { + return new Task<>() { + @Override + protected Void call() throws Volume.VolumeException { + vault.lock(forced); + return null; + } + + @Override + protected void scheduled() { + vault.setState(VaultState.PROCESSING); + } + + @Override + protected void succeeded() { + vault.setState(VaultState.LOCKED); + } + + @Override + protected void failed() { + vault.setState(VaultState.UNLOCKED); + } + }; + } + + private Task createNoopTask() { + return new Task<>() { + @Override + protected Void call() { + return null; + } + }; + } + +} diff --git a/main/ui/src/main/java/org/cryptomator/ui/mainwindow/VaultDetailUnlockedController.java b/main/ui/src/main/java/org/cryptomator/ui/mainwindow/VaultDetailUnlockedController.java index f2d801679..f180e4a83 100644 --- a/main/ui/src/main/java/org/cryptomator/ui/mainwindow/VaultDetailUnlockedController.java +++ b/main/ui/src/main/java/org/cryptomator/ui/mainwindow/VaultDetailUnlockedController.java @@ -8,6 +8,7 @@ import org.cryptomator.common.vaults.VaultState; import org.cryptomator.common.vaults.Volume; import org.cryptomator.ui.common.FxController; import org.cryptomator.ui.common.Tasks; +import org.cryptomator.ui.common.VaultService; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -20,12 +21,12 @@ public class VaultDetailUnlockedController implements FxController { private static final Logger LOG = LoggerFactory.getLogger(VaultDetailUnlockedController.class); private final ReadOnlyObjectProperty vault; - private final ExecutorService executor; + private final VaultService vaultService; @Inject - public VaultDetailUnlockedController(ObjectProperty vault, ExecutorService executor) { + public VaultDetailUnlockedController(ObjectProperty vault, VaultService vaultService) { this.vault = vault; - this.executor = executor; + this.vaultService = vaultService; } @FXML @@ -39,18 +40,8 @@ public class VaultDetailUnlockedController implements FxController { @FXML public void lock() { - Vault v = vault.get(); - v.setState(VaultState.PROCESSING); - Tasks.create(() -> { - v.lock(false); - }).onSuccess(() -> { - LOG.trace("Regular unmount succeeded."); - v.setState(VaultState.LOCKED); - }).onError(Exception.class, e -> { - v.setState(VaultState.UNLOCKED); - LOG.error("Regular unmount failed.", e); - // TODO - }).runOnce(executor); + vaultService.lock(vault.get(), false); + // TODO count lock attempts, and allow forced lock } /* Getter/Setter */ 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 897bce32c..c7bf82c80 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,17 +1,14 @@ package org.cryptomator.ui.quit; -import javafx.application.Platform; import javafx.collections.ObservableList; -import javafx.concurrent.ScheduledService; -import javafx.concurrent.Task; +import javafx.concurrent.Service; 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.VaultState; -import org.cryptomator.common.vaults.Volume; import org.cryptomator.ui.common.FxController; +import org.cryptomator.ui.common.VaultService; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -19,7 +16,6 @@ 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 @@ -31,14 +27,16 @@ public class QuitController implements FxController { private final QuitResponse response; private final ObservableList unlockedVaults; private final ExecutorService executor; + private final VaultService vaultService; public Button lockAndQuitButton; @Inject - QuitController(@QuitWindow Stage window, QuitResponse response, ObservableList vaults, ExecutorService executor) { + QuitController(@QuitWindow Stage window, QuitResponse response, ObservableList vaults, ExecutorService executor, VaultService vaultService) { this.window = window; this.response = response; this.unlockedVaults = vaults.filtered(Vault::isUnlocked); this.executor = executor; + this.vaultService = vaultService; } @FXML @@ -52,11 +50,11 @@ public class QuitController implements FxController { public void lockAndQuit() { lockAndQuitButton.setDisable(true); lockAndQuitButton.setContentDisplay(ContentDisplay.LEFT); - - Iterator toBeLocked = List.copyOf(unlockedVaults).iterator(); - ScheduledService lockAllService = new LockAllVaultsService(executor, toBeLocked); + + Service lockAllService = vaultService.createLockAllService(List.copyOf(unlockedVaults), false); + lockAllService.setOnSucceeded(evt -> { - if (!toBeLocked.hasNext()) { + if (unlockedVaults.isEmpty()) { window.close(); response.performQuit(); } @@ -71,65 +69,4 @@ public class QuitController implements FxController { lockAllService.start(); } - /** - * @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.setOnScheduled(evt -> { - vault.setState(VaultState.PROCESSING); - }); - task.setOnSucceeded(evt -> { - vault.setState(VaultState.LOCKED); - }); - task.setOnFailed(evt -> { - LOG.warn("Failed to lock vault", vault); - }); - return task; - } - - /** - * @return Task that succeeds immediately - */ - private Task createNoopTask() { - return new Task<>() { - @Override - protected Void call() { - return null; - } - }; - } - - private class LockAllVaultsService extends ScheduledService { - - 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(); - } - } - } - }