2013/06/05

Resolving Properties With Spring

It is a well known (and not surprising at all) fact, that you can use properties in Spring. You can reference them in you application context configuration file(s), or by the new Spring 3 @Value annotation.

In the XML config the reference to the property named "x.y.z" this simple: 
<bean name="theBean" class="myBean">
  <property name="parameter" value="${x.y.z}" /> 
</bean>

This is usually accompanied by the element from context namespace setting the name of the configuration file:
<context:property-placeholder location="classpath:app.properties"/>

This is good approach for simple scenarios. In a more complex setup things can get a bit unwieldy. 

First of all each occurrence of <context:property-placeholder> element causes creation of a new instance of the class responsible for property names resolving - PropertyPlaceholderConfigurer instance. You can enjoy lot of fun with unresolved placeholders in your application, once the library you're using brings its application context configuration containing <context:property-placeholder>. Even if you set ignoreUnresolvablePlaceholders=false for the element, it does not change the fact you have multiple PropertyPlaceholderconfigurers where you need only one.

You may want to the properties loaded by Spring to a Spring unaware class or further customize the property resolving. In such case you'll create a custom PropertyPlaceholderConfigurer ... and get more collisions with default ones created by the <context:property-placeholder>. 

Custom PropertyPlaceholderConfigurer


The solution is simple, remove all the occurrences of <context:property-placeholder> and create a custom PropertyPlaceholderConfigurer, that will fulfill your desires. Example of one is below. The static method getProperty() can serve properties to Spring unaware code.

import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.config.PropertyPlaceholderConfigurer;
import java.util.*;

class MyPropertyResolver extends PropertyPlaceholderConfigurer
{

    private static Map<String, String> propertiesMap = new HashMap<String, String>();

    @Override
    protected void processProperties
    (ConfigurableListableBeanFactory beanFactory, Properties properties)
    {
        super.processProperties(beanFactory, properties);
        for ( Object propertyKey : properties.keySet())
        {
            propertiesMap.put
            (
               propertyKey.toString(),
               // Spring 3.x
               resolvePlaceholder(propertyKey.toString(), properties)
               // for Spring 2.5 :
               // parseStringValue(propertyKey.toString(), properties, Collections.EMPTY_SET)
            );
        }
    }

    public static String getProperty(String name)
    {
        return propertiesMap.get(name);
    }
}
 
Following configuration of bean named appProperties provides setup for multiple property files - the last one is the application property file overriding default values provided by libraries. Option ignoreResorceNotFound=true ensures that the missing files are not taken for an error.
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
                           http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">

    <!-- load it early, provides properties to other classes -->
    <bean id="appProperties" class="MyPropertyResolver">
        <property name="ignoreResourceNotFound" value="true"/>
        <property name="locations">
            <list>
                <!-- Ordering matters, properties in the later loaded files 
                     override values from the previous files -->
                <value>classpath:lib.properties</value>
                <value>classpath:app.properties</value>
            </list>
        </property>
    </bean>

</beans>

Adding to an Application Context


The last thing you need to do to make it working is to put the bean into your application context and use it. Following setup for a web application ensures that the bean is instantiated ASAP during application context initialization.
<web-app xmlns="http://java.sun.com/xml/ns/j2ee" version="2.4" 
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
         xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee 
                             http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd"> 

  <context-param> 
    <param-name>contextConfigLocation</param-name> 
    <param-value> 
      classpath:spring-resources.xml 
      ... 
    </param-value> 
  </context-param> 

</web-app>

For command-line application it's very simple too:
ClassPathXmlApplicationContext appContext = new ClassPathXmlApplicationContext(
                                            new String[] {"spring-resources.xml"});
MyPropertyResolver resolver = (MyPropertyResolver) appContext.getBean("appProperties");