mirror of
https://github.com/cryptomator/cryptomator.git
synced 2026-05-20 11:41:26 +00:00
Show generic error screen in case of exceptions during migration (references #1056)
This commit is contained in:
@@ -12,6 +12,7 @@ public enum FxmlFile {
|
||||
CHANGEPASSWORD("/fxml/changepassword.fxml"), //
|
||||
FORGET_PASSWORD("/fxml/forget_password.fxml"), //
|
||||
MAIN_WINDOW("/fxml/main_window.fxml"), //
|
||||
MIGRATION_GENERIC_ERROR("/fxml/migration_generic_error.fxml"), //
|
||||
MIGRATION_RUN("/fxml/migration_run.fxml"), //
|
||||
MIGRATION_START("/fxml/migration_start.fxml"), //
|
||||
MIGRATION_SUCCESS("/fxml/migration_success.fxml"), //
|
||||
|
||||
@@ -12,7 +12,7 @@ public class StackTraceController implements FxController {
|
||||
this.stackTrace = provideStackTrace(cause);
|
||||
}
|
||||
|
||||
static String provideStackTrace(Throwable cause) {
|
||||
private static String provideStackTrace(Throwable cause) {
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
cause.printStackTrace(new PrintStream(baos));
|
||||
return baos.toString(StandardCharsets.UTF_8);
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
package org.cryptomator.ui.migration;
|
||||
|
||||
import dagger.Lazy;
|
||||
import javafx.fxml.FXML;
|
||||
import javafx.scene.Scene;
|
||||
import javafx.stage.Stage;
|
||||
import org.cryptomator.ui.common.FxController;
|
||||
import org.cryptomator.ui.common.FxmlFile;
|
||||
import org.cryptomator.ui.common.FxmlScene;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
@MigrationScoped
|
||||
public class MigrationGenericErrorController implements FxController {
|
||||
|
||||
private final Stage window;
|
||||
private final Lazy<Scene> startScene;
|
||||
|
||||
@Inject
|
||||
MigrationGenericErrorController(@MigrationWindow Stage window, @FxmlScene(FxmlFile.MIGRATION_START) Lazy<Scene> startScene) {
|
||||
this.window = window;
|
||||
this.startScene = startScene;
|
||||
}
|
||||
|
||||
@FXML
|
||||
public void back() {
|
||||
window.setScene(startScene.get());
|
||||
}
|
||||
}
|
||||
@@ -4,6 +4,8 @@ import dagger.Binds;
|
||||
import dagger.Module;
|
||||
import dagger.Provides;
|
||||
import dagger.multibindings.IntoMap;
|
||||
import javafx.beans.property.ObjectProperty;
|
||||
import javafx.beans.property.SimpleObjectProperty;
|
||||
import javafx.scene.Scene;
|
||||
import javafx.scene.image.Image;
|
||||
import javafx.stage.Modality;
|
||||
@@ -14,6 +16,7 @@ import org.cryptomator.ui.common.FxController;
|
||||
import org.cryptomator.ui.common.FxControllerKey;
|
||||
import org.cryptomator.ui.common.FxmlFile;
|
||||
import org.cryptomator.ui.common.FxmlScene;
|
||||
import org.cryptomator.ui.common.StackTraceController;
|
||||
import org.cryptomator.ui.mainwindow.MainWindow;
|
||||
|
||||
import javax.inject.Named;
|
||||
@@ -45,6 +48,13 @@ abstract class MigrationModule {
|
||||
return stage;
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Named("genericErrorCause")
|
||||
@MigrationScoped
|
||||
static ObjectProperty<Throwable> provideGenericErrorCause() {
|
||||
return new SimpleObjectProperty<>();
|
||||
}
|
||||
|
||||
@Provides
|
||||
@FxmlScene(FxmlFile.MIGRATION_START)
|
||||
@MigrationScoped
|
||||
@@ -66,6 +76,13 @@ abstract class MigrationModule {
|
||||
return fxmlLoaders.createScene("/fxml/migration_success.fxml");
|
||||
}
|
||||
|
||||
@Provides
|
||||
@FxmlScene(FxmlFile.MIGRATION_GENERIC_ERROR)
|
||||
@MigrationScoped
|
||||
static Scene provideMigrationGenericErrorScene(@MigrationWindow FXMLLoaderFactory fxmlLoaders) {
|
||||
return fxmlLoaders.createScene("/fxml/migration_generic_error.fxml");
|
||||
}
|
||||
|
||||
// ------------------
|
||||
|
||||
@Binds
|
||||
@@ -83,4 +100,16 @@ abstract class MigrationModule {
|
||||
@FxControllerKey(MigrationSuccessController.class)
|
||||
abstract FxController bindMigrationSuccessController(MigrationSuccessController controller);
|
||||
|
||||
@Binds
|
||||
@IntoMap
|
||||
@FxControllerKey(MigrationGenericErrorController.class)
|
||||
abstract FxController bindMigrationGenericErrorController(MigrationGenericErrorController controller);
|
||||
|
||||
@Provides
|
||||
@IntoMap
|
||||
@FxControllerKey(StackTraceController.class)
|
||||
static FxController provideStackTraceController(@Named("genericErrorCause") ObjectProperty<Throwable> errorCause) {
|
||||
return new StackTraceController(errorCause.get());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -4,10 +4,12 @@ import dagger.Lazy;
|
||||
import javafx.animation.KeyFrame;
|
||||
import javafx.animation.KeyValue;
|
||||
import javafx.animation.Timeline;
|
||||
import javafx.application.Platform;
|
||||
import javafx.beans.binding.Bindings;
|
||||
import javafx.beans.binding.ObjectBinding;
|
||||
import javafx.beans.property.BooleanProperty;
|
||||
import javafx.beans.property.DoubleProperty;
|
||||
import javafx.beans.property.ObjectProperty;
|
||||
import javafx.beans.property.ReadOnlyDoubleProperty;
|
||||
import javafx.beans.property.SimpleBooleanProperty;
|
||||
import javafx.beans.property.SimpleDoubleProperty;
|
||||
@@ -21,6 +23,7 @@ import javafx.stage.Stage;
|
||||
import javafx.util.Duration;
|
||||
import org.cryptomator.common.vaults.Vault;
|
||||
import org.cryptomator.common.vaults.VaultState;
|
||||
import org.cryptomator.cryptofs.common.FileSystemCapabilityChecker;
|
||||
import org.cryptomator.cryptofs.migration.Migrators;
|
||||
import org.cryptomator.cryptofs.migration.api.MigrationProgressListener;
|
||||
import org.cryptomator.cryptofs.migration.api.NoApplicableMigratorException;
|
||||
@@ -36,44 +39,52 @@ import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Named;
|
||||
import java.util.Arrays;
|
||||
import java.util.Optional;
|
||||
import java.util.Timer;
|
||||
import java.util.TimerTask;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
import java.util.concurrent.ScheduledFuture;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
@MigrationScoped
|
||||
public class MigrationRunController implements FxController {
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(MigrationRunController.class);
|
||||
private static final String MASTERKEY_FILENAME = "masterkey.cryptomator"; // TODO: deduplicate constant declared in multiple classes
|
||||
private static final Duration MIGRATION_PROGRESS_UPDATE_INTERVAL = Duration.millis(25);
|
||||
private static final long MIGRATION_PROGRESS_UPDATE_MILLIS = 50;
|
||||
|
||||
private final Stage window;
|
||||
private final Vault vault;
|
||||
private final ExecutorService executor;
|
||||
private final ScheduledExecutorService scheduler;
|
||||
private final Optional<KeychainAccess> keychainAccess;
|
||||
private final ObjectProperty<Throwable> errorCause;
|
||||
private final Lazy<Scene> startScene;
|
||||
private final Lazy<Scene> successScene;
|
||||
private final ObjectBinding<ContentDisplay> migrateButtonContentDisplay;
|
||||
private final Lazy<Scene> genericErrorScene;
|
||||
private final BooleanProperty migrationButtonDisabled;
|
||||
private final DoubleProperty migrationProgress;
|
||||
private final ScheduledService<Double> migrationProgressObservationService;
|
||||
private volatile double volatileMigrationProgress = -1.0;
|
||||
public NiceSecurePasswordField passwordField;
|
||||
|
||||
@Inject
|
||||
public MigrationRunController(@MigrationWindow Stage window, @MigrationWindow Vault vault, ExecutorService executor, Optional<KeychainAccess> keychainAccess, @FxmlScene(FxmlFile.MIGRATION_START) Lazy<Scene> startScene, @FxmlScene(FxmlFile.MIGRATION_SUCCESS) Lazy<Scene> successScene) {
|
||||
public MigrationRunController(@MigrationWindow Stage window, @MigrationWindow Vault vault, ExecutorService executor, ScheduledExecutorService scheduler, Optional<KeychainAccess> keychainAccess, @Named("genericErrorCause") ObjectProperty<Throwable> errorCause, @FxmlScene(FxmlFile.MIGRATION_START) Lazy<Scene> startScene, @FxmlScene(FxmlFile.MIGRATION_SUCCESS) Lazy<Scene> successScene, @FxmlScene(FxmlFile.MIGRATION_GENERIC_ERROR) Lazy<Scene> genericErrorScene) {
|
||||
this.window = window;
|
||||
this.vault = vault;
|
||||
this.executor = executor;
|
||||
this.scheduler = scheduler;
|
||||
this.keychainAccess = keychainAccess;
|
||||
this.errorCause = errorCause;
|
||||
this.startScene = startScene;
|
||||
this.successScene = successScene;
|
||||
this.migrateButtonContentDisplay = Bindings.createObjectBinding(this::getMigrateButtonContentDisplay, vault.stateProperty());
|
||||
this.genericErrorScene = genericErrorScene;
|
||||
this.migrationButtonDisabled = new SimpleBooleanProperty();
|
||||
this.migrationProgress = new SimpleDoubleProperty(volatileMigrationProgress);
|
||||
this.migrationProgressObservationService = new MigrationProgressObservationService();
|
||||
migrationProgressObservationService.setExecutor(executor);
|
||||
migrationProgressObservationService.setPeriod(MIGRATION_PROGRESS_UPDATE_INTERVAL);
|
||||
}
|
||||
|
||||
public void initialize() {
|
||||
@@ -93,7 +104,11 @@ public class MigrationRunController implements FxController {
|
||||
LOG.info("Migrating vault {}", vault.getPath());
|
||||
CharSequence password = passwordField.getCharacters();
|
||||
vault.setState(VaultState.PROCESSING);
|
||||
migrationProgressObservationService.start();
|
||||
ScheduledFuture<?> progressSyncTask = scheduler.scheduleAtFixedRate(() -> {
|
||||
Platform.runLater(() -> {
|
||||
migrationProgress.set(volatileMigrationProgress);
|
||||
});
|
||||
}, 0, MIGRATION_PROGRESS_UPDATE_MILLIS, TimeUnit.MILLISECONDS);
|
||||
Tasks.create(() -> {
|
||||
Migrators migrators = Migrators.get();
|
||||
migrators.migrate(vault.getPath(), MASTERKEY_FILENAME, password, this::migrationProgressChanged);
|
||||
@@ -118,11 +133,11 @@ public class MigrationRunController implements FxController {
|
||||
// TODO show specific error screen
|
||||
}).onError(Exception.class, e -> { // including RuntimeExceptions
|
||||
LOG.error("Migration failed for technical reasons.", e);
|
||||
vault.setState(VaultState.ERROR);
|
||||
// TODO show generic error screen
|
||||
vault.setState(VaultState.NEEDS_MIGRATION);
|
||||
errorCause.set(e);
|
||||
window.setScene(genericErrorScene.get());
|
||||
}).andFinally(() -> {
|
||||
migrationProgressObservationService.cancel();
|
||||
migrationProgressObservationService.reset();
|
||||
progressSyncTask.cancel(true);
|
||||
}).runOnce(executor);
|
||||
}
|
||||
|
||||
@@ -161,27 +176,6 @@ public class MigrationRunController implements FxController {
|
||||
}
|
||||
}
|
||||
|
||||
// Sets migrationProgress to volatileMigrationProgress at its configured interval
|
||||
private class MigrationProgressObservationService extends ScheduledService<Double> {
|
||||
|
||||
@Override
|
||||
protected Task<Double> createTask() {
|
||||
return new Task<>() {
|
||||
@Override
|
||||
protected Double call() {
|
||||
return volatileMigrationProgress;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void succeeded() {
|
||||
assert getValue() != null;
|
||||
migrationProgress.set(getValue());
|
||||
super.succeeded();
|
||||
}
|
||||
}
|
||||
|
||||
/* Animations */
|
||||
|
||||
private void shakeWindow() {
|
||||
|
||||
32
main/ui/src/main/resources/fxml/migration_generic_error.fxml
Normal file
32
main/ui/src/main/resources/fxml/migration_generic_error.fxml
Normal file
@@ -0,0 +1,32 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<?import javafx.geometry.Insets?>
|
||||
<?import javafx.scene.control.Button?>
|
||||
<?import javafx.scene.control.ButtonBar?>
|
||||
<?import javafx.scene.layout.VBox?>
|
||||
<?import javafx.scene.layout.Region?>
|
||||
<VBox xmlns="http://javafx.com/javafx"
|
||||
xmlns:fx="http://javafx.com/fxml"
|
||||
fx:controller="org.cryptomator.ui.migration.MigrationGenericErrorController"
|
||||
minWidth="400"
|
||||
maxWidth="400"
|
||||
minHeight="145"
|
||||
spacing="12">
|
||||
<padding>
|
||||
<Insets topRightBottomLeft="12"/>
|
||||
</padding>
|
||||
<children>
|
||||
<fx:include source="/fxml/stacktrace.fxml"/>
|
||||
|
||||
<Region VBox.vgrow="ALWAYS"/>
|
||||
|
||||
<VBox alignment="BOTTOM_CENTER" VBox.vgrow="ALWAYS">
|
||||
<ButtonBar buttonMinWidth="120" buttonOrder="B+U">
|
||||
<buttons>
|
||||
<Button text="%generic.button.back" ButtonBar.buttonData="BACK_PREVIOUS" cancelButton="true" onAction="#back"/>
|
||||
<Region ButtonBar.buttonData="OTHER"/>
|
||||
</buttons>
|
||||
</ButtonBar>
|
||||
</VBox>
|
||||
</children>
|
||||
</VBox>
|
||||
Reference in New Issue
Block a user