Configuring Spring depending on the deployment environment

June 30, 2007

The central idea in Spring is to promote loose coupling and avoiding to tie the application code to the environment it is going to be deployed in. For example, whether your DAO’s database connection is retrieved from JNDI our is coming from a driver directly is neatly configured in the application context without the DAO having to be aware of this. A common entry in the application context file is:

<bean id="datasource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver" />
<property name="url" value="jdbc:mysql://localhost/my_db" />
<property name="username" value="my_name" />
<property name="password" value="my_pwd" />
<bean>

This configures a datasource bean that is later going to be injected a number of DAO beans. All the DAO has to know is that it operates on some kind of datasource. It doesn’t need to concern itself where the datasource is coming from.

This is very well during development, but at a certain moment the application is going to be deployed in a different environment. At that time some of the configuration of the application likely needs changes. In our example the database username and password are probably different in acceptance and production. Also some input or output file can be in a different location or some thresholds are different, etc. However, Since those settings are in a Spring configuration file that is typically deployed inside the application jar or war, this would require making a special build for each deployment environment. People are doing this, but it’s not an ideal situation in my opinion.

Luckily, Spring has a PropertyPlaceholderConfigurer that enables us to get rid of the exact values of properties (and other string literals). It enables us to convert the above fragment to:

<bean id="datasource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="${db.driver}" />
<property name="url" value="${db.url}" />
<property name="username" value="${db.username}" />
<property name="password" value="${db.password}" />
</bean>

The support for this is enabled by adding a bean definition like this:

<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="location" value="
classpath:env.properties"/>
</bean>

We have now gained that the database configuration is read from a property file in the classpath. However, that really doesn’t bring all that much closer to our goal of avoiding environment specific builds. We still have to put the property file on the classpath, thus presumably still in some deployed resource, if not the original jar/war.

A bit better is to use the file: prefix as in value="file:/etc/apps/myapp/env.properties". If you have a dedicated machine for each environment and all use the same directories, this might work. If there’s an indication of the environment in the path or the paths in the development is widely different in development (a windows laptop for example) and production (a 16-way Sun box perhaps) this still won’t be good enough.

Luckily there’s a somewhat hidden feature of Spring property values. I don’t know in what Spring version it was introduced and I can’t find it the documentation but if you use an attribute value of the form ${value}, Spring will look up value as a system property. Although this has the same syntax as the PropertyPlaceholderConfigurer, it follows a completely different code path. Anyway, if we set the location property to file:${myapp_config_basepath}/env.properties and define a system variable myapp_config_basepath on the VM that is hosting the application, we achieved what we were striving for: a single jar/war that can be deployed unchanged in all application environments and that still can be configured based on a property file that is located wherever the system administrator fancies. There may be other application specific ways of achieving this, but the above solution has the merit of working everywhere (if the security manager is not being unusually strict).