I want to use properties files that use values from environment variables. The Apache Commons Configuration library provides Variable Interpolation
But regardless what I try, I do not see this happen. For testing I created a small main.properties
file:
propertyA=first
propertyB=${propertyA}
system.path=${env:PATH}
base.prop = /base
first.prop = ${base.prop}/first
second.prop = ${first.prop}/second
Then I created a small application to read this configuration:
import java.io.File;
import java.io.FileReader;
import org.apache.commons.configuration2.Configuration;
import org.apache.commons.configuration2.ConfigurationUtils;
import org.apache.commons.configuration2.FileBasedConfiguration;
import org.apache.commons.configuration2.PropertiesConfiguration;
import org.apache.commons.configuration2.builder.ConfigurationBuilderEvent;
import org.apache.commons.configuration2.builder.FileBasedConfigurationBuilder;
import org.apache.commons.configuration2.builder.fluent.Configurations;
import org.apache.commons.configuration2.builder.fluent.Parameters;
import org.apache.commons.configuration2.event.ConfigurationEvent;
import org.apache.commons.configuration2.ex.ConfigurationException;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
public class ConfigTest {
private static final Logger log = LogManager.getLogger();
private static Configuration load1(String filename) throws ConfigurationException {
Configurations configs = new Configurations();
// Read data from this file
File propertiesFile = new File(filename);
PropertiesConfiguration config = configs.properties(propertiesFile);
return config;
}
private static Configuration load2(String filename) throws ConfigurationException {
Parameters params = new Parameters();
FileBasedConfigurationBuilder<FileBasedConfiguration> builder =
new FileBasedConfigurationBuilder<FileBasedConfiguration>(PropertiesConfiguration.class)
.configure(params.properties()
.setFileName(filename));
Configuration result = builder.getConfiguration();
return result;
}
private static Configuration load3(String filename) throws ConfigurationException {
Parameters params = new Parameters();
params.properties().setIncludesAllowed(true);
FileBasedConfigurationBuilder<FileBasedConfiguration> builder =
new FileBasedConfigurationBuilder<FileBasedConfiguration>(PropertiesConfiguration.class)
.configure(params.properties()
.setFileName(filename));
builder.addEventListener(ConfigurationBuilderEvent.ANY, (t) -> {
log.warn("configurationBuilderEvent {}", t);
});
Configuration result = builder.getConfiguration();
return result;
}
public static void main(String[] args) throws ConfigurationException {
Configuration result = load3("data/main.properties");
ConfigurationUtils.dump(result, System.out);
}
}
But regardless of what I try, my output always looks like this:
propertyA=first
propertyB=${propertyA}
system.path=${env:PATH}
base.prop=/base
first.prop=${base.prop}/first
second.prop=${first.prop}/second
I would have expected properties to be resolved against each other and the environment.
How can this be achieved with Commons Config?
Hope this will help you out :) The “problem” isn’t that Commons Configuration isn’t working – it’s that its interpolation and include‐handling work in a somewhat “lazy” fashion. In other words, they aren’t applied when you simply dump the configuration’s raw properties. Here are the key points:
Lazy Variable Interpolation: Commons Configuration does not replace variables when you iterate over or dump the configuration. Instead, it performs interpolation when you actually retrieve a value (e.g. by calling getString("propertyB")). So if you use something like ConfigurationUtils.dump(config, System.out), you’ll see the unresolved placeholders (e.g. ${propertyA}, ${env:PATH}) even though if you call config.getString("propertyB") you may get the resolved value. Environment Variables: Out of the box, Commons Configuration 2.x registers several lookups (including system properties and, if available, the environment – typically under the key "env") so that you can refer to environment variables as ${env:PATH}. (Note, however, that in some versions you might have to register an EnvironmentLookup explicitly.) In your case, the “env:” variable isn’t replaced in the dump because interpolation happens on access. Includes: When you enable includes (by calling, for example,
params.properties().setIncludesAllowed(true);
) the builder will process any import= or include= lines in your properties file. However, if the included file isn’t found, it does not necessarily throw an exception immediately – it will log a warning. (There isn’t a “fail‐fast” mode by default.) Again, the inclusion is processed internally during the load/build phase, so the “import” key remains in the configuration map. What to Do To “see” the effect you expect you must actually retrieve the property values rather than dumping the configuration. For example:
Configuration config = load3("data/main.properties");
System.out.println("propertyB: " + config.getString("propertyB"));
System.out.println("first.prop: " + config.getString("first.prop"));
This will show you that propertyB is resolved to the value of propertyA and that interpolated properties are processed. (Similarly, if you call getString("system.path"), you should see your environment’s PATH, assuming that lookup is available.)
In Summary Includes: Enable them with setIncludesAllowed(true). Missing files will simply log a warning instead of throwing an exception by default. Interpolation: Variables (including those that refer to other properties and environment variables) are resolved lazily when you call a getter (e.g. getString()). Don’t expect ConfigurationUtils.dump() to show the substituted values. Environment Variables: The ${env:PATH} syntax will work at retrieval time (if your version registers an environment lookup by default or if you register one manually). Thus, to “achieve” what you want with Commons Configuration, simply make sure you configure the builder to allow includes and then use the configuration’s getter methods (or register additional lookups if needed) to get the resolved values.
Happy configuring!