From 68c42bfc3dc55e728954deb10eb81fa32e8403f4 Mon Sep 17 00:00:00 2001 From: crschnick Date: Tue, 28 Nov 2023 14:54:52 +0000 Subject: [PATCH] Rework operation mode error handling --- .../io/xpipe/app/core/mode/OperationMode.java | 17 +++++++++-- .../io/xpipe/app/core/mode/PlatformMode.java | 10 ++----- .../app/exchange/cli/ModeExchangeImpl.java | 2 +- .../io/xpipe/app/issue/GuiErrorHandler.java | 19 ++++++------ .../xpipe/app/issue/GuiErrorHandlerBase.java | 11 ++----- .../xpipe/app/issue/TerminalErrorHandler.java | 29 +++++++++---------- .../xpipe/app/launcher/LauncherCommand.java | 6 ++-- .../io/xpipe/app/launcher/LauncherInput.java | 2 +- .../java/io/xpipe/app/util/PlatformState.java | 23 +++++++++++---- 9 files changed, 68 insertions(+), 51 deletions(-) diff --git a/app/src/main/java/io/xpipe/app/core/mode/OperationMode.java b/app/src/main/java/io/xpipe/app/core/mode/OperationMode.java index f0aef40f5..17905db7f 100644 --- a/app/src/main/java/io/xpipe/app/core/mode/OperationMode.java +++ b/app/src/main/java/io/xpipe/app/core/mode/OperationMode.java @@ -4,6 +4,7 @@ import io.xpipe.app.core.*; import io.xpipe.app.ext.DataStoreProviders; import io.xpipe.app.issue.*; import io.xpipe.app.launcher.LauncherCommand; +import io.xpipe.app.util.PlatformState; import io.xpipe.app.util.ThreadHelper; import io.xpipe.app.util.XPipeSession; import io.xpipe.core.store.LocalStore; @@ -121,10 +122,21 @@ public abstract class OperationMode { } public static void switchToAsync(OperationMode newMode) { - ThreadHelper.createPlatformThread("mode switcher", false, () -> switchTo(newMode)).start(); + ThreadHelper.createPlatformThread("mode switcher", false, () -> { + switchToSyncIfPossible(newMode); + }).start(); } - public static void switchTo(OperationMode newMode) { + public static void switchToSyncOrThrow(OperationMode newMode) throws Throwable { + TrackEvent.info("Attempting to switch mode to " + newMode.getId()); + + if (!newMode.isSupported()) { + throw PlatformState.getLastError() != null ? PlatformState.getLastError() : new IllegalStateException("Unsupported operation mode: " + newMode.getId()); + } + + set(newMode); + } + public static void switchToSyncIfPossible(OperationMode newMode) { TrackEvent.info("Attempting to switch mode to " + newMode.getId()); if (newMode.equals(TRAY) && !TRAY.isSupported()) { @@ -142,6 +154,7 @@ public abstract class OperationMode { set(newMode); } + public static void switchUp(OperationMode newMode) { if (newMode == BACKGROUND) { return; diff --git a/app/src/main/java/io/xpipe/app/core/mode/PlatformMode.java b/app/src/main/java/io/xpipe/app/core/mode/PlatformMode.java index 9ad2fba57..1a2aae548 100644 --- a/app/src/main/java/io/xpipe/app/core/mode/PlatformMode.java +++ b/app/src/main/java/io/xpipe/app/core/mode/PlatformMode.java @@ -13,8 +13,8 @@ public abstract class PlatformMode extends OperationMode { @Override public boolean isSupported() { - PlatformState.initPlatform(); - return PlatformState.getCurrent() == PlatformState.RUNNING; + var r = PlatformState.initPlatformIfNeeded(); + return r; } @Override @@ -24,11 +24,7 @@ public abstract class PlatformMode extends OperationMode { } TrackEvent.info("mode", "Platform mode initial setup"); - var r = PlatformState.initPlatform(); - if (r.isPresent()) { - throw r.get(); - } - + PlatformState.initPlatformOrThrow(); AppFont.loadFonts(); AppTheme.init(); AppStyle.init(); diff --git a/app/src/main/java/io/xpipe/app/exchange/cli/ModeExchangeImpl.java b/app/src/main/java/io/xpipe/app/exchange/cli/ModeExchangeImpl.java index 58ae1e32a..1b91e049f 100644 --- a/app/src/main/java/io/xpipe/app/exchange/cli/ModeExchangeImpl.java +++ b/app/src/main/java/io/xpipe/app/exchange/cli/ModeExchangeImpl.java @@ -26,7 +26,7 @@ public class ModeExchangeImpl extends ModeExchange .toList())); } - OperationMode.switchTo(mode); + OperationMode.switchToSyncIfPossible(mode); return ModeExchange.Response.builder() .usedMode(OperationMode.map(OperationMode.get())) .build(); diff --git a/app/src/main/java/io/xpipe/app/issue/GuiErrorHandler.java b/app/src/main/java/io/xpipe/app/issue/GuiErrorHandler.java index b518eae80..2751c06b8 100644 --- a/app/src/main/java/io/xpipe/app/issue/GuiErrorHandler.java +++ b/app/src/main/java/io/xpipe/app/issue/GuiErrorHandler.java @@ -1,6 +1,5 @@ package io.xpipe.app.issue; -import io.xpipe.app.core.mode.OperationMode; import io.xpipe.app.util.LicenseProvider; import io.xpipe.app.util.LicenseRequiredException; @@ -12,23 +11,23 @@ public class GuiErrorHandler extends GuiErrorHandlerBase implements ErrorHandler public void handle(ErrorEvent event) { log.handle(event); - if (!OperationMode.GUI.isSupported() || event.isOmitted()) { + if (event.isOmitted()) { ErrorAction.ignore().handle(event); return; } + if (!startupGui(throwable -> { + var second = ErrorEvent.fromThrowable(throwable).build(); + log.handle(second); + ErrorAction.ignore().handle(second); + })) { + return; + } + handleGui(event); } private void handleGui(ErrorEvent event) { - if (!startupGui(throwable -> { - log.handle(ErrorEvent.fromThrowable(throwable).build()); - ErrorAction.ignore().handle(event); - })) { - return; - } - - if (event.getThrowable() instanceof LicenseRequiredException lex) { LicenseProvider.get().showLicenseAlert(lex); event.setShouldSendDiagnostics(true); diff --git a/app/src/main/java/io/xpipe/app/issue/GuiErrorHandlerBase.java b/app/src/main/java/io/xpipe/app/issue/GuiErrorHandlerBase.java index c2cfb4c84..5e050606b 100644 --- a/app/src/main/java/io/xpipe/app/issue/GuiErrorHandlerBase.java +++ b/app/src/main/java/io/xpipe/app/issue/GuiErrorHandlerBase.java @@ -8,21 +8,16 @@ import java.util.function.Consumer; public class GuiErrorHandlerBase { protected boolean startupGui(Consumer onFail) { - var ex = PlatformState.initPlatform(); - if (ex.isPresent()) { - onFail.accept(ex.get()); - return false; - } - try { + PlatformState.initPlatformOrThrow(); AppProperties.init(); AppState.init(); AppExtensionManager.init(false); AppI18n.init(); AppStyle.init(); AppTheme.init(); - } catch (Throwable r) { - onFail.accept(r); + } catch (Throwable ex) { + onFail.accept(ex); return false; } diff --git a/app/src/main/java/io/xpipe/app/issue/TerminalErrorHandler.java b/app/src/main/java/io/xpipe/app/issue/TerminalErrorHandler.java index e6ee49340..8f164247b 100644 --- a/app/src/main/java/io/xpipe/app/issue/TerminalErrorHandler.java +++ b/app/src/main/java/io/xpipe/app/issue/TerminalErrorHandler.java @@ -16,23 +16,23 @@ public class TerminalErrorHandler extends GuiErrorHandlerBase implements ErrorHa public void handle(ErrorEvent event) { log.handle(event); - if (!OperationMode.GUI.isSupported() || event.isOmitted() || OperationMode.isInShutdown()) { - SentryErrorHandler.getInstance().handle(event); + if (event.isOmitted() || OperationMode.isInShutdown()) { + ErrorAction.ignore().handle(event); OperationMode.halt(1); return; } + if (!startupGui(throwable -> { + handleWithSecondaryException(event, throwable); + ErrorAction.ignore().handle(event); + })) { + return; + } + handleGui(event); } private void handleGui(ErrorEvent event) { - if (!startupGui(throwable -> { - handleSecondaryException(event, throwable); - ErrorAction.ignore().handle(event); - })) { - return; - } - try { AppProperties.init(); AppState.init(); @@ -43,7 +43,7 @@ public class TerminalErrorHandler extends GuiErrorHandlerBase implements ErrorHa ErrorHandlerComp.showAndTryWait(event, true); } catch (Throwable r) { event.clearAttachments(); - handleSecondaryException(event, r); + handleWithSecondaryException(event, r); return; } @@ -54,13 +54,12 @@ public class TerminalErrorHandler extends GuiErrorHandlerBase implements ErrorHa OperationMode.halt(1); } - private void handleSecondaryException(ErrorEvent event, Throwable t) { - log.handle(event); - SentryErrorHandler.getInstance().handle(event); + private void handleWithSecondaryException(ErrorEvent event, Throwable t) { + ErrorAction.ignore().handle(event); var second = ErrorEvent.fromThrowable(t).build(); log.handle(second); - SentryErrorHandler.getInstance().handle(ErrorEvent.fromThrowable(t).build()); + ErrorAction.ignore().handle(second); OperationMode.halt(1); } @@ -89,7 +88,7 @@ public class TerminalErrorHandler extends GuiErrorHandlerBase implements ErrorHa } catch (Throwable t) { var event = ErrorEvent.fromThrowable(t).build(); log.handle(event); - SentryErrorHandler.getInstance().handle(event); + ErrorAction.ignore().handle(event); OperationMode.halt(1); } } diff --git a/app/src/main/java/io/xpipe/app/launcher/LauncherCommand.java b/app/src/main/java/io/xpipe/app/launcher/LauncherCommand.java index 7e0a55c76..85ebd0834 100644 --- a/app/src/main/java/io/xpipe/app/launcher/LauncherCommand.java +++ b/app/src/main/java/io/xpipe/app/launcher/LauncherCommand.java @@ -13,6 +13,7 @@ import io.xpipe.beacon.exchange.FocusExchange; import io.xpipe.beacon.exchange.OpenExchange; import io.xpipe.core.process.OsType; import io.xpipe.core.util.XPipeDaemonMode; +import lombok.SneakyThrows; import picocli.CommandLine; import java.awt.*; @@ -118,11 +119,12 @@ public class LauncherCommand implements Callable { } @Override + @SneakyThrows public Integer call() { checkStart(); // Initialize base mode first to have access to the preferences to determine effective mode - OperationMode.switchTo(OperationMode.BACKGROUND); - OperationMode.switchTo(OperationMode.map(getEffectiveMode())); + OperationMode.switchToSyncOrThrow(OperationMode.BACKGROUND); + OperationMode.switchToSyncOrThrow(OperationMode.map(getEffectiveMode())); LauncherInput.handle(inputs); // URL open operations have to be handled in a special way on macOS! diff --git a/app/src/main/java/io/xpipe/app/launcher/LauncherInput.java b/app/src/main/java/io/xpipe/app/launcher/LauncherInput.java index c1b6597c0..466961e13 100644 --- a/app/src/main/java/io/xpipe/app/launcher/LauncherInput.java +++ b/app/src/main/java/io/xpipe/app/launcher/LauncherInput.java @@ -39,7 +39,7 @@ public abstract class LauncherInput { var requiresPlatform = all.stream().anyMatch(launcherInput -> launcherInput.requiresJavaFXPlatform()); if (requiresPlatform) { - OperationMode.switchTo(OperationMode.GUI); + OperationMode.switchToSyncIfPossible(OperationMode.GUI); } var hasGui = OperationMode.get() == OperationMode.GUI; diff --git a/app/src/main/java/io/xpipe/app/util/PlatformState.java b/app/src/main/java/io/xpipe/app/util/PlatformState.java index a21ead9d2..866d923d5 100644 --- a/app/src/main/java/io/xpipe/app/util/PlatformState.java +++ b/app/src/main/java/io/xpipe/app/util/PlatformState.java @@ -21,6 +21,9 @@ public enum PlatformState { @Setter private static PlatformState current = PlatformState.NOT_INITIALIZED; + @Getter + private static Exception lastError; + public static void teardown() { PlatformThread.runLaterIfNeededBlocking(() -> { // Fix to preserve clipboard contents after shutdown @@ -36,14 +39,24 @@ public enum PlatformState { setCurrent(PlatformState.EXITED); } - public static void initPlatformOrThrow() throws Throwable { - var r = PlatformState.initPlatform(); - if (r.isPresent()) { - throw r.get(); + public static void initPlatformOrThrow() throws Exception { + initPlatformIfNeeded(); + if (lastError != null) { + throw lastError; } } - public static Optional initPlatform() { + public static boolean initPlatformIfNeeded() { + if (current == NOT_INITIALIZED) { + var t = PlatformState.initPlatform().orElse(null); + lastError = t instanceof Exception e ? e : t != null ? new Exception(t) : null; + } + + return current == RUNNING; + } + + + private static Optional initPlatform() { if (current == EXITED) { return Optional.of(new IllegalStateException("Platform has already exited")); }