Search code examples
javaspringspring-bootapplication.properties

Spring Boot - configuration not being read?


I am using springboot 2.0.4.RELEASE and am trying to configure database properties for my app. I added a new configuration file as follows:

@Configuration
@ConfigurationProperties(prefix="spring.datasource")
public class DatabaseConfig 
{
    private String userName;
    private String password;
    
    public String getUserName() {
        try {
            userName = Files.readAllLines(Paths.get("/secrets/username.txt")).get(0);
        } catch (IOException e) {
            e.printStackTrace();
        }
        return userName;
    }
    public void setUserName(String userName) {
        this.userName = userName;
    }
    public String getPassword() {
        try {
            password = Files.readAllLines(Paths.get("/secrets/password.txt")).get(0);
        } catch (IOException e) {
            e.printStackTrace();
        }
        return password;
    }
    public void setPassword(String password) {
        this.password = password;
    }
}

Here's my application.properties

spring.datasource.url=jdbc:sqlserver://mno35978487001.cloud.xyz.com:14481
spring.datasource.driver-class-name=com.microsoft.sqlserver.jdbc.SQLServerDriver
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.SQLServer2012Dialect
spring.jpa.show-sql=true
spring.jpa.hibernate.naming.physical-strategy=org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl

I was hoping when the application runs, application.properties will be able to read the user name and password from the file but that doesn't seem to happen. Any thoughts on what I am missing? Are there any better ways of reading those values and setting them dynamically?


Solution

  • You can add custom property source locator.

    https://source.coveo.com/2018/08/03/spring-boot-and-aws-parameter-store/

    Basically, you need

    1. Create your property source
        public class SecretStorePropertySource extends PropertySource<SecretStoreSource> {
        
            public SecretStorePropertySource(String name) {
                super(name);
            }
        
            @Override
            public Object getProperty(String name) {
                if (name.startsWith("secrets.")) {
                    // converts property name into file name, 
                    // e.g. secrets.username -> /secrets/username.txt
                    return Files.readAllLines(Paths.get("/"+name.replace('.','/')+".txt")).get(0);           
                }
                return null;
            }
        }
    
    1. Register it on start using custom environment post processor
        public class SecretStorePropertySourceEnvironmentPostProcessor implements EnvironmentPostProcessor {
        
            @Override
            public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) {
                environment.getPropertySources()
                        .addLast(new SecretStorePropertySource("customSecretPropertySource",
                                new SecretStorePropertySource()));
            }
        }
    
    1. Tell SpringBoot to use your custom environment post processor

    Add to src/main/resources/META-INF/spring.factories file

    org.springframework.boot.env.EnvironmentPostProcessor=com.mycompany.myproduct.SecretStorePropertySourceEnvironmentPostProcessor
    
    1. Use secrets. prefix to pass property through your source
    spring.datasource.username=${secrets.username}
    spring.datasource.password=${secrets.password}
    

    However, if you are going to provide it via file then just rename this file to application-secrets.yml and place on target server into config folder which itself is located in the same folder where application jar copied.

    Now you can ask SpringBoot to load it by adding secrets into active profiles list.

    https://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#boot-features-external-config-profile-specific-properties

    https://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#boot-features-adding-active-profiles

    https://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#howto-set-active-spring-profiles


    Not saying I'm advising it but you also can make your code working

    @Configuration
    public class Secrets {
        @Bean("SecretUserName")
        public String getUserName() {
            try {
                return Files.readAllLines(Paths.get("/secrets/username.txt")).get(0);
            } catch (IOException e) {
                throw new IllegalStateException(e);
            }
        }
    
        @Bean("SecretUserPassword")
        public String getPassword() {
            try {
                return Files.readAllLines(Paths.get("/secrets/password.txt")).get(0);
            } catch (IOException e) {
                throw new IllegalStateException(e);
            }
        }
    }
    

    and adding following lines to application.yml

    spring.datasource.username=#{@SecretUserName}
    spring.datasource.password=#{@SecretUserPassword}