mirror of
https://github.com/cryptomator/cryptomator.git
synced 2026-05-20 03:31:27 +00:00
Show stack trace in UI for vaults in error state
This commit is contained in:
@@ -27,6 +27,7 @@ import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Named;
|
||||
import javax.inject.Provider;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.NoSuchFileException;
|
||||
@@ -51,6 +52,7 @@ public class Vault {
|
||||
private final StringBinding defaultMountFlags;
|
||||
private final AtomicReference<CryptoFileSystem> cryptoFileSystem;
|
||||
private final ObjectProperty<VaultState> state;
|
||||
private final ObjectProperty<Exception> lastKnownException;
|
||||
private final VaultStats stats;
|
||||
private final StringBinding displayableName;
|
||||
private final StringBinding displayablePath;
|
||||
@@ -59,18 +61,20 @@ public class Vault {
|
||||
private final BooleanBinding unlocked;
|
||||
private final BooleanBinding missing;
|
||||
private final BooleanBinding needsMigration;
|
||||
private final BooleanBinding unknownError;
|
||||
private final StringBinding accessPoint;
|
||||
private final BooleanBinding accessPointPresent;
|
||||
|
||||
private volatile Volume volume;
|
||||
|
||||
@Inject
|
||||
Vault(VaultSettings vaultSettings, Provider<Volume> volumeProvider, @DefaultMountFlags StringBinding defaultMountFlags, AtomicReference<CryptoFileSystem> cryptoFileSystem, ObjectProperty<VaultState> state, VaultStats stats) {
|
||||
Vault(VaultSettings vaultSettings, Provider<Volume> volumeProvider, @DefaultMountFlags StringBinding defaultMountFlags, AtomicReference<CryptoFileSystem> cryptoFileSystem, ObjectProperty<VaultState> state, @Named("lastKnownException") ObjectProperty<Exception> lastKnownException, VaultStats stats) {
|
||||
this.vaultSettings = vaultSettings;
|
||||
this.volumeProvider = volumeProvider;
|
||||
this.defaultMountFlags = defaultMountFlags;
|
||||
this.cryptoFileSystem = cryptoFileSystem;
|
||||
this.state = state;
|
||||
this.lastKnownException = lastKnownException;
|
||||
this.stats = stats;
|
||||
this.displayableName = Bindings.createStringBinding(this::getDisplayableName, vaultSettings.path());
|
||||
this.displayablePath = Bindings.createStringBinding(this::getDisplayablePath, vaultSettings.path());
|
||||
@@ -79,6 +83,7 @@ public class Vault {
|
||||
this.unlocked = Bindings.createBooleanBinding(this::isUnlocked, state);
|
||||
this.missing = Bindings.createBooleanBinding(this::isMissing, state);
|
||||
this.needsMigration = Bindings.createBooleanBinding(this::isNeedsMigration, state);
|
||||
this.unknownError = Bindings.createBooleanBinding(this::isUnknownError, state);
|
||||
this.accessPoint = Bindings.createStringBinding(this::getAccessPoint, state);
|
||||
this.accessPointPresent = this.accessPoint.isNotEmpty();
|
||||
}
|
||||
@@ -149,6 +154,18 @@ public class Vault {
|
||||
state.setValue(value);
|
||||
}
|
||||
|
||||
public ObjectProperty<Exception> lastKnownExceptionProperty() {
|
||||
return lastKnownException;
|
||||
}
|
||||
|
||||
public Exception getLastKnownException() {
|
||||
return lastKnownException.get();
|
||||
}
|
||||
|
||||
public void setLastKnownException(Exception e) {
|
||||
lastKnownException.setValue(e);
|
||||
}
|
||||
|
||||
public BooleanBinding lockedProperty() {
|
||||
return locked;
|
||||
}
|
||||
@@ -189,6 +206,14 @@ public class Vault {
|
||||
return state.get() == VaultState.NEEDS_MIGRATION;
|
||||
}
|
||||
|
||||
public BooleanBinding unknownErrorProperty() {
|
||||
return unknownError;
|
||||
}
|
||||
|
||||
public boolean isUnknownError() {
|
||||
return state.get() == VaultState.ERROR;
|
||||
}
|
||||
|
||||
public StringBinding displayableNameProperty() {
|
||||
return displayableName;
|
||||
}
|
||||
|
||||
@@ -10,6 +10,9 @@ import org.cryptomator.common.settings.VaultSettings;
|
||||
|
||||
import dagger.Subcomponent;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import javax.inject.Named;
|
||||
|
||||
@PerVault
|
||||
@Subcomponent(modules = {VaultModule.class})
|
||||
public interface VaultComponent {
|
||||
@@ -25,6 +28,9 @@ public interface VaultComponent {
|
||||
@BindsInstance
|
||||
Builder initialVaultState(VaultState vaultState);
|
||||
|
||||
@BindsInstance
|
||||
Builder initialErrorCause(@Nullable @Named("lastKnownException") Exception initialErrorCause);
|
||||
|
||||
VaultComponent build();
|
||||
}
|
||||
|
||||
|
||||
@@ -31,9 +31,9 @@ import static org.cryptomator.common.Constants.MASTERKEY_FILENAME;
|
||||
|
||||
@Singleton
|
||||
public class VaultListManager {
|
||||
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(VaultListManager.class);
|
||||
|
||||
|
||||
private final VaultComponent.Builder vaultComponentBuilder;
|
||||
private final ObservableList<Vault> vaultList;
|
||||
|
||||
@@ -41,7 +41,7 @@ public class VaultListManager {
|
||||
public VaultListManager(VaultComponent.Builder vaultComponentBuilder, Settings settings) {
|
||||
this.vaultComponentBuilder = vaultComponentBuilder;
|
||||
this.vaultList = FXCollections.observableArrayList(Vault::observables);
|
||||
|
||||
|
||||
addAll(settings.getDirectories());
|
||||
vaultList.addListener(new VaultListChangeListener(settings.getDirectories()));
|
||||
}
|
||||
@@ -65,12 +65,12 @@ public class VaultListManager {
|
||||
return newVault;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void addAll(Collection<VaultSettings> vaultSettings) {
|
||||
Collection<Vault> vaults = vaultSettings.stream().map(this::create).collect(Collectors.toList());
|
||||
vaultList.addAll(vaults);
|
||||
}
|
||||
|
||||
|
||||
private Optional<Vault> get(Path vaultPath) {
|
||||
return vaultList.stream().filter(v -> {
|
||||
try {
|
||||
@@ -82,23 +82,25 @@ public class VaultListManager {
|
||||
}
|
||||
|
||||
private Vault create(VaultSettings vaultSettings) {
|
||||
VaultState vaultState = determineVaultState(vaultSettings.path().get());
|
||||
VaultComponent comp = vaultComponentBuilder.vaultSettings(vaultSettings).initialVaultState(vaultState).build();
|
||||
return comp.vault();
|
||||
VaultComponent.Builder compBuilder = vaultComponentBuilder.vaultSettings(vaultSettings);
|
||||
try {
|
||||
VaultState 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.initialErrorCause(e);
|
||||
}
|
||||
return compBuilder.build().vault();
|
||||
}
|
||||
|
||||
public static VaultState determineVaultState(Path pathToVault) {
|
||||
try {
|
||||
if (!CryptoFileSystemProvider.containsVault(pathToVault, MASTERKEY_FILENAME)) {
|
||||
return VaultState.MISSING;
|
||||
} else if (Migrators.get().needsMigration(pathToVault, MASTERKEY_FILENAME)) {
|
||||
return VaultState.NEEDS_MIGRATION;
|
||||
} else {
|
||||
return VaultState.LOCKED;
|
||||
}
|
||||
} catch (IOException e) {
|
||||
LOG.warn("Could not determine vault state of " + pathToVault + " due to unexpected exception.", e);
|
||||
return VaultState.ERROR;
|
||||
public static VaultState determineVaultState(Path pathToVault) throws IOException {
|
||||
if (!CryptoFileSystemProvider.containsVault(pathToVault, MASTERKEY_FILENAME)) {
|
||||
return VaultState.MISSING;
|
||||
} else if (Migrators.get().needsMigration(pathToVault, MASTERKEY_FILENAME)) {
|
||||
return VaultState.NEEDS_MIGRATION;
|
||||
} else {
|
||||
return VaultState.LOCKED;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -23,6 +23,8 @@ import org.cryptomator.cryptofs.CryptoFileSystem;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import javax.inject.Named;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
@@ -46,6 +48,14 @@ public class VaultModule {
|
||||
return new SimpleObjectProperty<>(initialState);
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Named("lastKnownException")
|
||||
@PerVault
|
||||
public ObjectProperty<Exception> provideLastKnownException(@Named("lastKnownException") @Nullable Exception initialErrorCause) {
|
||||
return new SimpleObjectProperty<>(initialErrorCause);
|
||||
}
|
||||
|
||||
|
||||
@Provides
|
||||
public Volume provideVolume(Settings settings, WebDavVolume webDavVolume, FuseVolume fuseVolume, DokanyVolume dokanyVolume) {
|
||||
VolumeImpl preferredImpl = settings.preferredVolumeImpl().get();
|
||||
|
||||
@@ -21,10 +21,11 @@ abstract class ErrorModule {
|
||||
static FXMLLoaderFactory provideFxmlLoaderFactory(Map<Class<? extends FxController>, Provider<FxController>> factories, DefaultSceneFactory sceneFactory, ResourceBundle resourceBundle) {
|
||||
return new FXMLLoaderFactory(factories, sceneFactory, resourceBundle);
|
||||
}
|
||||
|
||||
|
||||
@Provides
|
||||
@Named("stackTrace")
|
||||
static String provideStackTrace(Throwable cause) {
|
||||
// TODO deduplicate VaultDetailUnknownErrorController.java
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
cause.printStackTrace(new PrintStream(baos));
|
||||
return baos.toString(StandardCharsets.UTF_8);
|
||||
|
||||
@@ -6,13 +6,9 @@ import dagger.Provides;
|
||||
import dagger.multibindings.IntoMap;
|
||||
import javafx.scene.Scene;
|
||||
import javafx.scene.image.Image;
|
||||
import javafx.scene.input.KeyCode;
|
||||
import javafx.scene.input.KeyCodeCombination;
|
||||
import javafx.scene.input.KeyCombination;
|
||||
import javafx.stage.Stage;
|
||||
import javafx.stage.StageStyle;
|
||||
import org.cryptomator.ui.addvaultwizard.AddVaultWizardComponent;
|
||||
import org.cryptomator.ui.common.DefaultSceneFactory;
|
||||
import org.cryptomator.ui.common.FXMLLoaderFactory;
|
||||
import org.cryptomator.ui.common.FxController;
|
||||
import org.cryptomator.ui.common.FxControllerKey;
|
||||
@@ -87,7 +83,7 @@ abstract class MainWindowModule {
|
||||
@IntoMap
|
||||
@FxControllerKey(VaultDetailController.class)
|
||||
abstract FxController bindVaultDetailController(VaultDetailController controller);
|
||||
|
||||
|
||||
@Binds
|
||||
@IntoMap
|
||||
@FxControllerKey(WelcomeController.class)
|
||||
@@ -102,7 +98,7 @@ abstract class MainWindowModule {
|
||||
@IntoMap
|
||||
@FxControllerKey(VaultDetailUnlockedController.class)
|
||||
abstract FxController bindVaultDetailUnlockedController(VaultDetailUnlockedController controller);
|
||||
|
||||
|
||||
@Binds
|
||||
@IntoMap
|
||||
@FxControllerKey(VaultDetailMissingVaultController.class)
|
||||
@@ -113,11 +109,15 @@ abstract class MainWindowModule {
|
||||
@FxControllerKey(VaultDetailNeedsMigrationController.class)
|
||||
abstract FxController bindVaultDetailNeedsMigrationController(VaultDetailNeedsMigrationController controller);
|
||||
|
||||
@Binds
|
||||
@IntoMap
|
||||
@FxControllerKey(VaultDetailUnknownErrorController.class)
|
||||
abstract FxController bindVaultDetailUnknownErrorController(VaultDetailUnknownErrorController controller);
|
||||
|
||||
@Binds
|
||||
@IntoMap
|
||||
@FxControllerKey(VaultListCellController.class)
|
||||
abstract FxController bindVaultListCellController(VaultListCellController controller);
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
package org.cryptomator.ui.mainwindow;
|
||||
|
||||
import javafx.beans.binding.Binding;
|
||||
import javafx.beans.property.ObjectProperty;
|
||||
import org.cryptomator.common.vaults.Vault;
|
||||
import org.cryptomator.ui.common.FxController;
|
||||
import org.fxmisc.easybind.EasyBind;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.PrintStream;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
@MainWindowScoped
|
||||
public class VaultDetailUnknownErrorController implements FxController {
|
||||
|
||||
private final Binding<String> stackTrace;
|
||||
|
||||
@Inject
|
||||
public VaultDetailUnknownErrorController(ObjectProperty<Vault> vault) {
|
||||
this.stackTrace = EasyBind.select(vault).selectObject(Vault::lastKnownExceptionProperty).map(this::provideStackTrace).orElse("");
|
||||
}
|
||||
|
||||
private String provideStackTrace(Throwable cause) {
|
||||
// TODO deduplicate ErrorModule.java
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
cause.printStackTrace(new PrintStream(baos));
|
||||
return baos.toString(StandardCharsets.UTF_8);
|
||||
}
|
||||
|
||||
/* Getter/Setter */
|
||||
|
||||
public Binding<String> stackTraceProperty() {
|
||||
return stackTrace;
|
||||
}
|
||||
|
||||
public String getStackTrace() {
|
||||
return stackTrace.getValue();
|
||||
}
|
||||
}
|
||||
@@ -18,6 +18,7 @@ import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import java.io.IOException;
|
||||
|
||||
@MainWindowScoped
|
||||
public class VaultListController implements FxController {
|
||||
@@ -68,8 +69,14 @@ public class VaultListController implements FxController {
|
||||
case LOCKED:
|
||||
case NEEDS_MIGRATION:
|
||||
case MISSING:
|
||||
VaultState determinedState = VaultListManager.determineVaultState(newValue.getPath());
|
||||
newValue.setState(determinedState);
|
||||
try {
|
||||
VaultState determinedState = VaultListManager.determineVaultState(newValue.getPath());
|
||||
newValue.setState(determinedState);
|
||||
} catch (IOException e) {
|
||||
LOG.warn("Failed to determine vault state for " + newValue.getPath(), e);
|
||||
newValue.setState(VaultState.ERROR);
|
||||
newValue.setLastKnownException(e);
|
||||
}
|
||||
break;
|
||||
case ERROR:
|
||||
case UNLOCKED:
|
||||
|
||||
@@ -52,5 +52,6 @@
|
||||
<fx:include VBox.vgrow="ALWAYS" source="vault_detail_unlocked.fxml" visible="${controller.vault.unlocked}" managed="${controller.vault.unlocked}"/>
|
||||
<fx:include VBox.vgrow="ALWAYS" source="vault_detail_missing.fxml" visible="${controller.vault.missing}" managed="${controller.vault.missing}"/>
|
||||
<fx:include VBox.vgrow="ALWAYS" source="vault_detail_needsmigration.fxml" visible="${controller.vault.needsMigration}" managed="${controller.vault.needsMigration}"/>
|
||||
<fx:include VBox.vgrow="ALWAYS" source="vault_detail_unknownerror.fxml" visible="${controller.vault.unknownError}" managed="${controller.vault.unknownError}"/>
|
||||
</children>
|
||||
</VBox>
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
<?import javafx.scene.control.Label?>
|
||||
<?import javafx.scene.control.TextArea?>
|
||||
<?import javafx.scene.layout.VBox?>
|
||||
<VBox xmlns="http://javafx.com/javafx"
|
||||
xmlns:fx="http://javafx.com/fxml"
|
||||
fx:controller="org.cryptomator.ui.mainwindow.VaultDetailUnknownErrorController"
|
||||
alignment="CENTER"
|
||||
spacing="24">
|
||||
<children>
|
||||
<Label text="%generic.error.title" wrapText="true"/>
|
||||
<Label text="%generic.error.instruction" wrapText="true"/>
|
||||
|
||||
<TextArea VBox.vgrow="ALWAYS" text="${controller.stackTrace}" prefRowCount="5" editable="false"/>
|
||||
</children>
|
||||
</VBox>
|
||||
Reference in New Issue
Block a user