Compare commits

..

1 Commits

Author SHA1 Message Date
JaniruTEC
9601263645 Added an additional layer of abstraction to error reports
Fixed var names
Added abstract base class for controllers
Added base interface for error components
Added base interface for error component builders
Generified scene providers
Made GenericError classes and InvalidMountPointException classes depend on those base interfaces/classes
2021-04-11 02:54:21 +02:00
13 changed files with 197 additions and 123 deletions

View File

@@ -0,0 +1,35 @@
package org.cryptomator.ui.error;
import org.cryptomator.ui.common.FxController;
import javax.annotation.Nullable;
import javafx.fxml.FXML;
import javafx.scene.Scene;
import javafx.stage.Stage;
public class AbstractErrorController implements FxController {
protected final Stage window;
protected final Scene returnScene;
public AbstractErrorController(@ErrorReport Stage window, @ErrorReport @Nullable Scene returnScene) {
this.returnScene = returnScene;
this.window = window;
}
@FXML
public void back() {
if (this.returnScene != null) {
this.window.setScene(this.returnScene);
}
}
@FXML
public void close() {
this.window.close();
}
public boolean isReturnScenePresent() {
return this.returnScene != null;
}
}

View File

@@ -0,0 +1,53 @@
package org.cryptomator.ui.error;
import dagger.BindsInstance;
import javax.annotation.Nullable;
import javax.inject.Named;
import javafx.application.Platform;
import javafx.scene.Scene;
import javafx.stage.Stage;
public interface ErrorComponentBase {
@ErrorReport
Stage window();
@Named("errorScene")
Scene errorScene();
@ErrorReport
Throwable cause();
@ErrorReport
@Nullable
Scene previousScene();
default void showErrorScene() {
if (Platform.isFxApplicationThread()) {
show();
} else {
Platform.runLater(this::show);
}
}
private void show() {
Stage stage = window();
stage.setScene(errorScene());
stage.show();
}
interface BuilderBase<B, C> {
@BindsInstance
B window(@ErrorReport Stage window);
@BindsInstance
B cause(@ErrorReport Throwable cause);
@BindsInstance
B returnToScene(@ErrorReport @Nullable Scene returnScene);
C build();
}
}

View File

@@ -1,15 +1,11 @@
package org.cryptomator.ui.error;
import dagger.Binds;
import dagger.Module;
import dagger.Provides;
import dagger.multibindings.IntoMap;
import org.cryptomator.ui.common.DefaultSceneFactory;
import org.cryptomator.ui.common.FxController;
import org.cryptomator.ui.common.FxControllerKey;
import org.cryptomator.ui.common.FxmlFile;
import org.cryptomator.ui.common.FxmlLoaderFactory;
import org.cryptomator.ui.common.FxmlScene;
import javax.inject.Named;
import javax.inject.Provider;
@@ -30,23 +26,16 @@ abstract class ErrorModule {
@Provides
@Named("stackTrace")
static String provideStackTrace(Throwable cause) {
static String provideStackTrace(@ErrorReport Throwable cause) {
// TODO deduplicate VaultDetailUnknownErrorController.java
ByteArrayOutputStream baos = new ByteArrayOutputStream();
cause.printStackTrace(new PrintStream(baos));
return baos.toString(StandardCharsets.UTF_8);
}
@Binds
@IntoMap
@FxControllerKey(GenericErrorController.class)
abstract FxController bindErrorController(GenericErrorController controller);
@Provides
@FxmlScene(FxmlFile.GENERIC_ERROR)
static Scene provideErrorScene(FxmlLoaderFactory fxmlLoaders) {
return fxmlLoaders.createScene(FxmlFile.GENERIC_ERROR);
@Named("errorScene")
static Scene provideErrorScene(FxmlLoaderFactory fxmlLoaders, @ErrorReport FxmlFile file) {
return fxmlLoaders.createScene(file);
}
}

View File

@@ -0,0 +1,14 @@
package org.cryptomator.ui.error;
import javax.inject.Qualifier;
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
@Qualifier
@Documented
@Retention(RUNTIME)
public @interface ErrorReport {
}

View File

@@ -1,51 +1,32 @@
package org.cryptomator.ui.error;
import dagger.BindsInstance;
import dagger.Binds;
import dagger.Module;
import dagger.Provides;
import dagger.Subcomponent;
import dagger.multibindings.IntoMap;
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 javax.annotation.Nullable;
import javafx.application.Platform;
import javafx.scene.Scene;
import javafx.stage.Stage;
@Subcomponent(modules = {ErrorModule.class})
public interface GenericErrorComponent {
Stage window();
@FxmlScene(FxmlFile.GENERIC_ERROR)
Scene scene();
default void showErrorScene() {
if (Platform.isFxApplicationThread()) {
show();
} else {
Platform.runLater(this::show);
}
}
private void show() {
Stage stage = window();
stage.setScene(scene());
stage.show();
}
@Subcomponent(modules = {ErrorModule.class, GenericErrorComponent.GenericErrorModule.class})
public interface GenericErrorComponent extends ErrorComponentBase {
@Subcomponent.Builder
interface Builder {
interface Builder extends BuilderBase<Builder, GenericErrorComponent> { /* Handled by base interface */ }
@BindsInstance
Builder cause(Throwable cause);
@Module
abstract class GenericErrorModule {
@BindsInstance
Builder window(Stage window);
@BindsInstance
Builder returnToScene(@Nullable Scene previousScene);
GenericErrorComponent build();
@Provides
@ErrorReport
static FxmlFile provideFxmlFile() {
return FxmlFile.GENERIC_ERROR;
}
@Binds
@IntoMap
@FxControllerKey(GenericErrorController.class)
abstract FxController bindController(GenericErrorController controller);
}
}
}

View File

@@ -1,46 +1,24 @@
package org.cryptomator.ui.error;
import org.cryptomator.ui.common.FxController;
import javax.annotation.Nullable;
import javax.inject.Inject;
import javax.inject.Named;
import javafx.fxml.FXML;
import javafx.scene.Scene;
import javafx.stage.Stage;
public class GenericErrorController implements FxController {
public class GenericErrorController extends AbstractErrorController {
private final String stackTrace;
private final Scene previousScene;
private final Stage window;
@Inject
GenericErrorController(@Named("stackTrace") String stackTrace, @Nullable Scene previousScene, Stage window) {
GenericErrorController(@ErrorReport Stage window, @ErrorReport @Nullable Scene previousScene, @Named("stackTrace") String stackTrace) {
super(window, previousScene);
this.stackTrace = stackTrace;
this.previousScene = previousScene;
this.window = window;
}
@FXML
public void back() {
if (previousScene != null) {
window.setScene(previousScene);
}
}
@FXML
public void close() {
window.close();
}
/* Getter/Setter */
public boolean isPreviousScenePresent() {
return previousScene != null;
}
public String getStackTrace() {
return stackTrace;
return this.stackTrace;
}
}

View File

@@ -0,0 +1,42 @@
package org.cryptomator.ui.error;
import dagger.Binds;
import dagger.BindsInstance;
import dagger.Module;
import dagger.Provides;
import dagger.Subcomponent;
import dagger.multibindings.IntoMap;
import org.cryptomator.common.vaults.Vault;
import org.cryptomator.ui.common.FxController;
import org.cryptomator.ui.common.FxControllerKey;
import org.cryptomator.ui.common.FxmlFile;
import org.cryptomator.ui.unlock.UnlockInvalidMountPointController;
import org.cryptomator.ui.unlock.UnlockScoped;
@Subcomponent(modules = {ErrorModule.class, InvalidMountPointExceptionComponent.InvalidMountPointExceptionModule.class})
@UnlockScoped
public interface InvalidMountPointExceptionComponent extends ErrorComponentBase {
@Subcomponent.Builder
interface Builder extends BuilderBase<Builder, InvalidMountPointExceptionComponent> {
@BindsInstance
Builder vault(@ErrorReport Vault vault);
}
@Module
abstract class InvalidMountPointExceptionModule {
@Provides
@ErrorReport
static FxmlFile provideFxmlFile() {
return FxmlFile.UNLOCK_INVALID_MOUNT_POINT;
}
@Binds
@IntoMap
@FxControllerKey(UnlockInvalidMountPointController.class)
abstract FxController bindController(UnlockInvalidMountPointController controller);
}
}

View File

@@ -9,8 +9,9 @@ import dagger.Binds;
import dagger.Module;
import dagger.Provides;
import org.apache.commons.lang3.SystemUtils;
import org.cryptomator.ui.error.GenericErrorComponent;
import org.cryptomator.ui.common.StageFactory;
import org.cryptomator.ui.error.GenericErrorComponent;
import org.cryptomator.ui.error.InvalidMountPointExceptionComponent;
import org.cryptomator.ui.lock.LockComponent;
import org.cryptomator.ui.mainwindow.MainWindowComponent;
import org.cryptomator.ui.preferences.PreferencesComponent;
@@ -26,8 +27,8 @@ import java.io.UncheckedIOException;
import java.util.Collections;
import java.util.List;
@Module(includes = {UpdateCheckerModule.class}, subcomponents = {MainWindowComponent.class, PreferencesComponent.class, UnlockComponent.class, LockComponent.class, QuitComponent.class, GenericErrorComponent.class})
abstract class FxApplicationModule {
@Module(includes = {UpdateCheckerModule.class}, subcomponents = {MainWindowComponent.class, PreferencesComponent.class, UnlockComponent.class, LockComponent.class, QuitComponent.class, GenericErrorComponent.class, InvalidMountPointExceptionComponent.class})
public abstract class FxApplicationModule {
@Provides
@Named("windowIcons")

View File

@@ -1,49 +1,38 @@
package org.cryptomator.ui.unlock;
import dagger.Lazy;
import org.cryptomator.common.vaults.MountPointRequirement;
import org.cryptomator.common.vaults.Vault;
import org.cryptomator.ui.common.FxController;
import org.cryptomator.ui.common.FxmlFile;
import org.cryptomator.ui.common.FxmlScene;
import org.cryptomator.ui.error.AbstractErrorController;
import org.cryptomator.ui.error.ErrorReport;
import javax.annotation.Nullable;
import javax.inject.Inject;
import javafx.fxml.FXML;
import javafx.scene.Scene;
import javafx.stage.Stage;
//At the current point in time only the CustomMountPointChooser may cause this window to be shown.
@UnlockScoped
public class UnlockInvalidMountPointController implements FxController {
public class UnlockInvalidMountPointController extends AbstractErrorController {
private final Stage window;
private final Lazy<Scene> unlockScene;
private final Vault vault;
@Inject
UnlockInvalidMountPointController(@UnlockWindow Stage window, @FxmlScene(FxmlFile.UNLOCK) Lazy<Scene> unlockScene, @UnlockWindow Vault vault) {
this.window = window;
this.unlockScene = unlockScene;
UnlockInvalidMountPointController(@ErrorReport Stage window, @ErrorReport @Nullable Scene previousScene, @ErrorReport Vault vault) {
super(window, previousScene);
this.vault = vault;
}
@FXML
public void back() {
window.setScene(unlockScene.get());
}
/* Getter/Setter */
public String getMountPoint() {
return vault.getVaultSettings().getCustomMountPath().orElse("AUTO");
return this.vault.getVaultSettings().getCustomMountPath().orElse("AUTO");
}
public boolean getMustExist() {
MountPointRequirement requirement = vault.getVolume().orElseThrow(() -> new IllegalStateException("Invalid Mountpoint without a Volume?!")).getMountPointRequirement();
MountPointRequirement requirement = this.vault.getVolume().orElseThrow(() -> new IllegalStateException("Invalid Mountpoint without a Volume?!")).getMountPointRequirement();
assert requirement != MountPointRequirement.NONE; //An invalid MountPoint with no required MountPoint doesn't seem sensible
assert requirement != MountPointRequirement.PARENT_OPT_MOUNT_POINT; //Not implemented anywhere (yet)
return requirement == MountPointRequirement.EMPTY_MOUNT_POINT;
}
}

View File

@@ -130,10 +130,4 @@ abstract class UnlockModule {
@IntoMap
@FxControllerKey(UnlockSuccessController.class)
abstract FxController bindUnlockSuccessController(UnlockSuccessController controller);
@Binds
@IntoMap
@FxControllerKey(UnlockInvalidMountPointController.class)
abstract FxController bindUnlockInvalidMountPointController(UnlockInvalidMountPointController controller);
}

View File

@@ -8,6 +8,6 @@ import java.lang.annotation.RetentionPolicy;
@Scope
@Documented
@Retention(RetentionPolicy.RUNTIME)
@interface UnlockScoped {
public @interface UnlockScoped {
}

View File

@@ -10,11 +10,12 @@ import org.cryptomator.common.vaults.Volume.VolumeException;
import org.cryptomator.cryptolib.api.InvalidPassphraseException;
import org.cryptomator.integrations.keychain.KeychainAccessException;
import org.cryptomator.ui.common.Animations;
import org.cryptomator.ui.error.GenericErrorComponent;
import org.cryptomator.ui.common.FxmlFile;
import org.cryptomator.ui.common.FxmlScene;
import org.cryptomator.ui.common.UserInteractionLock;
import org.cryptomator.ui.common.VaultService;
import org.cryptomator.ui.error.GenericErrorComponent;
import org.cryptomator.ui.error.InvalidMountPointExceptionComponent;
import org.cryptomator.ui.unlock.UnlockModule.PasswordEntry;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -56,11 +57,11 @@ public class UnlockWorkflow extends Task<Boolean> {
private final KeychainManager keychain;
private final Lazy<Scene> unlockScene;
private final Lazy<Scene> successScene;
private final Lazy<Scene> invalidMountPointScene;
private final GenericErrorComponent.Builder genericErrorBuilder;
private final InvalidMountPointExceptionComponent.Builder invalidMountPointExceptionBuilder;
@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, GenericErrorComponent.Builder genericErrorBuilder) {
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, GenericErrorComponent.Builder genericErrorBuilder, InvalidMountPointExceptionComponent.Builder invalidMountPointExceptionBuilder) {
this.window = window;
this.vault = vault;
this.vaultService = vaultService;
@@ -71,8 +72,8 @@ public class UnlockWorkflow extends Task<Boolean> {
this.keychain = keychain;
this.unlockScene = unlockScene;
this.successScene = successScene;
this.invalidMountPointScene = invalidMountPointScene;
this.genericErrorBuilder = genericErrorBuilder;
this.invalidMountPointExceptionBuilder = invalidMountPointExceptionBuilder;
setOnFailed(event -> {
Throwable throwable = event.getSource().getException();
@@ -172,26 +173,23 @@ public class UnlockWorkflow extends Task<Boolean> {
} else {
LOG.error("Unlock failed. Mountpoint doesn't exist (needs to be a folder): {}", cause.getMessage());
}
showInvalidMountPointScene();
showInvalidMountPointScene(cause);
return;
} else if (cause instanceof FileAlreadyExistsException) {
LOG.error("Unlock failed. Mountpoint already exists: {}", cause.getMessage());
showInvalidMountPointScene();
showInvalidMountPointScene(cause);
return;
} else if (cause instanceof DirectoryNotEmptyException) {
LOG.error("Unlock failed. Mountpoint not an empty directory: {}", cause.getMessage());
showInvalidMountPointScene();
showInvalidMountPointScene(cause);
return;
} else {
handleGenericError(impExc);
}
}
private void showInvalidMountPointScene() {
Platform.runLater(() -> {
window.setScene(invalidMountPointScene.get());
window.show();
});
private void showInvalidMountPointScene(Throwable e) {
invalidMountPointExceptionBuilder.cause(e).window(window).returnToScene(window.getScene()).vault(vault).build().showErrorScene();
}
private void handleGenericError(Throwable e) {

View File

@@ -36,7 +36,7 @@
<ButtonBar buttonMinWidth="120" buttonOrder="B+C">
<buttons>
<Button text="%generic.button.back" ButtonBar.buttonData="BACK_PREVIOUS" onAction="#back" visible="${controller.previousScenePresent}"/>
<Button text="%generic.button.back" ButtonBar.buttonData="BACK_PREVIOUS" onAction="#back" visible="${controller.returnScenePresent}"/>
<Button text="%generic.button.cancel" ButtonBar.buttonData="CANCEL_CLOSE" onAction="#close"/>
</buttons>
</ButtonBar>