Using Noah to Configure Properties in your Spring Container

Posted by Tejus Parikh on October 7, 2011

On a recent trip through our code base, I realized that there were multiple projects that used identical properties defined in files that sat on the file system. I find DRY is a superior pattern to copy and paste I wanted to consolidate these into a single location.

One approach would be to move them up a level in the source stack, and either rely on the properties being included in a jar or included via the build process. Both of these approaches suffer from some drawbacks and require rebuilds on a config change. Also the properties are still located all over the place, making it difficult to recover from mistakes or handle changes to the environment.

One of my co-workers, John Vincent, is currently developing Noah which is a lightweight node/service registry. However, there's nothing that says you couldn't use it as a centralized location for properties files.

I uploaded my properties file using Noah's Ephemeral API. From there, it was a very trivial to make the properties available to Spring:

<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
    <property name="ignoreResourceNotFound" value="false"></property>
    <property name="locations">
        <list>
            <value>http://noah.domain/path/to/file</value>
        </list>
    </property>
</bean>

However, I didn't like the url hardcoded in the configuration file. I'd prefer to have them configured through some properties that do live on the filesystem. In effect, each server would just need to know where to go to configure themselves. This required more advanced Spring trickery. Since all PropertyPlaceholderConfigurer instances implement PriorityOrder, they will be implemented before any property expansion has become. Therefore, you have to create your own Ordered bean that wraps a PropertyPlaceholderConfigurer. The following code is adapted from this discussion forum.

public class OrderedPropertyPlaceholderConfigurator implements Ordered, BeanFactoryPostProcessor {
    private Resource[] locations;
    private String systemPropertiesModeName;
    private Boolean ignoreResourceNotFound;
    private String propertyPlaceholderPrefix;
    private String propertyPlaceholderSuffix;

    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
        PropertyPlaceholderConfigurer configurer = new PropertyPlaceholderConfigurer();
        configurer.setLocations(locations);

        if(null != systemPropertiesModeName) {
            configurer.setSystemPropertiesModeName(systemPropertiesModeName);
        }

        if(null != ignoreResourceNotFound) {
            configurer.setIgnoreResourceNotFound(ignoreResourceNotFound);
        }

        if(null != propertyPlaceholderPrefix) {
            configurer.setPlaceholderPrefix(propertyPlaceholderPrefix);
        }

        if(null != propertyPlaceholderSuffix) {
            configurer.setPlaceholderSuffix(propertyPlaceholderSuffix);
        }

        configurer.postProcessBeanFactory(beanFactory);
    }

    /***********
     * Setters would go here
     ***********/

    @Override
    public int getOrder() {
        // Needs to run first, so order is 0
        return 0;
    }
}

Then you configure it in your applicationContext.xml file like the following:

<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
    <property name="ignoreResourceNotFound" value="false"></property>
    <property name="ignoreUnresolvablePlaceholders" value="true"></property>
    <property name="placeholderPrefix" value="@{"></property>
    <property name="placeholderSuffix" value="}"></property>
    <property name="locations">
        <list>
            <value>classpath:env.properties</value>
        </list>
    </property>
</bean>
<bean id="noahUrl" class="java.net.URL">
    
    <constructor-arg value="http"></constructor-arg>
    <constructor-arg value="@{noah.host}"></constructor-arg>
    <constructor-arg value="@{noah.port}"></constructor-arg>
    <constructor-arg value="@{noah.env.path}"></constructor-arg>
</bean>

<bean id="noahResource" class="org.springframework.core.io.UrlResource">
    <constructor-arg ref="noahUrl"></constructor-arg>
</bean>

<bean class="com.tejusparikh.spring.OrderedPropertyPlaceholderConfigurator">
    <property name="ignoreResourceNotFound" value="false"></property>
    <property name="systemPropertiesModeName" value="SYSTEM_PROPERTIES_MODE_OVERRIDE"></property>
    <property name="locations">
        <list>
            <ref bean="noahResource"></ref>
        </list>
    </property>
</bean>

Now, you just have to manage one file on the filesystem that points to where the rest of the application's configs can be found.

Related Posts:

Tejus Parikh

Tejus is an software developer, now working at large companies. Find out when I write new posts on twitter, via RSS or subscribe to the newsletter: