From 0e399adcbb731b4251ae5ff95a7773723437f9b5 Mon Sep 17 00:00:00 2001 From: Janne Valkealahti Date: Fri, 27 May 2022 08:23:45 +0100 Subject: [PATCH] Better errors with non-interactive mode - Change how errors are printed for interactive vs non-interactive mode. - This now changes behaviour so that stacktrace is printed if non-interactive mode is active so that user has a change to see the full error. - Fixes #427 --- .../shell/result/ResultHandlerConfig.java | 5 +- .../shell/result/ThrowableResultHandler.java | 56 +++++++++++++++---- 2 files changed, 47 insertions(+), 14 deletions(-) diff --git a/spring-shell-core/src/main/java/org/springframework/shell/result/ResultHandlerConfig.java b/spring-shell-core/src/main/java/org/springframework/shell/result/ResultHandlerConfig.java index b7127c0d5..2d1cd9f5a 100644 --- a/spring-shell-core/src/main/java/org/springframework/shell/result/ResultHandlerConfig.java +++ b/spring-shell-core/src/main/java/org/springframework/shell/result/ResultHandlerConfig.java @@ -23,6 +23,7 @@ import org.springframework.context.annotation.Configuration; import org.springframework.shell.TerminalSizeAware; import org.springframework.shell.command.CommandCatalog; +import org.springframework.shell.context.ShellContext; import org.springframework.shell.jline.InteractiveShellRunner; /** @@ -62,7 +63,7 @@ public CommandParserExceptionsExceptionResultHandler commandParserExceptionsExce @Bean public ThrowableResultHandler throwableResultHandler(Terminal terminal, CommandCatalog commandCatalog, - ObjectProvider interactiveApplicationRunner) { - return new ThrowableResultHandler(terminal, commandCatalog, interactiveApplicationRunner); + ShellContext shellContext, ObjectProvider interactiveApplicationRunner) { + return new ThrowableResultHandler(terminal, commandCatalog, shellContext, interactiveApplicationRunner); } } diff --git a/spring-shell-core/src/main/java/org/springframework/shell/result/ThrowableResultHandler.java b/spring-shell-core/src/main/java/org/springframework/shell/result/ThrowableResultHandler.java index 26aba98c2..daa2486ab 100644 --- a/spring-shell-core/src/main/java/org/springframework/shell/result/ThrowableResultHandler.java +++ b/spring-shell-core/src/main/java/org/springframework/shell/result/ThrowableResultHandler.java @@ -13,9 +13,11 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package org.springframework.shell.result; +import java.io.PrintWriter; +import java.io.StringWriter; + import org.jline.terminal.Terminal; import org.jline.utils.AttributedString; import org.jline.utils.AttributedStringBuilder; @@ -24,15 +26,20 @@ import org.springframework.beans.factory.ObjectProvider; import org.springframework.shell.ResultHandler; import org.springframework.shell.command.CommandCatalog; +import org.springframework.shell.context.InteractionMode; +import org.springframework.shell.context.ShellContext; import org.springframework.shell.jline.InteractiveShellRunner; import org.springframework.util.StringUtils; /** * A {@link ResultHandler} that prints thrown exceptions messages in red. * - *

Also stores the last exception reported, so that details can be printed using a dedicated command.

+ * Stores the last exception reported, so that details can be printed using a + * dedicated command if in interactive mode. Prints stacktrace if in + * non-interactive mode as dedicated command could not be used. * * @author Eric Bottard + * @author Janne Valkealahti */ public class ThrowableResultHandler extends TerminalAwareResultHandler { @@ -47,30 +54,46 @@ public class ThrowableResultHandler extends TerminalAwareResultHandler interactiveRunner; - public ThrowableResultHandler(Terminal terminal, CommandCatalog commandCatalog, + private ShellContext shellContext; + + public ThrowableResultHandler(Terminal terminal, CommandCatalog commandCatalog, ShellContext shellContext, ObjectProvider interactiveRunner) { super(terminal); this.commandCatalog = commandCatalog; + this.shellContext = shellContext; this.interactiveRunner = interactiveRunner; } @Override protected void doHandleResult(Throwable result) { lastError = result; - String toPrint = StringUtils.hasLength(result.getMessage()) ? result.getMessage() : result.toString(); - terminal.writer().println(new AttributedString(toPrint, + boolean shouldHandle = shouldHandle(); + + if (shouldHandle) { + String errorMsg = StringUtils.hasLength(result.getMessage()) ? result.getMessage() : result.toString(); + terminal.writer().println(new AttributedString(errorMsg, AttributedStyle.DEFAULT.foreground(AttributedStyle.RED)).toAnsi()); - if (interactiveRunner.getIfAvailable() != null && commandCatalog.getRegistrations().keySet().contains(DETAILS_COMMAND_NAME)) { - terminal.writer().println( - new AttributedStringBuilder() + + String noteMsg; + if (showShortError()) { + noteMsg = new AttributedStringBuilder() .append("Details of the error have been omitted. You can use the ", AttributedStyle.DEFAULT.foreground(AttributedStyle.RED)) .append(DETAILS_COMMAND_NAME, AttributedStyle.DEFAULT.foreground(AttributedStyle.RED).bold()) .append(" command to print the full stacktrace.", AttributedStyle.DEFAULT.foreground(AttributedStyle.RED)) - .toAnsi() - ); + .toAnsi(); + } + else { + StringWriter sw = new StringWriter(); + PrintWriter pw = new PrintWriter(sw); + result.printStackTrace(pw); + String stacktraceStr = sw.toString(); + noteMsg = new AttributedString(stacktraceStr, + AttributedStyle.DEFAULT.foreground(AttributedStyle.RED)).toAnsi(); + } + terminal.writer().println(noteMsg); + terminal.writer().flush(); } - terminal.writer().flush(); - if (interactiveRunner.getIfAvailable() == null) { + else { if (result instanceof RuntimeException) { throw (RuntimeException) result; } @@ -89,4 +112,13 @@ else if (result instanceof Error) { public Throwable getLastError() { return lastError; } + + private boolean shouldHandle() { + return interactiveRunner.getIfAvailable() != null; + } + + private boolean showShortError() { + return commandCatalog.getRegistrations().keySet().contains(DETAILS_COMMAND_NAME) + && this.shellContext.getInteractionMode() == InteractionMode.INTERACTIVE; + } }