Sunday, April 5, 2015

A way to read properties with variable interpolation

Recently, I tried to define and read a global properties in an application server. The benefit of such property configured in the application server - it can be shared across all web applications that are deployed on this server. Every deployed application can read the same property which is configured just once at one place. What I tried to do was a system property with another system property in the value part. In the application server JBoss / WildFly, you can e.g. define a system property in the configuration file standalone.xml. I set the property exporting.service.config.file.
<system-properties>
    <property name="exporting.service.config.file" value="${jboss.server.config.dir}\exporting\exporting-service.properties"/>
</system-properties>
jboss.server.config.dir points to the base configuration directory in JBoss. This property is set automatically by JBoss. In this example, we have a so-called Variable Interpolation. The definition from the Wikipedia: "Variable interpolation (also variable substitution or variable expansion) is the process of evaluating a string literal containing one or more placeholders, yielding a result in which the placeholders are replaced with their corresponding values". Another example for placeholders ${...} in property value would be the following configuration:
application.name=My App
application.version=2.0
application.title=${application.name} ${application.version}
When we now try to get the system property from the first example with Java's System.getProperty(...)
 
String globalConfigFile = System.getProperty("exporting.service.config.file");
 
we will get the value ${jboss.server.config.dir}\exporting\exporting-service.properties. The placeholder ${jboss.server.config.dir} is not resolved. There are the same troubles in the second example as well.

What would be the simplest way to read properties with variable interpolation? Well, there is the Spring Framework with PlaceholderConfigurerSupport and so on. But it is an overhead to have such big framework as dependency. Is there a lightweight library? Yes, sure - Apache Commons Configuration. Apache Commons Configuration provides special prefix names for properties to evaluate them in a certain context. There are for instance:
  • sys: This prefix marks a variable to be a system property. Commons Configuration will search for a system property with the given name and replace the variable by its value.
  • const: The prefix indicates that a variable is to be interpreted as a constant member field of a class. The name of the variable must be fully qualified class name.
  • env: The prefix references OS-specific environment properties.
 Some examples from the documentation:
user.file = ${sys:user.home}/settings.xml
action.key = ${const:java.awt.event.KeyEvent.VK_CANCEL}
java.home = ${env:JAVA_HOME}
Now, I could add the needed dependency to my Maven project
<dependency>
    <groupId>commons-configuration</groupId>
    <artifactId>commons-configuration</artifactId>
    <version>1.10</version>
</dependency>
set the prefix sys: before jboss.server.config.dir
<system-properties>
    <property name="exporting.service.config.file" value="${sys:jboss.server.config.dir}\exporting\exporting-service.properties"/>
</system-properties>
and write the following code
import org.apache.commons.configuration.SystemConfiguration;

...

SystemConfiguration systemConfiguration = new SystemConfiguration();
String globalConfigFile = systemConfiguration.getString("exporting.service.config.file");
...
The String globalConfigFile on my notebook has the value C:\Development\Servers\jboss-as-7.1.1.Final\standalone\configuration\exporting\exporting-service.properties. The prefix sys: marks a variable to be a system property. Commons Configuration will search for a system property with the given name and replace the variable by its value. The complete code:
import org.apache.commons.configuration.ConfigurationException;
import org.apache.commons.configuration.PropertiesConfiguration;
import org.apache.commons.configuration.SystemConfiguration;

...

PropertiesConfiguration propertiesConfiguration = new PropertiesConfiguration();
SystemConfiguration systemConfiguration = new SystemConfiguration();
String globalConfigFile = systemConfiguration.getString("exporting.service.config.file");
if (globalConfigFile != null) {
    try {                
        propertiesConfiguration.setDelimiterParsingDisabled(true);                
        propertiesConfiguration.load(globalConfigFile);
    } catch (ConfigurationException e) {
        LOG.log(Level.INFO, "Cannot read global properties");
    }            
}
Any single property can be read e.g. as
propertiesConfiguration.getString("someKey")
propertiesConfiguration.getString("someKey", someDefaultValue)
propertiesConfiguration.getBoolean("someKey")
propertiesConfiguration.getBoolean("someKey", someDefaultValue)
propertiesConfiguration.getInteger("someKey")
propertiesConfiguration.getInteger("someKey", someDefaultValue)
usw. That's all. Let me know if you know another simple ways to read properties with variable interpolation.

1 comment:

  1. Among those posts I've seen, this is the most particular one, and I think the blogger must have spent lots of time on it, thank you so much!

    yatiken software

    ReplyDelete

Note: Only a member of this blog may post a comment.