Spring Sweets: Custom Exit Code From Exception
When we write a Spring Boot application a lot of things are done for us.
For example when an exception in the application occurs when we start our application, Spring Boot will exit the application with exit code 1
.
If everything goes well and the we stop the application the exit code is 0
.
When we use the run
method of SpringApplication
and an exception is not handled by our code, Spring Boot will catch it and will check if the exception implements the ExitCodeGenerator
interface.
The ExitCodeGenerator
interface has one method getExitCode()
which must return a exit code value.
This value is used as input argument for the method System.exit()
that is invoke by Spring Boot to stop the application.
In the following example application we write a Spring Boot command line application that can throw an exception on startup.
The exception class implements the ExitCodeGenerator
interface:
package mrhaki.springboot;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.ExitCodeGenerator;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import java.util.Random;
import static org.springframework.boot.SpringApplication.run;
@SpringBootApplication
public class SampleApp {
/**
* Run the application, let Spring Boot
* decide the exit code and use the
* exit code value for {@code System.exit()} method.
*/
public static void main(String[] args) {
run(SampleApp.class, args);
}
/**
* Very simple {@code CommandLineRunner} to get a
* random {@code Integer} value and throw exception if value
* is higher than or equal to 5. This code is executed
* when we run the application.
*
* @return Simple application logic.
*/
@Bean
CommandLineRunner randomException() {
return args -> {
final Integer value = new Random().nextInt(10);
if (value >= 5) {
throw new TooHighException(String.format("Value %d is too high", value));
} else {
System.out.println(value);
}
};
}
/**
* Exception when a Integer value is too high.
* We implement the {@code ExitCodeGenerator} interface and
* annotate this class as a Spring component. Spring Boot
* will look for classes that implement {@code ExitCodeGenerator}
* and use them to get a exit code.
*/
static class TooHighException extends Exception implements ExitCodeGenerator {
TooHighException(final String message) {
super(message);
}
/**
* @return Always return 42 as exit code when this exception occurs.
*/
@Override
public int getExitCode() {
return 42;
}
}
}
But there is also an exit
method available in the SpringApplication
class.
This method determines the exit code value by looking up beans in the application context that implement the ExitCodeExceptionMapper
interface.
The ExitCodeExceptionMapper
interface has the method getExitCode(Throwable)
.
We can write an implementation that has logic to return different exit codes based on characteristics of the given Throwable
.
This is especially useful when we want to have an exit code for exceptions from third party libraries.
In the following example we use the exit
method of SpringApplication
and a Spring bean exitCodeExceptionMapper
that implements the ExitCodeExceptionMapper
interface using a Java 8 lambda expression:
package mrhaki.springboot;
import org.jooq.DSLContext;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.ExitCodeExceptionMapper;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.dao.DataAccessResourceFailureException;
import static mrhaki.springboot.sql.Tables.USERS;
import static org.springframework.boot.SpringApplication.exit;
import static org.springframework.boot.SpringApplication.run;
@SpringBootApplication
public class SampleApp {
/**
* Run the application, let Spring Boot
* decide the exit code and use the
* exit code value for {@code System.exit()} method.
*/
public static void main(String[] args) {
System.exit(exit(run(SampleApp.class, args)));
}
/**
* Very simple {@code CommandLineRunner} to get a
* a list of users from a Postgresql database
* using JOOQ.
*
* @return Simple application logic.
*/
@Bean
CommandLineRunner showUsers(final DSLContext dslContext) {
return args -> dslContext.select(USERS.NAME)
.from(USERS)
.fetch()
.forEach(name -> System.out.println(name.get(USERS.NAME)));
}
/**
* Spring bean that implements the {@code ExitCodeExceptionMapper}
* interface using a lambda expression.
*/
@Bean
ExitCodeExceptionMapper exitCodeExceptionMapper() {
return exception -> {
// Using ExitCodeExceptionMapper we can set
// the exit code ourselves, even if we didn't
// write the exception ourselves.
if (exception.getCause() instanceof DataAccessResourceFailureException) {
return 42;
}
return 1;
};
}
}
Written with Spring Boot 1.5.2.RELEASE.