linuxNoob
linuxNoob

Reputation: 720

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?

Upvotes: 1

Views: 3646

Answers (2)

Alexander Pavlov
Alexander Pavlov

Reputation: 2210

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}

Upvotes: 3

munzld
munzld

Reputation: 279

Instead of reading txt files you could make environment variables and use placeholders in your application.properties to reference them:

spring.datasource.username: ${USERNAME}
spring.datasource.password: ${PASSWORD}

You can read more about placeholders here.

Upvotes: 3

Related Questions