From ba627d0d6021217ece6eddb6ab889a5ee3c936d8 Mon Sep 17 00:00:00 2001 From: Rexbas Date: Sun, 19 Feb 2023 16:29:06 +0100 Subject: [PATCH 1/5] Add a scheduled service to auto unlock vaults that were missing at startup --- .../cryptomator/ui/fxapp/AutoUnlocker.java | 67 +++++++++++++++++-- .../cryptomator/ui/fxapp/FxApplication.java | 4 +- 2 files changed, 62 insertions(+), 9 deletions(-) diff --git a/src/main/java/org/cryptomator/ui/fxapp/AutoUnlocker.java b/src/main/java/org/cryptomator/ui/fxapp/AutoUnlocker.java index 973d919fc..410910e47 100644 --- a/src/main/java/org/cryptomator/ui/fxapp/AutoUnlocker.java +++ b/src/main/java/org/cryptomator/ui/fxapp/AutoUnlocker.java @@ -1,30 +1,83 @@ package org.cryptomator.ui.fxapp; import org.cryptomator.common.vaults.Vault; +import org.cryptomator.common.vaults.VaultListManager; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import javax.inject.Inject; import javafx.collections.ObservableList; +import java.util.Arrays; import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletionStage; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.ScheduledFuture; +import java.util.concurrent.TimeUnit; +import java.util.stream.Stream; @FxApplicationScoped public class AutoUnlocker { + private static final Logger LOG = LoggerFactory.getLogger(AutoUnlocker.class); + private final ObservableList vaults; private final FxApplicationWindows appWindows; + private final ScheduledExecutorService scheduler; + private ScheduledFuture future; + private boolean isPeriodicCheckActive = false; @Inject - public AutoUnlocker(ObservableList vaults, FxApplicationWindows appWindows) { + public AutoUnlocker(ObservableList vaults, FxApplicationWindows appWindows, ScheduledExecutorService scheduler) { this.vaults = vaults; this.appWindows = appWindows; + this.scheduler = scheduler; } - public void unlock() { - vaults.stream().filter(Vault::isLocked) // - .filter(v -> v.getVaultSettings().unlockAfterStartup().get()) // - .>reduce(CompletableFuture.completedFuture(null), // - (unlockFlow, v) -> unlockFlow.handle((voit, ex) -> appWindows.startUnlockWorkflow(v, null)).thenCompose(stage -> stage), //we don't care here about the exception, logged elsewhere - (unlockChain1, unlockChain2) -> unlockChain1.handle((voit, ex) -> unlockChain2).thenCompose(stage -> stage)); + public void unlockAll() { + unlock(vaults.stream().filter(v -> v.getVaultSettings().unlockAfterStartup().get())); } + public void unlock(Stream vaultStream) { + vaultStream.filter(Vault::isLocked) + .>reduce(CompletableFuture.completedFuture(null), + (unlockFlow, v) -> unlockFlow.handle((voit, ex) -> appWindows.startUnlockWorkflow(v, null)).thenCompose(stage -> stage), // we don't care here about the exception, logged elsewhere + (unlockChain1, unlockChain2) -> unlockChain1.handle((voit, ex) -> unlockChain2).thenCompose(stage -> stage)); + } + + public void startMissingVaultsChecker() { + if (!isPeriodicCheckActive && getMissingAutoUnlockVaults().count() > 0) { + LOG.info("Found MISSING vaults, starting periodic check"); + future = scheduler.scheduleWithFixedDelay(this::tick, 0, 1, TimeUnit.SECONDS); + isPeriodicCheckActive = true; + } + } + + private void tick() { + // Find the vaults that are missing but have an existing directory + Vault[] vaultArray = getMissingAutoUnlockVaults().filter(v -> v.getPath().toFile().isDirectory()).toArray(Vault[]::new); + if (vaultArray.length > 0) { + + // Redetermine vault states + for (Vault v : vaultArray) { + LOG.info("Found vault directory for '{}'", v.getDisplayName()); + VaultListManager.redetermineVaultState(v); + } + + // Unlock the vaults that were previously missing + unlock(Arrays.stream(vaultArray)); + } + + // Stop checking if there are no more missing vaults + if (getMissingAutoUnlockVaults().count() == 0) { + LOG.info("No more MISSING vaults, stopping periodic check"); + isPeriodicCheckActive = false; + future.cancel(false); + } + } + + private Stream getMissingAutoUnlockVaults() { + return vaults.stream() + .filter(Vault::isMissing) + .filter(v -> v.getVaultSettings().unlockAfterStartup().get()); + } } diff --git a/src/main/java/org/cryptomator/ui/fxapp/FxApplication.java b/src/main/java/org/cryptomator/ui/fxapp/FxApplication.java index 3ddb7cba6..850d2a91e 100644 --- a/src/main/java/org/cryptomator/ui/fxapp/FxApplication.java +++ b/src/main/java/org/cryptomator/ui/fxapp/FxApplication.java @@ -68,7 +68,7 @@ public class FxApplication { }); launchEventHandler.startHandlingLaunchEvents(); - autoUnlocker.unlock(); + autoUnlocker.unlockAll(); + autoUnlocker.startMissingVaultsChecker(); } - } From 72fd38baf15808537e3ef2931d626fef9950019c Mon Sep 17 00:00:00 2001 From: Rexbas Date: Sat, 6 May 2023 15:40:24 +0200 Subject: [PATCH 2/5] Add timeout to periodic missing vaults check --- .../org/cryptomator/ui/fxapp/AutoUnlocker.java | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/src/main/java/org/cryptomator/ui/fxapp/AutoUnlocker.java b/src/main/java/org/cryptomator/ui/fxapp/AutoUnlocker.java index 410910e47..79b440782 100644 --- a/src/main/java/org/cryptomator/ui/fxapp/AutoUnlocker.java +++ b/src/main/java/org/cryptomator/ui/fxapp/AutoUnlocker.java @@ -23,7 +23,8 @@ public class AutoUnlocker { private final ObservableList vaults; private final FxApplicationWindows appWindows; private final ScheduledExecutorService scheduler; - private ScheduledFuture future; + private ScheduledFuture checkFuture; + private ScheduledFuture timeoutFuture; private boolean isPeriodicCheckActive = false; @Inject @@ -47,12 +48,13 @@ public class AutoUnlocker { public void startMissingVaultsChecker() { if (!isPeriodicCheckActive && getMissingAutoUnlockVaults().count() > 0) { LOG.info("Found MISSING vaults, starting periodic check"); - future = scheduler.scheduleWithFixedDelay(this::tick, 0, 1, TimeUnit.SECONDS); + checkFuture = scheduler.scheduleWithFixedDelay(this::check, 0, 1, TimeUnit.SECONDS); + timeoutFuture = scheduler.schedule(this::timeout, 2, TimeUnit.MINUTES); isPeriodicCheckActive = true; } } - private void tick() { + private void check() { // Find the vaults that are missing but have an existing directory Vault[] vaultArray = getMissingAutoUnlockVaults().filter(v -> v.getPath().toFile().isDirectory()).toArray(Vault[]::new); if (vaultArray.length > 0) { @@ -71,10 +73,17 @@ public class AutoUnlocker { if (getMissingAutoUnlockVaults().count() == 0) { LOG.info("No more MISSING vaults, stopping periodic check"); isPeriodicCheckActive = false; - future.cancel(false); + checkFuture.cancel(false); + timeoutFuture.cancel(false); } } + private void timeout() { + LOG.info("MISSING vaults periodic check timed out"); + isPeriodicCheckActive = false; + checkFuture.cancel(false); + } + private Stream getMissingAutoUnlockVaults() { return vaults.stream() .filter(Vault::isMissing) From 1253b7db2b3056427a70a448ca5abcebc35740d7 Mon Sep 17 00:00:00 2001 From: Rexbas Date: Thu, 11 May 2023 20:43:23 +0200 Subject: [PATCH 3/5] Make unlock method private and simplify missing vaults unlocker --- .../cryptomator/ui/fxapp/AutoUnlocker.java | 52 +++++++------------ .../cryptomator/ui/fxapp/FxApplication.java | 3 +- 2 files changed, 20 insertions(+), 35 deletions(-) diff --git a/src/main/java/org/cryptomator/ui/fxapp/AutoUnlocker.java b/src/main/java/org/cryptomator/ui/fxapp/AutoUnlocker.java index 79b440782..a4754fd29 100644 --- a/src/main/java/org/cryptomator/ui/fxapp/AutoUnlocker.java +++ b/src/main/java/org/cryptomator/ui/fxapp/AutoUnlocker.java @@ -7,7 +7,7 @@ import org.slf4j.LoggerFactory; import javax.inject.Inject; import javafx.collections.ObservableList; -import java.util.Arrays; +import java.util.List; import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletionStage; import java.util.concurrent.ScheduledExecutorService; @@ -23,9 +23,8 @@ public class AutoUnlocker { private final ObservableList vaults; private final FxApplicationWindows appWindows; private final ScheduledExecutorService scheduler; - private ScheduledFuture checkFuture; + private ScheduledFuture unlockMissingFuture; private ScheduledFuture timeoutFuture; - private boolean isPeriodicCheckActive = false; @Inject public AutoUnlocker(ObservableList vaults, FxApplicationWindows appWindows, ScheduledExecutorService scheduler) { @@ -34,54 +33,41 @@ public class AutoUnlocker { this.scheduler = scheduler; } - public void unlockAll() { + public void tryUnlockForTimespan(int timespan) { + // Unlock all available auto unlock vaults unlock(vaults.stream().filter(v -> v.getVaultSettings().unlockAfterStartup().get())); + + // Start a temporary service if there are missing auto unlock vaults + if (getMissingAutoUnlockVaults().findAny().isPresent()) { + LOG.info("Found MISSING vaults, starting periodic check"); + unlockMissingFuture = scheduler.scheduleWithFixedDelay(this::unlockMissing, 0, 1, TimeUnit.SECONDS); + timeoutFuture = scheduler.schedule(this::timeout, timespan, TimeUnit.MINUTES); + } } - public void unlock(Stream vaultStream) { + private void unlock(Stream vaultStream) { vaultStream.filter(Vault::isLocked) .>reduce(CompletableFuture.completedFuture(null), (unlockFlow, v) -> unlockFlow.handle((voit, ex) -> appWindows.startUnlockWorkflow(v, null)).thenCompose(stage -> stage), // we don't care here about the exception, logged elsewhere (unlockChain1, unlockChain2) -> unlockChain1.handle((voit, ex) -> unlockChain2).thenCompose(stage -> stage)); } - public void startMissingVaultsChecker() { - if (!isPeriodicCheckActive && getMissingAutoUnlockVaults().count() > 0) { - LOG.info("Found MISSING vaults, starting periodic check"); - checkFuture = scheduler.scheduleWithFixedDelay(this::check, 0, 1, TimeUnit.SECONDS); - timeoutFuture = scheduler.schedule(this::timeout, 2, TimeUnit.MINUTES); - isPeriodicCheckActive = true; - } - } - - private void check() { - // Find the vaults that are missing but have an existing directory - Vault[] vaultArray = getMissingAutoUnlockVaults().filter(v -> v.getPath().toFile().isDirectory()).toArray(Vault[]::new); - if (vaultArray.length > 0) { - - // Redetermine vault states - for (Vault v : vaultArray) { - LOG.info("Found vault directory for '{}'", v.getDisplayName()); - VaultListManager.redetermineVaultState(v); - } - - // Unlock the vaults that were previously missing - unlock(Arrays.stream(vaultArray)); - } + private void unlockMissing() { + List missingAutoUnlockVaults = getMissingAutoUnlockVaults().toList(); + missingAutoUnlockVaults.forEach(VaultListManager::redetermineVaultState); + unlock(missingAutoUnlockVaults.stream()); // Stop checking if there are no more missing vaults - if (getMissingAutoUnlockVaults().count() == 0) { + if (getMissingAutoUnlockVaults().findAny().isEmpty()) { LOG.info("No more MISSING vaults, stopping periodic check"); - isPeriodicCheckActive = false; - checkFuture.cancel(false); + unlockMissingFuture.cancel(false); timeoutFuture.cancel(false); } } private void timeout() { LOG.info("MISSING vaults periodic check timed out"); - isPeriodicCheckActive = false; - checkFuture.cancel(false); + unlockMissingFuture.cancel(false); } private Stream getMissingAutoUnlockVaults() { diff --git a/src/main/java/org/cryptomator/ui/fxapp/FxApplication.java b/src/main/java/org/cryptomator/ui/fxapp/FxApplication.java index 850d2a91e..736b793d2 100644 --- a/src/main/java/org/cryptomator/ui/fxapp/FxApplication.java +++ b/src/main/java/org/cryptomator/ui/fxapp/FxApplication.java @@ -68,7 +68,6 @@ public class FxApplication { }); launchEventHandler.startHandlingLaunchEvents(); - autoUnlocker.unlockAll(); - autoUnlocker.startMissingVaultsChecker(); + autoUnlocker.tryUnlockForTimespan(2); } } From 32d7189a12817a72b9bade58bd79484197852ddc Mon Sep 17 00:00:00 2001 From: Rexbas Date: Fri, 12 May 2023 21:52:30 +0200 Subject: [PATCH 4/5] Add time unit parameter --- src/main/java/org/cryptomator/ui/fxapp/AutoUnlocker.java | 4 ++-- src/main/java/org/cryptomator/ui/fxapp/FxApplication.java | 3 ++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/main/java/org/cryptomator/ui/fxapp/AutoUnlocker.java b/src/main/java/org/cryptomator/ui/fxapp/AutoUnlocker.java index a4754fd29..bf40971c6 100644 --- a/src/main/java/org/cryptomator/ui/fxapp/AutoUnlocker.java +++ b/src/main/java/org/cryptomator/ui/fxapp/AutoUnlocker.java @@ -33,7 +33,7 @@ public class AutoUnlocker { this.scheduler = scheduler; } - public void tryUnlockForTimespan(int timespan) { + public void tryUnlockForTimespan(int timespan, TimeUnit timeUnit) { // Unlock all available auto unlock vaults unlock(vaults.stream().filter(v -> v.getVaultSettings().unlockAfterStartup().get())); @@ -41,7 +41,7 @@ public class AutoUnlocker { if (getMissingAutoUnlockVaults().findAny().isPresent()) { LOG.info("Found MISSING vaults, starting periodic check"); unlockMissingFuture = scheduler.scheduleWithFixedDelay(this::unlockMissing, 0, 1, TimeUnit.SECONDS); - timeoutFuture = scheduler.schedule(this::timeout, timespan, TimeUnit.MINUTES); + timeoutFuture = scheduler.schedule(this::timeout, timespan, timeUnit); } } diff --git a/src/main/java/org/cryptomator/ui/fxapp/FxApplication.java b/src/main/java/org/cryptomator/ui/fxapp/FxApplication.java index 736b793d2..cfc7ba62b 100644 --- a/src/main/java/org/cryptomator/ui/fxapp/FxApplication.java +++ b/src/main/java/org/cryptomator/ui/fxapp/FxApplication.java @@ -9,6 +9,7 @@ import org.slf4j.LoggerFactory; import javax.inject.Inject; import javax.inject.Named; import javafx.application.Platform; +import java.util.concurrent.TimeUnit; @FxApplicationScoped public class FxApplication { @@ -68,6 +69,6 @@ public class FxApplication { }); launchEventHandler.startHandlingLaunchEvents(); - autoUnlocker.tryUnlockForTimespan(2); + autoUnlocker.tryUnlockForTimespan(2, TimeUnit.MINUTES); } } From f338d2447bab04a2054071d12134ad8ddc596363 Mon Sep 17 00:00:00 2001 From: Sebastian Stenzel Date: Tue, 23 May 2023 09:48:27 +0200 Subject: [PATCH 5/5] improved AutoUnlocker readability --- .../cryptomator/ui/fxapp/AutoUnlocker.java | 25 ++++++++++++------- .../ui/fxapp/FxApplicationWindows.java | 3 ++- 2 files changed, 18 insertions(+), 10 deletions(-) diff --git a/src/main/java/org/cryptomator/ui/fxapp/AutoUnlocker.java b/src/main/java/org/cryptomator/ui/fxapp/AutoUnlocker.java index bf40971c6..e99cd6680 100644 --- a/src/main/java/org/cryptomator/ui/fxapp/AutoUnlocker.java +++ b/src/main/java/org/cryptomator/ui/fxapp/AutoUnlocker.java @@ -13,6 +13,7 @@ import java.util.concurrent.CompletionStage; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledFuture; import java.util.concurrent.TimeUnit; +import java.util.function.Predicate; import java.util.stream.Stream; @FxApplicationScoped @@ -35,8 +36,19 @@ public class AutoUnlocker { public void tryUnlockForTimespan(int timespan, TimeUnit timeUnit) { // Unlock all available auto unlock vaults - unlock(vaults.stream().filter(v -> v.getVaultSettings().unlockAfterStartup().get())); + Predicate shouldAutoUnlock = v -> v.getVaultSettings().unlockAfterStartup().get(); + unlockSequentially(vaults.stream().filter(shouldAutoUnlock)).thenRun(() -> startUnlockMissing(timespan, timeUnit)); + } + private CompletionStage unlockSequentially(Stream vaultStream) { + // this is an attempt to run all the unlock workflows sequentially, i.e. start the next workflow only after completing/failing the previous workflow. + return vaultStream.filter(Vault::isLocked).reduce(CompletableFuture.completedFuture(null), + (prevUnlock, nextVault) -> prevUnlock.thenCompose(unused -> appWindows.startUnlockWorkflow(nextVault, null)), + (prevUnlock, nextUnlock) -> nextUnlock.exceptionally(e -> null) // we don't care here about the exception, logged elsewhere + ); + } + + private void startUnlockMissing(int timespan, TimeUnit timeUnit) { // Start a temporary service if there are missing auto unlock vaults if (getMissingAutoUnlockVaults().findAny().isPresent()) { LOG.info("Found MISSING vaults, starting periodic check"); @@ -45,18 +57,13 @@ public class AutoUnlocker { } } - private void unlock(Stream vaultStream) { - vaultStream.filter(Vault::isLocked) - .>reduce(CompletableFuture.completedFuture(null), - (unlockFlow, v) -> unlockFlow.handle((voit, ex) -> appWindows.startUnlockWorkflow(v, null)).thenCompose(stage -> stage), // we don't care here about the exception, logged elsewhere - (unlockChain1, unlockChain2) -> unlockChain1.handle((voit, ex) -> unlockChain2).thenCompose(stage -> stage)); - } - private void unlockMissing() { List missingAutoUnlockVaults = getMissingAutoUnlockVaults().toList(); missingAutoUnlockVaults.forEach(VaultListManager::redetermineVaultState); - unlock(missingAutoUnlockVaults.stream()); + unlockSequentially(missingAutoUnlockVaults.stream()).thenRun(this::stopUnlockMissing); + } + private void stopUnlockMissing() { // Stop checking if there are no more missing vaults if (getMissingAutoUnlockVaults().findAny().isEmpty()) { LOG.info("No more MISSING vaults, stopping periodic check"); diff --git a/src/main/java/org/cryptomator/ui/fxapp/FxApplicationWindows.java b/src/main/java/org/cryptomator/ui/fxapp/FxApplicationWindows.java index 2b4f8e7bc..000954bd5 100644 --- a/src/main/java/org/cryptomator/ui/fxapp/FxApplicationWindows.java +++ b/src/main/java/org/cryptomator/ui/fxapp/FxApplicationWindows.java @@ -12,6 +12,7 @@ import org.cryptomator.ui.preferences.PreferencesComponent; import org.cryptomator.ui.preferences.SelectedPreferencesTab; import org.cryptomator.ui.quit.QuitComponent; import org.cryptomator.ui.unlock.UnlockComponent; +import org.cryptomator.ui.unlock.UnlockWorkflow; import org.jetbrains.annotations.Nullable; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -114,7 +115,7 @@ public class FxApplicationWindows { LOG.debug("Start unlock workflow for {}", vault.getDisplayName()); return unlockWorkflowFactory.create(vault, owner).unlockWorkflow(); }, Platform::runLater) // - .thenCompose(unlockWorkflow -> CompletableFuture.runAsync(unlockWorkflow, executor)) // + .thenAcceptAsync(UnlockWorkflow::run, executor) .exceptionally(e -> { showErrorWindow(e, owner == null ? primaryStage : owner, null); return null;