From cc2fa6dfa428c518bf62b99811555d612ad60570 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Hohwiller?= Date: Tue, 9 Apr 2024 02:16:43 +0200 Subject: [PATCH] improved exception handling and return codes --- .../tools/ide/cli/CliAbortException.java | 34 +++--- .../tools/ide/cli/CliOfflineException.java | 28 +++++ .../tools/ide/process/ProcessContextImpl.java | 29 ++--- .../tools/ide/process/ProcessResult.java | 103 ++++++++++-------- 4 files changed, 117 insertions(+), 77 deletions(-) create mode 100644 cli/src/main/java/com/devonfw/tools/ide/cli/CliOfflineException.java diff --git a/cli/src/main/java/com/devonfw/tools/ide/cli/CliAbortException.java b/cli/src/main/java/com/devonfw/tools/ide/cli/CliAbortException.java index 175b51da7..99bb832e3 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/cli/CliAbortException.java +++ b/cli/src/main/java/com/devonfw/tools/ide/cli/CliAbortException.java @@ -1,16 +1,18 @@ -package com.devonfw.tools.ide.cli; - -/** - * {@link CliException} that is thrown if the user aborted further processing due - */ -public final class CliAbortException extends CliException { - - /** - * The constructor. - */ - public CliAbortException() { - - super("Aborted by end-user.", 22); - } - -} +package com.devonfw.tools.ide.cli; + +import com.devonfw.tools.ide.process.ProcessResult; + +/** + * {@link CliException} that is thrown if the user aborted further processing due + */ +public final class CliAbortException extends CliException { + + /** + * The constructor. + */ + public CliAbortException() { + + super("Aborted by end-user.", ProcessResult.ABORT); + } + +} diff --git a/cli/src/main/java/com/devonfw/tools/ide/cli/CliOfflineException.java b/cli/src/main/java/com/devonfw/tools/ide/cli/CliOfflineException.java new file mode 100644 index 000000000..a1c043571 --- /dev/null +++ b/cli/src/main/java/com/devonfw/tools/ide/cli/CliOfflineException.java @@ -0,0 +1,28 @@ +package com.devonfw.tools.ide.cli; + +import com.devonfw.tools.ide.process.ProcessResult; + +/** + * {@link CliException} that is thrown if the user aborted further processing due + */ +public final class CliOfflineException extends CliException { + + /** + * The constructor. + */ + public CliOfflineException() { + + super("You are offline but network connection is required to perform the operation.", ProcessResult.OFFLINE); + } + + /** + * The constructor. + * + * @param message the {@link #getMessage() message}. + */ + public CliOfflineException(String message) { + + super(message, ProcessResult.OFFLINE); + } + +} diff --git a/cli/src/main/java/com/devonfw/tools/ide/process/ProcessContextImpl.java b/cli/src/main/java/com/devonfw/tools/ide/process/ProcessContextImpl.java index 16ba79150..72b0a3d4b 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/process/ProcessContextImpl.java +++ b/cli/src/main/java/com/devonfw/tools/ide/process/ProcessContextImpl.java @@ -30,6 +30,7 @@ public class ProcessContextImpl implements ProcessContext { private static final String PREFIX_USR_BIN_ENV = "/usr/bin/env "; + /** The owning {@link IdeContext}. */ protected final IdeContext context; private final ProcessBuilder processBuilder; @@ -74,7 +75,7 @@ public ProcessContext directory(Path directory) { if (directory != null) { this.processBuilder.directory(directory.toFile()); } else { - context.debug( + this.context.debug( "Could not set the process builder's working directory! Directory of the current java process is used."); } @@ -295,14 +296,10 @@ private String findBashOnWindows() { throw new IllegalStateException("Could not find Bash. Please install Git for Windows and rerun."); } - private String addExecutable(String executable, List args) { + private String addExecutable(String exec, List args) { - if (!SystemInfoImpl.INSTANCE.isWindows()) { - args.add(executable); - return null; - } String interpreter = null; - String fileExtension = FilenameUtil.getExtension(executable); + String fileExtension = FilenameUtil.getExtension(exec); boolean isBashScript = "sh".equals(fileExtension); if (!isBashScript) { String sheBang = getSheBang(this.executable); @@ -323,7 +320,6 @@ private String addExecutable(String executable, List args) { String bash = "bash"; interpreter = bash; // here we want to have native OS behavior even if OS is mocked during tests... - // if (this.context.getSystemInfo().isWindows()) { if (SystemInfoImpl.INSTANCE.isWindows()) { String findBashOnWindowsResult = findBashOnWindows(); if (findBashOnWindowsResult != null) { @@ -331,12 +327,11 @@ private String addExecutable(String executable, List args) { } } args.add(bash); + } else if (SystemInfoImpl.INSTANCE.isWindows() && "msi".equalsIgnoreCase(fileExtension)) { + args.add("msiexec"); + args.add("/i"); } - if ("msi".equalsIgnoreCase(fileExtension)) { - args.add(0, "/i"); - args.add(0, "msiexec"); - } - args.add(executable); + args.add(exec); return interpreter; } @@ -354,7 +349,7 @@ private void performLogOnError(ProcessResult result, int exitCode, String interp level = this.context.warning(); } else { level = this.context.error(); - level.log("Internal error: Undefined error handling {}", this.errorHandling); + this.context.error("Internal error: Undefined error handling {}", this.errorHandling); } level.log(message); } @@ -373,7 +368,7 @@ private void modifyArgumentsOnBackgroundProcess(ProcessMode processMode) { String bash = "bash"; // try to use bash in windows to start the process - if (context.getSystemInfo().isWindows()) { + if (this.context.getSystemInfo().isWindows()) { String findBashOnWindowsResult = findBashOnWindows(); if (findBashOnWindowsResult != null) { @@ -381,7 +376,7 @@ private void modifyArgumentsOnBackgroundProcess(ProcessMode processMode) { bash = findBashOnWindowsResult; } else { - context.warning( + this.context.warning( "Cannot start background process in windows! No bash installation found, output will be discarded."); this.processBuilder.redirectOutput(Redirect.DISCARD).redirectError(Redirect.DISCARD); return; @@ -400,7 +395,7 @@ private void modifyArgumentsOnBackgroundProcess(ProcessMode processMode) { private String buildCommandToRunInBackground() { - if (context.getSystemInfo().isWindows()) { + if (this.context.getSystemInfo().isWindows()) { StringBuilder stringBuilder = new StringBuilder(); diff --git a/cli/src/main/java/com/devonfw/tools/ide/process/ProcessResult.java b/cli/src/main/java/com/devonfw/tools/ide/process/ProcessResult.java index 56f59edf5..754aedef9 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/process/ProcessResult.java +++ b/cli/src/main/java/com/devonfw/tools/ide/process/ProcessResult.java @@ -1,44 +1,59 @@ -package com.devonfw.tools.ide.process; - -import java.util.List; - -/** - * Result of a {@link Process} execution. - * - * @see ProcessContext#run() - */ -public interface ProcessResult { - - /** Exit code for success. */ - int SUCCESS = 0; - - /** Exit code if tool was requested that is not installed. */ - int TOOL_NOT_INSTALLED = 4; - - /** - * @return the exit code. Will be {@link #SUCCESS} on successful completion of the {@link Process}. - */ - int getExitCode(); - - /** - * @return {@code true} if the {@link #getExitCode() exit code} indicates {@link #SUCCESS}, {@code false} otherwise - * (an error occurred). - */ - default boolean isSuccessful() { - - return getExitCode() == SUCCESS; - } - - /** - * @return the {@link List} with the lines captured on standard out. Will be {@code null} if not captured but - * redirected. - */ - List getOut(); - - /** - * @return the {@link List} with the lines captured on standard error. Will be {@code null} if not captured but - * redirected. - */ - List getErr(); - -} +package com.devonfw.tools.ide.process; + +import java.util.List; + +/** + * Result of a {@link Process} execution. + * + * @see ProcessContext#run() + */ +public interface ProcessResult { + + /** Return code for success. */ + int SUCCESS = 0; + + /** Return code if tool was requested that is not installed. */ + int TOOL_NOT_INSTALLED = 4; + + /** + * Return code to abort gracefully. + * + * @see com.devonfw.tools.ide.cli.CliAbortException + */ + int ABORT = 22; + + /** + * Return code if {@link com.devonfw.tools.ide.context.IdeContext#isOffline() offline} but network is required for + * requested operation. + * + * @see com.devonfw.tools.ide.cli.CliOfflineException + */ + int OFFLINE = 23; + + /** + * @return the exit code. Will be {@link #SUCCESS} on successful completion of the {@link Process}. + */ + int getExitCode(); + + /** + * @return {@code true} if the {@link #getExitCode() exit code} indicates {@link #SUCCESS}, {@code false} otherwise + * (an error occurred). + */ + default boolean isSuccessful() { + + return getExitCode() == SUCCESS; + } + + /** + * @return the {@link List} with the lines captured on standard out. Will be {@code null} if not captured but + * redirected. + */ + List getOut(); + + /** + * @return the {@link List} with the lines captured on standard error. Will be {@code null} if not captured but + * redirected. + */ + List getErr(); + +}