diff --git a/src/main/java/org/cryptomator/common/vaults/VaultState.java b/src/main/java/org/cryptomator/common/vaults/VaultState.java index 30be80fcb..e69890a53 100644 --- a/src/main/java/org/cryptomator/common/vaults/VaultState.java +++ b/src/main/java/org/cryptomator/common/vaults/VaultState.java @@ -8,7 +8,6 @@ import javax.inject.Inject; import javafx.application.Platform; import javafx.beans.value.ObservableObjectValue; import javafx.beans.value.ObservableValueBase; -import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicReference; import java.util.concurrent.locks.Condition; import java.util.concurrent.locks.Lock; @@ -98,31 +97,6 @@ public class VaultState extends ObservableValueBase implements return success; } - /** - * Waits for the specified time, until the desired state is reached. - * - * @param desiredState what state to wait for - * @param time the maximum time to wait - * @param unit the time unit of the {@code time} argument - * @return {@code false} if the waiting time detectably elapsed before reaching {@code desiredState} - * @throws InterruptedException if the current thread is interrupted - */ - public boolean awaitState(Value desiredState, long time, TimeUnit unit) throws InterruptedException { - lock.lock(); - try { - long remaining = TimeUnit.NANOSECONDS.convert(time, unit); - while (value.get() != desiredState) { - if (remaining <= 0L) { - return false; - } - remaining = valueChanged.awaitNanos(remaining); - } - return true; - } finally { - lock.unlock(); - } - } - /** * Waits, without a timeout, until the state differs from the specified one. * diff --git a/src/main/java/org/cryptomator/ui/unlock/UnlockWorkflow.java b/src/main/java/org/cryptomator/ui/unlock/UnlockWorkflow.java index 8b22d22be..5214d5629 100644 --- a/src/main/java/org/cryptomator/ui/unlock/UnlockWorkflow.java +++ b/src/main/java/org/cryptomator/ui/unlock/UnlockWorkflow.java @@ -5,7 +5,6 @@ import org.cryptomator.common.mount.ConflictingMountServiceException; import org.cryptomator.common.mount.FSKitExtensionSwitchedOffException; import org.cryptomator.common.mount.IllegalMountPointException; import org.cryptomator.common.vaults.Vault; -import org.cryptomator.common.vaults.VaultState; import org.cryptomator.cryptolib.api.CryptoException; import org.cryptomator.integrations.mount.MountFailedException; import org.cryptomator.ui.common.FxmlFile; @@ -30,7 +29,6 @@ import javafx.stage.Screen; import javafx.stage.Stage; import java.io.IOException; import java.nio.file.ReadOnlyFileSystemException; -import java.util.concurrent.TimeUnit; /** * A multi-step task that consists of background activities as well as user interaction. @@ -191,17 +189,11 @@ public class UnlockWorkflow extends Task { } private void restartUnlock() { - try { - var isLocked = vault.stateProperty().awaitState(VaultState.Value.LOCKED, 5, TimeUnit.SECONDS); - if (!isLocked) { - LOG.error("Vault did not changed to LOCKED state within 5 seconds. Aborting unlock retry."); - } else { - appWindows.startUnlockWorkflow(vault, owner); - } - } catch (InterruptedException e) { - LOG.error("Waiting for LOCKED vault state was interrupted. Aborting unlock retry.", e); - Thread.currentThread().interrupt(); + if (!vault.isLocked()) { + LOG.warn("Vault '{}' is {} instead of LOCKED. Aborting unlock retry.", vault.getDisplayName(), vault.getState()); + return; } + appWindows.startUnlockWorkflow(vault, owner); } @Override diff --git a/src/test/java/org/cryptomator/ui/unlock/UnlockWorkflowTest.java b/src/test/java/org/cryptomator/ui/unlock/UnlockWorkflowTest.java index 1a3c54c03..129d2dca8 100644 --- a/src/test/java/org/cryptomator/ui/unlock/UnlockWorkflowTest.java +++ b/src/test/java/org/cryptomator/ui/unlock/UnlockWorkflowTest.java @@ -5,7 +5,6 @@ import org.cryptomator.common.mount.FSKitExtensionSwitchedOffException; import org.cryptomator.common.settings.VaultSettings; import org.cryptomator.common.settings.WhenUnlocked; import org.cryptomator.common.vaults.Vault; -import org.cryptomator.common.vaults.VaultState; import org.cryptomator.integrations.mount.MountFailedException; import org.cryptomator.ui.common.VaultService; import org.cryptomator.ui.dialogs.Dialogs; @@ -25,7 +24,6 @@ import javafx.beans.property.SimpleObjectProperty; import javafx.stage.Stage; import java.io.IOException; import java.nio.file.ReadOnlyFileSystemException; -import java.util.concurrent.TimeUnit; import java.util.function.Consumer; class UnlockWorkflowTest { @@ -132,19 +130,34 @@ class UnlockWorkflowTest { private void assertExtensionSwitchedOffRetryRestartsUnlockWith(Stage unlockOwner) throws Exception { workflow = createWorkflow(unlockOwner); - Mockito.when(dialogs.prepareFSKitExtensionSwitchedOffDialog(Mockito.any(), Mockito.any(), Mockito.any())).thenReturn(dialogBuilder); - runFailingWith(new FSKitExtensionSwitchedOffException(new MountFailedException("switched off"))); - ArgumentCaptor> okAction = ArgumentCaptor.captor(); - Mockito.verify(dialogs).prepareFSKitExtensionSwitchedOffDialog(Mockito.any(), Mockito.any(), okAction.capture()); + var okAction = failWithExtensionSwitchedOffAndCaptureRetry(); var dialogStage = Mockito.mock(Stage.class); - okAction.getValue().accept(dialogStage); + okAction.accept(dialogStage); Mockito.verify(dialogStage).close(); Mockito.verify(appWindows).startUnlockWorkflow(vault, unlockOwner); Assertions.assertFalse(vaultSettings.usesReadOnlyMode.get()); } + @Test + public void testExtensionSwitchedOffRetryAbortsWhenVaultIsNotLocked() throws Exception { + var okAction = failWithExtensionSwitchedOffAndCaptureRetry(); + Mockito.when(vault.isLocked()).thenReturn(false); // e.g. an unlock from the tray menu started while the dialog was open + + okAction.accept(Mockito.mock(Stage.class)); + + Mockito.verify(appWindows, Mockito.never()).startUnlockWorkflow(Mockito.any(), Mockito.any()); + } + + private Consumer failWithExtensionSwitchedOffAndCaptureRetry() throws Exception { + Mockito.when(dialogs.prepareFSKitExtensionSwitchedOffDialog(Mockito.any(), Mockito.any(), Mockito.any())).thenReturn(dialogBuilder); + runFailingWith(new FSKitExtensionSwitchedOffException(new MountFailedException("switched off"))); + ArgumentCaptor> okAction = ArgumentCaptor.captor(); + Mockito.verify(dialogs).prepareFSKitExtensionSwitchedOffDialog(Mockito.any(), Mockito.any(), okAction.capture()); + return okAction.getValue(); + } + @Test public void testReadOnlyRetryRestartsUnlockWithOwner() throws Exception { Mockito.when(dialogs.prepareRetryIfReadonlyDialog(Mockito.any(), Mockito.any())).thenReturn(dialogBuilder); @@ -166,9 +179,7 @@ class UnlockWorkflowTest { invocation.getArgument(0, Runnable.class).run(); return null; }); - var vaultState = Mockito.mock(VaultState.class); - Mockito.when(vaultState.awaitState(VaultState.Value.LOCKED, 5, TimeUnit.SECONDS)).thenReturn(true); - Mockito.when(vault.stateProperty()).thenReturn(vaultState); + Mockito.when(vault.isLocked()).thenReturn(true); Mockito.doThrow(exception).when(keyLoadingStrategy).use(Mockito.any()); workflow.run();