Sfoglia il codice sorgente

Show generic error screen in case of exceptions during migration (references #1056)

Sebastian Stenzel 5 anni fa
parent
commit
f73c1889b7

+ 1 - 0
main/ui/src/main/java/org/cryptomator/ui/common/FxmlFile.java

@@ -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"), //

+ 1 - 1
main/ui/src/main/java/org/cryptomator/ui/common/StackTraceController.java

@@ -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);

+ 29 - 0
main/ui/src/main/java/org/cryptomator/ui/migration/MigrationGenericErrorController.java

@@ -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());
+	}
+}

+ 29 - 0
main/ui/src/main/java/org/cryptomator/ui/migration/MigrationModule.java

@@ -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());
+	}
+
 }

+ 26 - 32
main/ui/src/main/java/org/cryptomator/ui/migration/MigrationRunController.java

@@ -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 - 0
main/ui/src/main/resources/fxml/migration_generic_error.fxml

@@ -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>