Skip to content

Commit

Permalink
improved exception handling and return codes
Browse files Browse the repository at this point in the history
  • Loading branch information
hohwille committed Apr 9, 2024
1 parent 4601632 commit cc2fa6d
Show file tree
Hide file tree
Showing 4 changed files with 117 additions and 77 deletions.
34 changes: 18 additions & 16 deletions cli/src/main/java/com/devonfw/tools/ide/cli/CliAbortException.java
Original file line number Diff line number Diff line change
@@ -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);
}

}
Original file line number Diff line number Diff line change
@@ -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);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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.");
}

Expand Down Expand Up @@ -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<String> args) {
private String addExecutable(String exec, List<String> 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);
Expand All @@ -323,20 +320,18 @@ private String addExecutable(String executable, List<String> 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) {
bash = findBashOnWindowsResult;
}
}
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;
}

Expand All @@ -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);
}
Expand All @@ -373,15 +368,15 @@ 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) {

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;
Expand All @@ -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();

Expand Down
103 changes: 59 additions & 44 deletions cli/src/main/java/com/devonfw/tools/ide/process/ProcessResult.java
Original file line number Diff line number Diff line change
@@ -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<String> getOut();

/**
* @return the {@link List} with the lines captured on standard error. Will be {@code null} if not captured but
* redirected.
*/
List<String> 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<String> getOut();

/**
* @return the {@link List} with the lines captured on standard error. Will be {@code null} if not captured but
* redirected.
*/
List<String> getErr();

}

0 comments on commit cc2fa6d

Please sign in to comment.