Spring boot CommandLineRunner Exception Handling

14,962

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().

Share:
14,962
Arun Avanathan
Author by

Arun Avanathan

Updated on July 26, 2022

Comments

  • Arun Avanathan
    Arun Avanathan almost 2 years

    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