Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Spring Boot - Detect and terminate if property not set?

Is there any way for a Spring Boot web application to abort at startup if a required property is not set anywhere (neither in the application.properties file nor the other property sources)? Right now, if the property is included in another property, it seem that Spring Boot simply avoids substitution.

For example, in my application.properties file, I have the line:

quartz.datasource.url=jdbc:hsqldb:${my.home}/database/my-jobstore

Right now, if "my.home" is not set elsewhere, Spring Boot is setting the url literally to "jdbc:hsqldb:${my.home}/database/my-jobstore" (no substitution).

I would like to have the application fail to start if the property my.home were not set anywhere else.

like image 463
David H Avatar asked Jul 21 '15 00:07

David H


2 Answers

To throw a friendly exceptions just put a default null value in property, check and throw a exception in afterProperty method.

@Component
public static class ConfigurationGuard implements InitializingBean {

@Value("${my.home:#{null}}")
private String myHomeValue;

public void afterPropertiesSet() {
    if (this.myHomeValue == null or this.myHomeValue.equals("${my.home}") {
          throw new IllegalArgumentException("${my.home} must be configured");
    }
 }
}
like image 92
Fabio Mazzo Avatar answered Oct 15 '22 22:10

Fabio Mazzo


Although they work, I think the approach in the foremost answer is somewhat brittle, as it only works for the predefined name(s), and will silently stop checking the when someone changes quartz.datasource.url in the configs to use a different expansion.

Ideally, I want this value of ignoreUnresolvablePlaceholders to be false to get wholesale expansion checking when parsing my configs such as application.properties or its YAML variants, but it's hard-coded to true for these cases. This unfortunately leaves strings such as ${FOO} in its unexpanded form if FOO cannot be found, making troubleshooting extremely painful. This is especially the case for fields that don't readily appear in the logs such as passwords.

While I couldn't find a way of changing ignoreUnresolvablePlaceholders short of modifying Spring Boot's classes, I did find an alternative of using a custom PropertySource implementation and defining a new syntax such as "${!FOO}" to indicate FOO must exist as an environment variable or die. (The OP didn't mention whether my.home is an environment variable but the code below is for environment variables.)

First, an EnvironmentPostProcessor implementation is required for registering the custom PropertySource. This StrictSystemEnvironmentProcessor.java does this as well as holds the implementation of the custom PropertySource:

package some.package;

@Order(Ordered.LOWEST_PRECEDENCE)
class StrictSystemEnvironmentProcessor implements EnvironmentPostProcessor {

    private static final String PROPERTY_SOURCE_NAME = "STRICT_" + StandardEnvironment.SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME;

    @Override
    public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) {
        if (environment.getPropertySources().contains(PROPERTY_SOURCE_NAME)) {
            return;
        }

        SystemEnvironmentPropertySource delegate = (SystemEnvironmentPropertySource)environment.getPropertySources()
                .get(StandardEnvironment.SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME);

        environment.getPropertySources().addLast(new StrictSystemEnvironmentPropertySource(delegate));
    }

    private static class StrictSystemEnvironmentPropertySource extends SystemEnvironmentPropertySource {

        public StrictSystemEnvironmentPropertySource(SystemEnvironmentPropertySource delegate) {
            super(PROPERTY_SOURCE_NAME, delegate.getSource());
        }

        @Override
        public Object getProperty(String name) {
            if (name.startsWith("!")) {
                String variableName = name.substring(1);
                Object property = super.getProperty(variableName);
                if (property != null) {
                    return property;
                }

                throw new IllegalStateException("Environment variable '" + variableName + "' is not set");
            }
            return null;
        }
    }

}

Instead of returning null, an exception is thrown for names that start with !.

This META-INF/spring.factories is also required so that Spring initializes our EnvironmentPostProcessor:

org.springframework.boot.env.EnvironmentPostProcessor=some.package.StrictSystemEnvironmentProcessor

Then henceforth, I can write all environment variables substitutions in my configs as ${!FOO} to get strict existance checking.

like image 22
antak Avatar answered Oct 15 '22 22:10

antak