javaspringspring-boot

Spring Boot read config value from file


I have the following Spring Boot (3.3.0) config.yaml:

spring:
  datasource:
    appname: ${DOCUMENT_ID_DATABASE_APPNAME:DocumentIdService}
    driver-class-name: org.postgresql.Driver
    url: jdbc:postgresql:characterEncoding=UTF8&rewriteBatchedStatements=true
    username: ${DOCUMENT_ID_DATABASE_USERNAME:postgres}
    password: ${DOCUMENT_ID_DATABASE_PASSWORD:password}

Instead of password being read from the DOCUMENT_ID_DATABASE_PASSWORD env var, I would instead like the password to be read by the file pointed to by the env var, for example if DOCUMENT_ID_DATABASE_PASSWORD_FILE=/tmp/passwordfile, then that would mean:

password: // Set to content of /tmp/passwordfile, or if /tmp/passwordfile does not exist, default to "mypassword"

Solution

  • The full solution is:

    import java.io.IOException;
    
    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.env.EnvironmentPostProcessor;
    import org.springframework.core.env.ConfigurableEnvironment;
    import org.springframework.core.env.PropertySource;
    
    public final class SecretPropertyResolver implements EnvironmentPostProcessor
    {
        private static final String SECRET_PROPERTY_PREFIX = "secret.";
    
        @Override
        public void postProcessEnvironment(final ConfigurableEnvironment environment, final SpringApplication application)
        {
            environment.getPropertySources().addFirst(new PropertySource<Object>("SecretPropertyResolver")
            {
                @Override
                public Object getProperty(String name)
                {
                    // Assuming:
                    //   ${secret.MY_PASSWORD:password}
                    // This will get called twice:
                    //   Once with: secret.MY_PASSWORD:password
                    //   Once with: secret.MY_PASSWORD
                    // So we need to check for absence of the colon before continuing
                    if (name.startsWith(SECRET_PROPERTY_PREFIX) && !name.contains(":")) {
                        final String key = name.substring(SECRET_PROPERTY_PREFIX.length());
                        try {
                            // Logic to read from env/file here
                        } catch (final IOException e) {
                            throw new RuntimeException(String.format("Unable to read secret '%s'", key), e);
                        }
                    }
                    return null;
                }
            });
        }
    }
    

    src/main/resources/META-INF/spring-factories

    org.springframework.boot.env.EnvironmentPostProcessor=com.example.SecretPropertyResolver
    

    application.yaml

    password: ${secret.MY_PASSWORD:password}