Skip to content

Commit

Permalink
Handle RuntimeExceptions that occurr during command execution (#197)
Browse files Browse the repository at this point in the history
* Cleaned up error codes and added catch for RuntimeExceptions during command execution, which now result in a RUNTIME_ERROR.
* Split IO and argument use errors apart.

Co-authored-by: A.J. Stein <[email protected]>
  • Loading branch information
david-waltermire and aj-stein-nist authored Jul 14, 2023
1 parent ea71179 commit a006a54
Show file tree
Hide file tree
Showing 5 changed files with 68 additions and 33 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -378,29 +378,38 @@ public ExitStatus processCommand() {
return retval;
}

@SuppressWarnings("PMD.OnlyOneReturn") // readability
@SuppressWarnings({
"PMD.OnlyOneReturn", // readability
"PMD.AvoidCatchingGenericException" // needed here
})
protected ExitStatus invokeCommand(@NonNull CommandLine cmdLine) {
for (ICommand cmd : getCalledCommands()) {
try {
cmd.validateOptions(this, cmdLine);
} catch (InvalidArgumentException ex) {
String msg = ex.getMessage();
assert msg != null;
return handleInvalidCommand(msg);
ExitStatus retval;
try {
for (ICommand cmd : getCalledCommands()) {
try {
cmd.validateOptions(this, cmdLine);
} catch (InvalidArgumentException ex) {
String msg = ex.getMessage();
assert msg != null;
return handleInvalidCommand(msg);
}
}
}

ICommand targetCommand = getTargetCommand();
ExitStatus retval;
if (targetCommand == null) {
retval = ExitCode.INVALID_COMMAND.exit();
} else {
ICommandExecutor executor = targetCommand.newExecutor(this, cmdLine);
retval = executor.execute();
}
ICommand targetCommand = getTargetCommand();
if (targetCommand == null) {
retval = ExitCode.INVALID_COMMAND.exit();
} else {
ICommandExecutor executor = targetCommand.newExecutor(this, cmdLine);
retval = executor.execute();
}

if (ExitCode.INVALID_COMMAND.equals(retval.getExitCode())) {
showHelp();
if (ExitCode.INVALID_COMMAND.equals(retval.getExitCode())) {
showHelp();
}
} catch (RuntimeException ex) {
retval = ExitCode.RUNTIME_ERROR
.exitMessage(String.format("An uncaught runtime error occured. %s", ex.getLocalizedMessage()))
.withThrowable(ex);
}
return retval;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,12 +29,38 @@
import edu.umd.cs.findbugs.annotations.NonNull;

public enum ExitCode {
/**
* The command executed without issue.
*/
OK(0),
/**
* The command executed properly, but the operation failed.
*/
FAIL(1),
INPUT_ERROR(2),
/**
* An error occurred while reading or writing.
*/
IO_ERROR(2),
/**
* A command was requested by name that doesn't exist or required arguments are missing.
*/
INVALID_COMMAND(3),
/**
* The target argument was not found or invalid.
*/
INVALID_TARGET(4),
PROCESSING_ERROR(5);
/**
* Handled errors that occur during command execution.
*/
PROCESSING_ERROR(5),
/**
* Unhandled errors that occur during command execution.
*/
RUNTIME_ERROR(6),
/**
* The provided argument information for a command fails to match argument use requirements.
*/
INVALID_ARGUMENTS(7);

private final int statusCode;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -164,13 +164,13 @@ public ExitStatus execute() {
if (destination != null) {
if (Files.exists(destination)) {
if (!cmdLine.hasOption(OVERWRITE_OPTION)) {
return ExitCode.FAIL.exitMessage(
return ExitCode.INVALID_ARGUMENTS.exitMessage(
String.format("The provided destination '%s' already exists and the '%s' option was not provided.",
destination,
OptionUtils.toArgument(OVERWRITE_OPTION)));
}
if (!Files.isWritable(destination)) {
return ExitCode.FAIL.exitMessage(
return ExitCode.IO_ERROR.exitMessage(
"The provided destination '" + destination + "' is not writable.");
}
} else {
Expand Down Expand Up @@ -204,7 +204,7 @@ public ExitStatus execute() {
loader.convert(source, destination, toFormat, getLoadedClass());
}
} catch (IOException | IllegalArgumentException ex) {
return ExitCode.FAIL.exit().withThrowable(ex); // NOPMD readability
return ExitCode.PROCESSING_ERROR.exit().withThrowable(ex); // NOPMD readability
}
if (destination != null && LOGGER.isInfoEnabled()) {
LOGGER.info("Generated {} file: {}", toFormat.toString(), destination);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -194,7 +194,7 @@ public ExitStatus execute() {
try {
constraintSets.add(constraintLoader.load(constraintPath));
} catch (IOException | MetaschemaException ex) {
return ExitCode.FAIL.exitMessage("Unable to load constraint set '" + arg + "'.").withThrowable(ex);
return ExitCode.IO_ERROR.exitMessage("Unable to load constraint set '" + arg + "'.").withThrowable(ex);
}
}
} else {
Expand All @@ -204,7 +204,7 @@ public ExitStatus execute() {
try {
bindingContext = getBindingContext(constraintSets);
} catch (IOException | MetaschemaException ex) {
return ExitCode.FAIL
return ExitCode.PROCESSING_ERROR
.exitMessage("Unable to get binding context. " + ex.getMessage())
.withThrowable(ex);
}
Expand All @@ -221,7 +221,7 @@ public ExitStatus execute() {
String toFormatText = cmdLine.getOptionValue(AS_OPTION);
asFormat = Format.valueOf(toFormatText.toUpperCase(Locale.ROOT));
} catch (IllegalArgumentException ex) {
return ExitCode.FAIL
return ExitCode.IO_ERROR
.exitMessage("Invalid '--as' argument. The format must be one of: "
+ Arrays.stream(Format.values())
.map(format -> format.name())
Expand All @@ -234,11 +234,11 @@ public ExitStatus execute() {
asFormat = loader.detectFormat(source);
} catch (FileNotFoundException ex) {
// this case was already checked for
return ExitCode.INPUT_ERROR.exitMessage("The provided source file '" + source + "' does not exist.");
return ExitCode.IO_ERROR.exitMessage("The provided source file '" + source + "' does not exist.");
} catch (IOException ex) {
return ExitCode.FAIL.exit().withThrowable(ex);
return ExitCode.PROCESSING_ERROR.exit().withThrowable(ex);
} catch (IllegalArgumentException ex) {
return ExitCode.FAIL.exitMessage(
return ExitCode.IO_ERROR.exitMessage(
"Source file has unrecognizable format. Use '--as' to specify the format. The format must be one of: "
+ Arrays.stream(Format.values())
.map(format -> format.name())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -177,13 +177,13 @@ protected ExitStatus executeCommand(
if (destination != null) {
if (Files.exists(destination)) {
if (!cmdLine.hasOption(OVERWRITE_OPTION)) {
return ExitCode.FAIL.exitMessage( // NOPMD readability
return ExitCode.INVALID_ARGUMENTS.exitMessage( // NOPMD readability
String.format("The provided destination '%s' already exists and the '%s' option was not provided.",
destination,
OptionUtils.toArgument(OVERWRITE_OPTION)));
}
if (!Files.isWritable(destination)) {
return ExitCode.FAIL.exitMessage( // NOPMD readability
return ExitCode.IO_ERROR.exitMessage( // NOPMD readability
"The provided destination '" + destination + "' is not writable.");
}
} else {
Expand Down Expand Up @@ -229,7 +229,7 @@ protected ExitStatus executeCommand(
ISchemaGenerator.generateSchema(metaschema, destination, asFormat, configuration);
}
} catch (IOException | MetaschemaException ex) {
return ExitCode.FAIL.exit().withThrowable(ex); // NOPMD readability
return ExitCode.PROCESSING_ERROR.exit().withThrowable(ex); // NOPMD readability
}
if (destination != null && LOGGER.isInfoEnabled()) {
LOGGER.info("Generated {} schema file: {}", asFormat.toString(), destination);
Expand Down

0 comments on commit a006a54

Please sign in to comment.