Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Spring boot CommandLineRunner Exception Handling

We are using Spring-boot for a commandline application. We are using javax.validation for validating command-line arguments.

Now, if we have a validation error how can we print friendly error message? We don't want to show Stack trace.

Is there a ExceptionHandler mechanism we could use when we are running Spring-boot as CommandLineRunner?

Thanks Arun

Source

    @SpringBootApplication
    public class Deploy implements CommandLineRunner {
    private static final Logger LOGGER = LoggerFactory.getLogger(Deploy.class);

    @Autowired
    private DeployConfig config;

    @Autowired
    private DeployService deployService;

    /**
     * mvn clean package spring-boot:repackage
     * java -jar target/spring-boot-example-1.0.0-SNAPSHOT.jar --spring.profiles.active=qa --version=1.0
     *
     * @param strings arguments
     * @throws Exception
     */

    @Override
    public void run(String... strings) throws Exception {
        try {
            deployService.deploy(config);
        } catch (Exception ve) {
            LOGGER.error("Error : {}", ve.getMessage());
        }

        LOGGER.info("Created stack={}", config.getVersion());
    }

    public static void main(String... args) {
        LOGGER.info("Starting to run...");
        SpringApplication.run(Deploy.class, args);
        LOGGER.info("Completed the run...");
    }
}

Configuration

@Configuration
@EnableConfigurationProperties
@ConfigurationProperties
public class DeployConfig {

    @NotNull
    private String hello;

    @NotNull
    private String version;

    private String envKey;

    public String getHello() {
        return hello;
    }

    public void setHello(String hello) {
        this.hello = hello;
    }

    public String getVersion() {
        return version;
    }

    public void setVersion(String version) {
        this.version = version;
    }

    public String getEnvKey() {
        return envKey;
    }

    public void setEnvKey(String envKey) {
        this.envKey = envKey;
    }

    public String toString() {
        return ToStringBuilder.reflectionToString(this);
    }
}

Clean Run

mvn clean package spring-boot:repackage
java -jar target/spring-boot-example-1.0.0-SNAPSHOT.jar --spring.profiles.active=preprod,qa --version=1.0

Validation Check

java -jar target/spring-boot-example-1.0.0-SNAPSHOT.jar --spring.profiles.active=preprod,qa

Validation Error

2014-12-25 20:51:13,325 ERROR [main] [o.s.b.SpringApplication.run()] - Application startup failed
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'deploy': Injection of autowired dependencies failed; nested exception is org.springframework.beans.factory.BeanCreationException: Could not autowire field: private com.example.DeployConfig com.example.Deploy.config; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'deployConfig': Could not bind properties; nested exception is org.springframework.validation.BindException: org.springframework.validation.BeanPropertyBindingResult: 1 errors
Field error in object 'target' on field 'version': rejected value [null]; codes [NotNull.target.version,NotNull.version,NotNull.java.lang.String,NotNull]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [target.version,version]; arguments []; default message [version]]; default message [may not be null]
    at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessPropertyValues(AutowiredAnnotationBeanPostProcessor.java:334) ~[spring-beans-4.1.3.RELEASE.jar!/:4.1.3.RELEASE]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1202) ~[spring-beans-4.1.3.RELEASE.jar!/:4.1.3.RELEASE]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:537) ~[spring-beans-4.1.3.RELEASE.jar!/:4.1.3.RELEASE]

Complete Source

Source can be found in GitHub

like image 783
Arun Avanathan Avatar asked Dec 25 '14 06:12

Arun Avanathan


1 Answers

I've run into (almost) the same problem, and (much to my surprise) it seems the cleanest way to display an error to the user from a Spring Boot command line program is to actually System.exit(1) from CommandlineRunner.run(), after logging whatever error message you want. The Spring context will shut down cleanly anyway, but it doesn't trigger the context startup failure events, so you don't get all the other distracting log output.

You might have to adjust how you call the validation so you can catch the validation errors yourself inside run() and translate them into log + System.exit().

like image 180
Karsten Avatar answered Sep 22 '22 00:09

Karsten