1. Exception Handling
Many command line applications when applicable return an exit code which running environment
can use to differentiate if command has been executed successfully or not. In a spring-shell
this mostly relates when a command is run on a non-interactive mode meaning one command
is always executed once with an instance of a spring-shell.
1.1. Exception Resolving
Unhandled exceptions will bubble up into shell’s ResultHandlerService and then eventually
handled by some instance of ResultHandler. Chain of ExceptionResolver implementations
can be used to resolve exceptions and gives you flexibility to return message to get written
into console together with exit code which are wrapped within CommandHandlingResult.
static class CustomExceptionResolver implements CommandExceptionResolver {
@Override
public CommandHandlingResult resolve(Exception e) {
if (e instanceof CustomException) {
return CommandHandlingResult.of("Hi, handled exception\n", 42);
}
return null;
}
}
CommandExceptionResolver implementations can be defined globally as beans or defined
per CommandRegistration if it’s applicable only for a particular command itself.
CommandRegistration.builder()
.withErrorHandling()
.resolver(new CustomExceptionResolver())
.and()
.build();
Use you own exception types which can also be an instance of boot’s ExitCodeGenerator if
you want to define exit code there.
static class CustomException extends RuntimeException {}
| With annotation based configuration exception resolving can only be customised globally |
1.2. Exit Code Mappings
Default behaviour of an exit codes is as:
-
Errors from a command option parsing will result code of
2 -
Any generic error will result result code of
1 -
Obviously in any other case result code is
0
Every CommandRegistration can define its own mappings between Exception and exit code.
Essentially we’re bound to functionality in Spring Boot regarding exit code and simply
integrate into that.
Assuming there is an exception show below which would be thrown from a command:
static class MyException extends RuntimeException {
private final int code;
MyException(String msg, int code) {
super(msg);
this.code = code;
}
public int getCode() {
return code;
}
}
It is possible to define a mapping function between Throwable and exit code. You can also
just configure a class to exit code which is just a syntactic sugar within configurations.
CommandRegistration.builder()
.withExitCode()
.map(MyException.class, 3)
.map(t -> {
if (t instanceof MyException) {
return ((MyException) t).getCode();
}
return 0;
})
.and()
.build();
| Exit codes cannot be customized with annotation based configuration |