How to conditionally make a spring boot application terminate at start-up

Marko Previsic :

I want to implement the following use case - my spring boot application should start only if a certain property in application.yaml is set:

myapp
    active: true

If the property is not set, the context initialization should fail with a message that the property is missing.

I found in this topic how to achieve it: Spring Boot - Detect and terminate if property not set? but the problem why I can't follow this approach is that it is possible that the context initialization fails before the bean, that checks this property, is loaded.

For example, if some other bean fails to load because another property is missing, the context initialization will fail at that point and my bean, that checks the desired property, won't be loaded. This is not OK for me because I want the myapp.active property to be checked first, before any other beans get loaded.

The reason why I want to have it that way is that a certain profile should be set when running the app - the application-[profile].yaml contains both "myapp.active: true" and some other mandatory properties that are required to load the context.

I want my app always to fail because of myapp.active not being true so that I can output a meaningful message telling that the profile is missing and that the app must be run with one of the profiles (from given list of profiles). Some guys that aren't developers are running the app so I want them to know why the app didn't run, otherwise they will think there is some bug in the app.

How can I achieve this? Is it possible somehow to read the property before the beans are being loaded? I would like to avoid setting @DependsOn on all beans (or doing the same through a BeanPostProcesser) and am seeking for a more elegant solution.

Dirk Deyne :

The application won't start if you use a condition-on-property. Fast enough?

 @SpringBootApplication
 @ConditionalOnProperty(name = "myapp.active")
 public class FastFailWhenPropertyNotPresentApplication {

     public static void main(String[] args) {
            SpringApplication.run(FastFailWhenPropertyNotPresentApplication.class, args);
    }

}

Basically @SpringBootApplication is just a @Configuration class.

You have an option matchIfMissing that you can use to specify if the condition should match if the property is not set. Defaults to false.

EDIT:

A better solution is to configure your property via a @ConfigurationProperties combined with @Validated, so you can use the javax.validation.constraints annotations.

package stackoverflow.demo;

import javax.validation.constraints.AssertTrue;
import javax.validation.constraints.NotNull;

import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
import org.springframework.validation.annotation.Validated;

@Component
@ConfigurationProperties(prefix = "myapp")
@Validated
public class MyAppProperties {

  @AssertTrue
  @NotNull
  private Boolean active;

  public Boolean getActive() {
    return active;
  }

  public void setActive(Boolean active) {
    this.active = active;
  }

}

note: you can leave out @ConditionalOnProperty(name = "myapp.active")

use @AssertTrue in combination with @NotNull because of @AssertTrueconsiders null elements as valid.

and spring-boot generates a nice error-message for free:

***************************
APPLICATION FAILED TO START
***************************

Description:

Binding to target org.springframework.boot.context.properties.bind.BindException: Failed to bind properties under 'myapp' to stackoverflow.demo.MyAppProperties failed:

    Property: myapp.active
    Value: false
    Origin: class path resource [application.properties]:1:16
    Reason: must be true


Action:

Update your application's configuration

EDIT (after updated question)


A faster way: your application won't start, nor an application-context will be loaded

package stackoverflow.demo;

import org.springframework.beans.factory.config.YamlPropertiesFactoryBean;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.core.io.ClassPathResource;

@SpringBootApplication
public class FastFailWhenPropertyNotPresentApplication {

  static Boolean active;

  static {

    YamlPropertiesFactoryBean yaml = new YamlPropertiesFactoryBean();
    yaml.setResources(new ClassPathResource("application.yaml"));

    active = (Boolean) yaml.getObject().getOrDefault("myapp.active", false);

  }


    public static void main(String[] args) {
        if (!active) {
          System.err.println("your fail message");
        } else {
          SpringApplication.run(FastFailWhenPropertyNotPresentApplication.class, args);
        }
    }

}

EDIT

another solution that probably fits your needs best...

By listening to the ApplicationEnvironmentPreparedEvent

Event published when a {@link SpringApplication} is starting up and the * {@link Environment} is first available for inspection and modification. *

note: you cannot use a @EventListener, but you have add the Listener to the SpringApplication

package stackoverflow.demo;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.context.event.ApplicationEnvironmentPreparedEvent;
import org.springframework.context.ApplicationListener;

@SpringBootApplication
public class FastFailWhenPropertyNotPresentApplication {


  static class EnvironmentPrepared implements ApplicationListener<ApplicationEnvironmentPreparedEvent>{
    @Override
    public void onApplicationEvent(ApplicationEnvironmentPreparedEvent event) {
      Boolean active = event.getEnvironment().getProperty("myapp.active",Boolean.class,Boolean.FALSE);
      if(!active) {
        throw new RuntimeException("APPLICATION FAILED TO START: ACTIVE SHOULD BE TRUE ");
      }
    }
  };


  public static void main(String[] args) throws Exception {
    SpringApplication springApplication = new SpringApplication(FastFailWhenPropertyNotPresentApplication.class);
    springApplication.addListeners(new FastFailWhenPropertyNotPresentApplication.EnvironmentPrepared());
    springApplication.run(args);
  }

}

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=113436&siteId=1
Recommended