Monday, April 25, 2005

Someone on this Javalobby post asked me to elaborate on our use of Ant and copy filtering as a build configuration mechanism, so here it is.

The problem: projects tend to have lots of different configuration files that need to be changed based on the machine or circumstance in which they are installed. Local file paths, database passwords, and host names are just a few of the settings that typically need to be set. Frequently, the same value appears in multiple configuration files allowing for the problems always associated with dual-maintenance. Also, it's often inconvenient to locate all the configuration files in the same place, so a user (a developer or configuration engineer) needs to know where they all live in order to configure them (web.xml lives in WEB-INF, log4j.xml lives in the classpath, etc.)

The solution: We use Ant to solve this problem. To address the dual-maintenance issue, we have a single file that a user needs configure called that lives in the same directory as the build.xml file. It's typically copied from another file called that lives in CVS. Any values in override those in


In our Ant build process, we have tasks that handle configuring all of our various files. They look like this:

<target name="setupWebXml">

<copy overwrite="true"
<filterset refid="build.propertiesFilter" />
<filterset refid="" />


The filtersets are defined like this:

<!-- The following two filter sets are referenced in each of the following config targets.
First the tokens are replaced, then the
tokens are replaced (only if they didn't already exist in -->
<filterset begintoken="{" endtoken="}" id="build.propertiesFilter"
description="Used to parse tokens in config files into their associated values in">
<filtersfile file=""/>

<filterset begintoken="{" endtoken="}" id=""
description="Used to parse tokens in config files into their associated values from">
<filtersfile file=""/>

Here's a snippet of the web.xml-template file that becomes web.xml:


That's an artificial example for the purposes of this article; there are better ways to get the hostname, if you should be getting it at all.

Tip: At the top of each template file, put in a note (we actually have a token that also gets replaced) that warns users that the file has been generated and they should not edit it. It's very frustrating to make changes to a file and find they don't take effect because they've been clobbered by the build process.


Anonymous said...

very helpful thanks = )

KC Baltz said...

Glad it's helpful.

I'd forgotten about this post and feel I should update indicating I have what I feel is a better solution for this now, at least for web applications.

These days, I avoid building an environment-specific WAR by externalizing all those properties to a database. We have a table that is keyed by ApplicationID and Environment (Dev, QA, Beta, Prod). Then, in our application container (Tomcat, Resin, etc.) you configure your application with those keys (using JNDI) and it looks up the rest with a database query. We override a Spring Reloadable Message Bundle to look these up with priority over any .properties files, letting us access these properties like messages, which is easy to do.