0

I am loading my properties file as following:

@Configuration
@PropertySource("classpath:app.properties")
class MyApp {
    @Bean
    public PropertySourcesPlaceholderConfigurer PropertySourcesPlaceholderConfigurer() {
        return new PropertySourcesPlaceholderConfigurer();
    }
}

In the properties file, I have several database-related properties:

database.dataSource.url=jdbc:postgresql://localhost:${db-port:5432}/mydb
database.dataSource.x=...
database.dataSource.y=...
database.dataSource.z=...

Note:

  • ${db-port} should be replaced by either the value of property/environment variable db-port or 5432. In my case, I am defining the environment variable db-port when spawning the Tomcat container.

  • All database-related properties are grouped under database. root. This is intentional, see below.

I want to avoid that I have to enumerate/hardcode all possible database-related properties in my code. Luckily, the database layer in use (Hikari) has the nice feature that I can pass all properties via a java.util.Properties. So, I want retrieve all defined properties under database.* and simply forward it to Hikari.

For this, I wrote the following utility:

@Component
public class PropertyFetcher
{
    @Autowired
    private ConfigurableEnvironment environment;

    public Properties get(final String key) {
        final Properties p = new Properties();

        for (final PropertySource<?> s : environment.getPropertySources()) {
            if (s instanceof EnumerablePropertySource) {
                for (final String k : ((EnumerablePropertySource) s).getPropertyNames()) {
                    if (k.startsWith(key) && k.length() > key.length()) {
                        p.put(k.substring(key.length()), s.getProperty(k));
                    }
                }
            }
        }
        return p;
    }
}

Now, when calling get("database."), I have all database-related properties as defined in the properties file. Great! But, the value for property dataSource.url is now

jdbc:postgresql://localhost:${db-port:5432}/mydb

instead of

jdbc:postgresql://localhost:9876/mydb

So, for some reason, the ${db-port:5432} is not resolved (yet?) when going via this route (ConfigurableEnvironment).

How can this be fixed? Or is there a better way to get all properties under a certain root without having to enumerate/hardcode them into the code?

Please note that in the default scenario, the ${db-port:5432} in property database.dataSource.url=jdbc:postgresql://localhost:${db-port:5432}/mydb is correctly resolved. I tested this by defining the following member and logging it:

@Value("${database.dataSource.url}")
final String url; // holds jdbc:postgresql://localhost:9876/mydb
Jessy56
  • 63
  • 1
  • 10

1 Answers1

2

You should read the property values from real environment only. Then only you will get actual or effective value of a property.

This will require a little change in your code.

change this line:

p.put(k.substring(key.length()), s.getProperty(k));

to this:

p.put(k.substring(key.length()), environment.getProperty(k));
Sachin Gupta
  • 7,805
  • 4
  • 30
  • 45
  • Great, this fixed it! I already had the environment (`ConfigurableEnvironment`), so I just called `environment.getProperty(k)` and it works! Or should there be a good reason to inject `Environment` (not `ConfigurableEnvironment`) separately? – Jessy56 Oct 25 '17 at 10:09
  • ConfigurableEnvironment extends Environment. So it is good to have only one injection. thanks for that point. I will update the answer accordingly. – Sachin Gupta Oct 25 '17 at 10:12
  • Great! Thanks a lot. – Jessy56 Oct 25 '17 at 10:29