alternative thread-safe vault state without requiring explicit synchronization

This commit is contained in:
Sebastian Stenzel
2021-04-08 11:23:57 +02:00
parent beba6490c3
commit c306d8df04
16 changed files with 150 additions and 154 deletions

View File

@@ -13,9 +13,6 @@ import org.slf4j.LoggerFactory;
import javax.inject.Inject;
import javax.inject.Named;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionStage;
import java.util.concurrent.ExecutorService;
import java.util.function.Consumer;
public class DokanyVolume extends AbstractVolume {

View File

@@ -27,7 +27,6 @@ import org.slf4j.LoggerFactory;
import javax.inject.Inject;
import javax.inject.Named;
import javax.inject.Provider;
import javafx.application.Platform;
import javafx.beans.Observable;
import javafx.beans.binding.Bindings;
import javafx.beans.binding.BooleanBinding;
@@ -44,7 +43,6 @@ import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.atomic.AtomicReference;
import java.util.concurrent.locks.StampedLock;
import static org.cryptomator.common.Constants.MASTERKEY_FILENAME;
@@ -54,13 +52,11 @@ public class Vault {
private static final Logger LOG = LoggerFactory.getLogger(Vault.class);
private static final Path HOME_DIR = Paths.get(SystemUtils.USER_HOME);
private final StampedLock stateLock;
private final VaultSettings vaultSettings;
private final Provider<Volume> volumeProvider;
private final StringBinding defaultMountFlags;
private final AtomicReference<CryptoFileSystem> cryptoFileSystem;
private final ObjectProperty<VaultState> state;
private final VaultState state;
private final ObjectProperty<Exception> lastKnownException;
private final VaultStats stats;
private final StringBinding displayName;
@@ -78,7 +74,7 @@ public class Vault {
private volatile Volume volume;
@Inject
Vault(VaultSettings vaultSettings, Provider<Volume> volumeProvider, @DefaultMountFlags StringBinding defaultMountFlags, AtomicReference<CryptoFileSystem> cryptoFileSystem, ObjectProperty<VaultState> state, @Named("lastKnownException") ObjectProperty<Exception> lastKnownException, VaultStats stats) {
Vault(VaultSettings vaultSettings, Provider<Volume> volumeProvider, @DefaultMountFlags StringBinding defaultMountFlags, AtomicReference<CryptoFileSystem> cryptoFileSystem, VaultState state, @Named("lastKnownException") ObjectProperty<Exception> lastKnownException, VaultStats stats) {
this.vaultSettings = vaultSettings;
this.volumeProvider = volumeProvider;
this.defaultMountFlags = defaultMountFlags;
@@ -97,8 +93,6 @@ public class Vault {
this.accessPoint = Bindings.createStringBinding(this::getAccessPoint, state);
this.accessPointPresent = this.accessPoint.isNotEmpty();
this.showingStats = new SimpleBooleanProperty(false);
this.stateLock = new StampedLock();
}
// ******************************************************************************
@@ -146,12 +140,9 @@ public class Vault {
try {
volume = volumeProvider.get();
volume.mount(fs, getEffectiveMountFlags(), throwable -> {
LOG.info("Unmounted vault '{}'", getDisplayName());
destroyCryptoFileSystem();
new Thread(() -> { //TODO: maybe use the executor service
long stamp = stateLock.writeLock();
setState(VaultState.LOCKED, stamp);
stateLock.unlock(stamp);
}).start();
state.set(VaultState.Value.LOCKED);
if (throwable != null) {
LOG.warn("Unexpected unmount and lock of vault " + getDisplayName(), throwable);
}
@@ -182,32 +173,12 @@ public class Vault {
// Observable Properties
// *******************************************************************************
public ObjectProperty<VaultState> stateProperty() {
public VaultState stateProperty() {
return state;
}
public VaultState getState() {
return state.get();
}
public long lockVaultState() {
return stateLock.writeLock();
}
public void unlockVaultState(long stamp) {
stateLock.unlock(stamp);
}
public void setState(VaultState value, long stamp) {
if (stateLock.isWriteLockStamp(stamp)) {
if (Platform.isFxApplicationThread()) {
state.setValue(value);
} else {
Platform.runLater(() -> state.setValue(value));
}
} else {
throw new IllegalCallerException("Stamp is not a valid write lock.");
}
public VaultState.Value getState() {
return state.getValue();
}
public ObjectProperty<Exception> lastKnownExceptionProperty() {
@@ -227,7 +198,7 @@ public class Vault {
}
public boolean isLocked() {
return state.get() == VaultState.LOCKED;
return state.get() == VaultState.Value.LOCKED;
}
public BooleanBinding processingProperty() {
@@ -235,7 +206,7 @@ public class Vault {
}
public boolean isProcessing() {
return state.get() == VaultState.PROCESSING;
return state.get() == VaultState.Value.PROCESSING;
}
public BooleanBinding unlockedProperty() {
@@ -243,7 +214,7 @@ public class Vault {
}
public boolean isUnlocked() {
return state.get() == VaultState.UNLOCKED;
return state.get() == VaultState.Value.UNLOCKED;
}
public BooleanBinding missingProperty() {
@@ -251,7 +222,7 @@ public class Vault {
}
public boolean isMissing() {
return state.get() == VaultState.MISSING;
return state.get() == VaultState.Value.MISSING;
}
public BooleanBinding needsMigrationProperty() {
@@ -259,7 +230,7 @@ public class Vault {
}
public boolean isNeedsMigration() {
return state.get() == VaultState.NEEDS_MIGRATION;
return state.get() == VaultState.Value.NEEDS_MIGRATION;
}
public BooleanBinding unknownErrorProperty() {
@@ -267,7 +238,7 @@ public class Vault {
}
public boolean isUnknownError() {
return state.get() == VaultState.ERROR;
return state.get() == VaultState.Value.ERROR;
}
public StringBinding displayNameProperty() {
@@ -283,7 +254,7 @@ public class Vault {
}
public String getAccessPoint() {
if (state.get() == VaultState.UNLOCKED) {
if (state.getValue() == VaultState.Value.UNLOCKED) {
assert volume != null;
return volume.getMountPoint().orElse(Path.of("")).toString();
} else {

View File

@@ -26,7 +26,7 @@ public interface VaultComponent {
Builder vaultSettings(VaultSettings vaultSettings);
@BindsInstance
Builder initialVaultState(VaultState vaultState);
Builder initialVaultState(VaultState.Value vaultState);
@BindsInstance
Builder initialErrorCause(@Nullable @Named("lastKnownException") Exception initialErrorCause);

View File

@@ -93,45 +93,43 @@ public class VaultListManager {
private Vault create(VaultSettings vaultSettings) {
VaultComponent.Builder compBuilder = vaultComponentBuilder.vaultSettings(vaultSettings);
try {
VaultState vaultState = determineVaultState(vaultSettings.path().get());
VaultState.Value vaultState = determineVaultState(vaultSettings.path().get());
compBuilder.initialVaultState(vaultState);
} catch (IOException e) {
LOG.warn("Failed to determine vault state for " + vaultSettings.path().get(), e);
compBuilder.initialVaultState(VaultState.ERROR);
compBuilder.initialVaultState(VaultState.Value.ERROR);
compBuilder.initialErrorCause(e);
}
return compBuilder.build().vault();
}
public static VaultState redetermineVaultState(Vault vault) {
VaultState previousState = vault.getState();
public static VaultState.Value redetermineVaultState(Vault vault) {
VaultState state = vault.stateProperty();
VaultState.Value previousState = state.getValue();
return switch (previousState) {
case LOCKED, NEEDS_MIGRATION, MISSING -> {
long stamp = vault.lockVaultState();
try {
VaultState determinedState = determineVaultState(vault.getPath());
vault.setState(determinedState, stamp);
VaultState.Value determinedState = determineVaultState(vault.getPath());
state.set(determinedState);
yield determinedState;
} catch (IOException e) {
LOG.warn("Failed to determine vault state for " + vault.getPath(), e);
vault.setState(VaultState.ERROR, stamp);
state.set(VaultState.Value.ERROR);
vault.setLastKnownException(e);
yield VaultState.ERROR;
} finally {
vault.unlockVaultState(stamp);
yield VaultState.Value.ERROR;
}
}
case ERROR, UNLOCKED, PROCESSING -> previousState;
};
}
private static VaultState determineVaultState(Path pathToVault) throws IOException {
private static VaultState.Value determineVaultState(Path pathToVault) throws IOException {
if (!CryptoFileSystemProvider.containsVault(pathToVault, MASTERKEY_FILENAME)) {
return VaultState.MISSING;
return VaultState.Value.MISSING;
} else if (Migrators.get().needsMigration(pathToVault, MASTERKEY_FILENAME)) {
return VaultState.NEEDS_MIGRATION;
return VaultState.Value.NEEDS_MIGRATION;
} else {
return VaultState.LOCKED;
return VaultState.Value.LOCKED;
}
}

View File

@@ -40,12 +40,6 @@ public class VaultModule {
return new AtomicReference<>();
}
@Provides
@PerVault
public ObjectProperty<VaultState> provideVaultState(VaultState initialState) {
return new SimpleObjectProperty<>(initialState);
}
@Provides
@Named("lastKnownException")
@PerVault

View File

@@ -1,34 +1,92 @@
package org.cryptomator.common.vaults;
public enum VaultState {
/**
* No vault found at the provided path
*/
MISSING,
import com.google.common.base.Preconditions;
import javax.inject.Inject;
import javafx.application.Platform;
import javafx.beans.value.ObservableObjectValue;
import javafx.beans.value.ObservableValueBase;
import java.util.concurrent.atomic.AtomicReference;
@PerVault
public class VaultState extends ObservableValueBase<VaultState.Value> implements ObservableObjectValue<VaultState.Value> {
public enum Value {
/**
* No vault found at the provided path
*/
MISSING,
/**
* Vault requires migration to a newer vault format
*/
NEEDS_MIGRATION,
/**
* Vault ready to be unlocked
*/
LOCKED,
/**
* Vault in transition between two other states
*/
PROCESSING,
/**
* Vault is unlocked
*/
UNLOCKED,
/**
* Unknown state due to preceeding unrecoverable exceptions.
*/
ERROR;
}
private final AtomicReference<Value> value;
@Inject
public VaultState(VaultState.Value initialValue){
this.value = new AtomicReference<>(initialValue);
}
@Override
public Value get() {
return getValue();
}
@Override
public Value getValue() {
return value.get();
}
/**
* Vault requires migration to a newer vault format
* Transitions from <code>fromState</code> to <code>toState</code>.
* @param fromState Previous state
* @param toState New state
* @return <code>true</code> if successful
*/
NEEDS_MIGRATION,
public boolean transition(Value fromState, Value toState) {
Preconditions.checkArgument(fromState != toState, "fromState must be different than toState");
boolean success = value.compareAndSet(fromState, toState);
if (success) {
fireValueChangedEvent();
}
return success;
}
/**
* Vault ready to be unlocked
*/
LOCKED,
/**
* Vault in transition between two other states
*/
PROCESSING,
/**
* Vault is unlocked
*/
UNLOCKED,
/**
* Unknown state due to preceeding unrecoverable exceptions.
*/
ERROR;
public void set(Value newState) {
var oldState = value.getAndSet(newState);
if (oldState != newState) {
fireValueChangedEvent();
}
}
protected void fireValueChangedEvent() {
if (Platform.isFxApplicationThread()) {
super.fireValueChangedEvent();
} else {
Platform.runLater(super::fireValueChangedEvent);
}
}
}

View File

@@ -26,7 +26,7 @@ public class VaultStats {
private static final Logger LOG = LoggerFactory.getLogger(VaultStats.class);
private final AtomicReference<CryptoFileSystem> fs;
private final ObjectProperty<VaultState> state;
private final VaultState state;
private final ScheduledService<Optional<CryptoFileSystemStats>> updateService;
private final LongProperty bytesPerSecondRead = new SimpleLongProperty();
private final LongProperty bytesPerSecondWritten = new SimpleLongProperty();
@@ -41,7 +41,7 @@ public class VaultStats {
private final LongProperty filesWritten = new SimpleLongProperty();
@Inject
VaultStats(AtomicReference<CryptoFileSystem> fs, ObjectProperty<VaultState> state, ExecutorService executor) {
VaultStats(AtomicReference<CryptoFileSystem> fs, VaultState state, ExecutorService executor) {
this.fs = fs;
this.state = state;
this.updateService = new UpdateStatsService();
@@ -52,7 +52,7 @@ public class VaultStats {
}
private void vaultStateChanged(@SuppressWarnings("unused") Observable observable) {
if (VaultState.UNLOCKED == state.get()) {
if (VaultState.Value.UNLOCKED == state.get()) {
assert fs.get() != null;
LOG.debug("start recording stats");
Platform.runLater(() -> updateService.restart());

View File

@@ -163,8 +163,6 @@ public class VaultService {
private final Vault vault;
private final boolean forced;
private volatile long stamp;
/**
* @param vault The vault to lock
* @param forced Whether to attempt a forced lock
@@ -178,32 +176,28 @@ public class VaultService {
@Override
protected Vault call() throws Volume.VolumeException {
this.stamp = vault.lockVaultState();
vault.lock(forced);
return vault;
}
@Override
protected void scheduled() {
vault.setState(VaultState.PROCESSING, stamp);
vault.stateProperty().transition(VaultState.Value.UNLOCKED, VaultState.Value.PROCESSING);
}
@Override
protected void succeeded() {
vault.setState(VaultState.LOCKED, stamp);
vault.unlockVaultState(stamp);
vault.stateProperty().transition(VaultState.Value.PROCESSING, VaultState.Value.LOCKED);
}
@Override
protected void failed() {
vault.setState(VaultState.UNLOCKED, stamp);
vault.unlockVaultState(stamp);
vault.stateProperty().transition(VaultState.Value.PROCESSING, VaultState.Value.UNLOCKED);
}
@Override
protected void cancelled() {
vault.setState(VaultState.UNLOCKED, stamp);
vault.unlockVaultState(stamp);
vault.stateProperty().transition(VaultState.Value.PROCESSING, VaultState.Value.UNLOCKED);
}
}

View File

@@ -24,11 +24,13 @@ import java.util.Set;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicBoolean;
import static org.cryptomator.common.vaults.VaultState.Value.*;
@Singleton
public class AppLifecycleListener {
private static final Logger LOG = LoggerFactory.getLogger(AppLifecycleListener.class);
public static final Set<VaultState> STATES_ALLOWING_TERMINATION = EnumSet.of(VaultState.LOCKED, VaultState.NEEDS_MIGRATION, VaultState.MISSING, VaultState.ERROR);
public static final Set<VaultState.Value> STATES_ALLOWING_TERMINATION = EnumSet.of(LOCKED, NEEDS_MIGRATION, MISSING, ERROR);
private final FxApplicationStarter fxApplicationStarter;
private final CountDownLatch shutdownLatch;

View File

@@ -36,8 +36,6 @@ public class LockWorkflow extends Task<Void> {
private final Lazy<Scene> lockForcedScene;
private final Lazy<Scene> lockFailedScene;
private volatile long stamp;
@Inject
public LockWorkflow(@LockWindow Stage lockWindow, @LockWindow Vault vault, UserInteractionLock<LockModule.ForceLockDecision> forceLockDecisionLock, @FxmlScene(FxmlFile.LOCK_FORCED) Lazy<Scene> lockForcedScene, @FxmlScene(FxmlFile.LOCK_FAILED) Lazy<Scene> lockFailedScene) {
this.lockWindow = lockWindow;
@@ -49,7 +47,6 @@ public class LockWorkflow extends Task<Void> {
@Override
protected Void call() throws Volume.VolumeException, InterruptedException {
this.stamp = vault.lockVaultState();
try {
vault.lock(false);
} catch (Volume.VolumeException e) {
@@ -82,21 +79,19 @@ public class LockWorkflow extends Task<Void> {
@Override
protected void scheduled() {
vault.setState(VaultState.PROCESSING, stamp);
vault.stateProperty().transition(VaultState.Value.UNLOCKED, VaultState.Value.PROCESSING);
}
@Override
protected void succeeded() {
LOG.info("Lock of {} succeeded.", vault.getDisplayName());
vault.setState(VaultState.LOCKED, stamp);
vault.unlockVaultState(stamp);
vault.stateProperty().transition(VaultState.Value.PROCESSING, VaultState.Value.LOCKED);
}
@Override
protected void failed() {
LOG.warn("Failed to lock {}.", vault.getDisplayName());
vault.setState(VaultState.UNLOCKED, stamp);
vault.unlockVaultState(stamp);
vault.stateProperty().transition(VaultState.Value.PROCESSING, VaultState.Value.UNLOCKED);
lockWindow.setScene(lockFailedScene.get());
lockWindow.show();
}
@@ -104,8 +99,7 @@ public class LockWorkflow extends Task<Void> {
@Override
protected void cancelled() {
LOG.debug("Lock of {} canceled.", vault.getDisplayName());
vault.setState(VaultState.UNLOCKED, stamp);
vault.unlockVaultState(stamp);
vault.stateProperty().transition(VaultState.Value.PROCESSING, VaultState.Value.UNLOCKED);
}
}

View File

@@ -32,7 +32,8 @@ public class VaultDetailController implements FxController {
this.anyVaultSelected = vault.isNotNull();
}
private FontAwesome5Icon getGlyphForVaultState(VaultState state) {
// TODO deduplicate w/ VaultListCellController
private FontAwesome5Icon getGlyphForVaultState(VaultState.Value state) {
if (state != null) {
return switch (state) {
case LOCKED -> FontAwesome5Icon.LOCK;

View File

@@ -24,7 +24,8 @@ public class VaultListCellController implements FxController {
.map(this::getGlyphForVaultState);
}
private FontAwesome5Icon getGlyphForVaultState(VaultState state) {
// TODO deduplicate w/ VaultDetailController
private FontAwesome5Icon getGlyphForVaultState(VaultState.Value state) {
if (state != null) {
return switch (state) {
case LOCKED -> FontAwesome5Icon.LOCK;

View File

@@ -14,19 +14,13 @@ import org.cryptomator.ui.vaultoptions.VaultOptionsComponent;
import javax.inject.Inject;
import javafx.beans.binding.Binding;
import javafx.beans.binding.Bindings;
import javafx.beans.property.ObjectProperty;
import javafx.fxml.FXML;
import javafx.stage.Stage;
import java.util.Arrays;
import java.util.EnumSet;
import java.util.Optional;
import static org.cryptomator.common.vaults.VaultState.ERROR;
import static org.cryptomator.common.vaults.VaultState.LOCKED;
import static org.cryptomator.common.vaults.VaultState.MISSING;
import static org.cryptomator.common.vaults.VaultState.NEEDS_MIGRATION;
import static org.cryptomator.common.vaults.VaultState.UNLOCKED;
import static org.cryptomator.common.vaults.VaultState.Value.*;
@MainWindowScoped
public class VaultListContextMenuController implements FxController {
@@ -37,7 +31,7 @@ public class VaultListContextMenuController implements FxController {
private final KeychainManager keychain;
private final RemoveVaultComponent.Builder removeVault;
private final VaultOptionsComponent.Builder vaultOptionsWindow;
private final OptionalBinding<VaultState> selectedVaultState;
private final OptionalBinding<VaultState.Value> selectedVaultState;
private final Binding<Boolean> selectedVaultPassphraseStored;
private final Binding<Boolean> selectedVaultRemovable;
private final Binding<Boolean> selectedVaultUnlockable;
@@ -57,7 +51,6 @@ public class VaultListContextMenuController implements FxController {
this.selectedVaultRemovable = selectedVaultState.map(EnumSet.of(LOCKED, MISSING, ERROR, NEEDS_MIGRATION)::contains).orElse(false);
this.selectedVaultUnlockable = selectedVaultState.map(LOCKED::equals).orElse(false);
this.selectedVaultLockable = selectedVaultState.map(UNLOCKED::equals).orElse(false);
}
private boolean isPasswordStored(Vault vault) {

View File

@@ -26,6 +26,7 @@ import javax.inject.Named;
import javafx.application.Platform;
import javafx.beans.binding.Bindings;
import javafx.beans.binding.ObjectBinding;
import javafx.beans.binding.ObjectExpression;
import javafx.beans.property.BooleanProperty;
import javafx.beans.property.DoubleProperty;
import javafx.beans.property.ObjectProperty;
@@ -89,7 +90,10 @@ public class MigrationRunController implements FxController {
if (keychain.isSupported()) {
loadStoredPassword();
}
migrationButtonDisabled.bind(vault.stateProperty().isNotEqualTo(VaultState.NEEDS_MIGRATION).or(passwordField.textProperty().isEmpty()));
migrationButtonDisabled.bind(ObjectExpression.objectExpression(vault.stateProperty())
.isNotEqualTo(VaultState.Value.NEEDS_MIGRATION)
.or(passwordField.textProperty().isEmpty()));
}
@FXML
@@ -101,8 +105,7 @@ public class MigrationRunController implements FxController {
public void migrate() {
LOG.info("Migrating vault {}", vault.getPath());
CharSequence password = passwordField.getCharacters();
long stamp = vault.lockVaultState();
vault.setState(VaultState.PROCESSING, stamp);
vault.stateProperty().transition(VaultState.Value.NEEDS_MIGRATION, VaultState.Value.PROCESSING);
passwordField.setDisable(true);
ScheduledFuture<?> progressSyncTask = scheduler.scheduleAtFixedRate(() -> {
Platform.runLater(() -> {
@@ -116,10 +119,10 @@ public class MigrationRunController implements FxController {
}).onSuccess(needsAnotherMigration -> {
if (needsAnotherMigration) {
LOG.info("Migration of '{}' succeeded, but another migration is required.", vault.getDisplayName());
vault.setState(VaultState.NEEDS_MIGRATION, stamp);
vault.stateProperty().transition(VaultState.Value.PROCESSING, VaultState.Value.NEEDS_MIGRATION);
} else {
LOG.info("Migration of '{}' succeeded.", vault.getDisplayName());
vault.setState(VaultState.LOCKED, stamp);
vault.stateProperty().transition(VaultState.Value.PROCESSING, VaultState.Value.LOCKED);
passwordField.wipe();
window.setScene(successScene.get());
}
@@ -128,23 +131,22 @@ public class MigrationRunController implements FxController {
passwordField.setDisable(false);
passwordField.selectAll();
passwordField.requestFocus();
vault.setState(VaultState.NEEDS_MIGRATION, stamp);
vault.stateProperty().transition(VaultState.Value.PROCESSING, VaultState.Value.NEEDS_MIGRATION);
}).onError(FileSystemCapabilityChecker.MissingCapabilityException.class, e -> {
LOG.error("Underlying file system not supported.", e);
vault.setState(VaultState.NEEDS_MIGRATION, stamp);
vault.stateProperty().transition(VaultState.Value.PROCESSING, VaultState.Value.NEEDS_MIGRATION);
missingCapability.set(e.getMissingCapability());
window.setScene(capabilityErrorScene.get());
}).onError(FileNameTooLongException.class, e -> {
LOG.error("Migration failed because the underlying file system does not support long filenames.", e);
vault.setState(VaultState.NEEDS_MIGRATION, stamp);
vault.stateProperty().transition(VaultState.Value.PROCESSING, VaultState.Value.NEEDS_MIGRATION);
errorComponent.cause(e).window(window).returnToScene(startScene.get()).build().showErrorScene();
window.setScene(impossibleScene.get());
}).onError(Exception.class, e -> { // including RuntimeExceptions
LOG.error("Migration failed for technical reasons.", e);
vault.setState(VaultState.NEEDS_MIGRATION, stamp);
vault.stateProperty().transition(VaultState.Value.PROCESSING, VaultState.Value.NEEDS_MIGRATION);
errorComponent.cause(e).window(window).returnToScene(startScene.get()).build().showErrorScene();
}).andFinally(() -> {
vault.unlockVaultState(stamp);
passwordField.setDisable(false);
progressSyncTask.cancel(true);
}).runOnce(executor);

View File

@@ -43,8 +43,8 @@ abstract class VaultStatisticsModule {
var weakStage = new WeakReference<>(stage);
vault.stateProperty().addListener(new ChangeListener<>() {
@Override
public void changed(ObservableValue<? extends VaultState> observable, VaultState oldValue, VaultState newValue) {
if (newValue != VaultState.UNLOCKED) {
public void changed(ObservableValue<? extends VaultState.Value> observable, VaultState.Value oldValue, VaultState.Value newValue) {
if (newValue != VaultState.Value.UNLOCKED) {
Stage stage = weakStage.get();
if (stage != null) {
stage.hide();

View File

@@ -59,8 +59,6 @@ public class UnlockWorkflow extends Task<Boolean> {
private final Lazy<Scene> invalidMountPointScene;
private final ErrorComponent.Builder errorComponent;
private volatile long stamp;
@Inject
UnlockWorkflow(@UnlockWindow Stage window, @UnlockWindow Vault vault, VaultService vaultService, AtomicReference<char[]> password, @Named("savePassword") AtomicBoolean savePassword, @Named("savedPassword") Optional<char[]> savedPassword, UserInteractionLock<PasswordEntry> passwordEntryLock, KeychainManager keychain, @FxmlScene(FxmlFile.UNLOCK) Lazy<Scene> unlockScene, @FxmlScene(FxmlFile.UNLOCK_SUCCESS) Lazy<Scene> successScene, @FxmlScene(FxmlFile.UNLOCK_INVALID_MOUNT_POINT) Lazy<Scene> invalidMountPointScene, ErrorComponent.Builder errorComponent) {
this.window = window;
@@ -89,7 +87,6 @@ public class UnlockWorkflow extends Task<Boolean> {
@Override
protected Boolean call() throws InterruptedException, IOException, VolumeException, InvalidMountPointException {
try {
this.stamp = vault.lockVaultState();
if (attemptUnlock()) {
handleSuccess();
return true;
@@ -176,15 +173,12 @@ public class UnlockWorkflow extends Task<Boolean> {
LOG.error("Unlock failed. Mountpoint doesn't exist (needs to be a folder): {}", cause.getMessage());
}
showInvalidMountPointScene();
return;
} else if (cause instanceof FileAlreadyExistsException) {
LOG.error("Unlock failed. Mountpoint already exists: {}", cause.getMessage());
showInvalidMountPointScene();
return;
} else if (cause instanceof DirectoryNotEmptyException) {
LOG.error("Unlock failed. Mountpoint not an empty directory: {}", cause.getMessage());
showInvalidMountPointScene();
return;
} else {
handleGenericError(impExc);
}
@@ -210,25 +204,22 @@ public class UnlockWorkflow extends Task<Boolean> {
@Override
protected void scheduled() {
vault.setState(VaultState.PROCESSING, stamp);
vault.stateProperty().transition(VaultState.Value.LOCKED, VaultState.Value.PROCESSING);
}
@Override
protected void succeeded() {
vault.setState(VaultState.UNLOCKED, stamp);
vault.unlockVaultState(stamp);
vault.stateProperty().transition(VaultState.Value.PROCESSING, VaultState.Value.UNLOCKED);
}
@Override
protected void failed() {
vault.setState(VaultState.LOCKED, stamp);
vault.unlockVaultState(stamp);
vault.stateProperty().transition(VaultState.Value.PROCESSING, VaultState.Value.LOCKED);
}
@Override
protected void cancelled() {
vault.setState(VaultState.LOCKED, stamp);
vault.unlockVaultState(stamp);
vault.stateProperty().transition(VaultState.Value.PROCESSING, VaultState.Value.LOCKED);
}
}