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 index 6e76a2beb..8e3cabff0 100644 --- a/main/ui/src/main/java/org/cryptomator/ui/common/VaultService.java +++ b/main/ui/src/main/java/org/cryptomator/ui/common/VaultService.java @@ -1,5 +1,6 @@ package org.cryptomator.ui.common; +import com.google.common.collect.ImmutableList; import javafx.application.Platform; import javafx.concurrent.ScheduledService; import javafx.concurrent.Service; @@ -35,40 +36,38 @@ public class VaultService { * @param forced Whether to attempt a forced lock */ public void lock(Vault vault, boolean forced) { - Task task = createLockTask(vault, forced); + Task task = new LockVaultTask(vault, forced); task.setOnSucceeded(evt -> LOG.info("Locked {}", vault.getDisplayableName())); - task.setOnFailed(evt -> LOG.error("Failed to lock vault " + vault.getDisplayableName(), evt.getSource().getException())); + task.setOnFailed(evt -> LOG.error("Failed to lock ", evt.getSource().getException())); executorService.execute(task); } public void lockAll(Collection vaults, boolean forced) { - Service service = createLockAllService(vaults, forced); + Service service = createLockAllService(vaults, forced); + service.setOnSucceeded(evt -> LOG.info("Locked {}", service.getValue().getDisplayableName())); 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 vaults The list of vaults to be locked * @param forced Whether to attempt a forced lock - * @return Service that tries to lock all given vaults + * @return Service that tries to lock all given vaults and cancels itself automatically when done */ - public Service createLockAllService(Collection vaults, boolean forced) { - Iterator iter = vaults.iterator(); - ScheduledService service = new ScheduledService<>() { + public Service createLockAllService(Collection vaults, boolean forced) { + Iterator iter = ImmutableList.copyOf(vaults).iterator(); + ScheduledService service = new ScheduledService<>() { @Override - protected Task createTask() { + protected Task createTask() { assert Platform.isFxApplicationThread(); if (iter.hasNext()) { - return createLockTask(iter.next(), forced); + return new LockVaultTask(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(); + cancel(); + return new IllegalStateTask("This task should never be executed."); } } }; @@ -77,44 +76,63 @@ public class VaultService { } /** - * 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 + * A task that locks a vault */ - public Task createLockTask(Vault vault, boolean forced) { - return new Task<>() { - @Override - protected Void call() throws Volume.VolumeException { - vault.lock(forced); - return null; - } + private static class LockVaultTask extends Task { - @Override - protected void scheduled() { - vault.setState(VaultState.PROCESSING); - } + private final Vault vault; + private final boolean forced; - @Override - protected void succeeded() { - vault.setState(VaultState.LOCKED); - } + /** + * @param vault The vault to lock + * @param forced Whether to attempt a forced lock + */ + public LockVaultTask(Vault vault, boolean forced) { + this.vault = vault; + this.forced = forced; + } + + @Override + protected Vault call() throws Volume.VolumeException { + vault.lock(forced); + return vault; + } + + @Override + protected void scheduled() { + vault.setState(VaultState.PROCESSING); + } + + @Override + protected void succeeded() { + vault.setState(VaultState.LOCKED); + } + + @Override + protected void failed() { + vault.setState(VaultState.UNLOCKED); + } - @Override - protected void failed() { - vault.setState(VaultState.UNLOCKED); - } - }; } - private Task createNoopTask() { - return new Task<>() { - @Override - protected Void call() { - return null; - } - }; + /** + * A task that throws an IllegalStateException + */ + private static class IllegalStateTask extends Task { + + private final String message; + + /** + * @param message The message of the IllegalStateException + */ + public IllegalStateTask(String message) { + this.message = message; + } + + @Override + protected V call() throws IllegalStateException { + throw new IllegalStateException(message); + } } } 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 750e19373..3faffca61 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 @@ -47,15 +47,17 @@ public class QuitController implements FxController { lockAndQuitButton.setDisable(true); lockAndQuitButton.setContentDisplay(ContentDisplay.LEFT); - Service lockAllService = vaultService.createLockAllService(List.copyOf(unlockedVaults), false); + Service lockAllService = vaultService.createLockAllService(unlockedVaults, false); lockAllService.setOnSucceeded(evt -> { + LOG.info("Locked {}", lockAllService.getValue().getDisplayableName()); if (unlockedVaults.isEmpty()) { window.close(); response.performQuit(); } }); lockAllService.setOnFailed(evt -> { + LOG.warn("Locking failed", lockAllService.getException()); lockAndQuitButton.setDisable(false); lockAndQuitButton.setContentDisplay(ContentDisplay.TEXT_ONLY); // TODO: show force lock or force quit scene (and DO NOT cancelQuit() here!)