Monday, November 12, 2007

Centralising enviroment variables in resource files

It's always a pain to manage different resource files, which most of times, share non-environmental properties with environmental dependent properties. Keeping one copy of each file per environment is not always a good option, for the following reasons:

  • To create a new environment, it's needed to go through all files looking for environment dependent ones.
  • Changes to environment dependent files need to be in sync with all different environment
  • Duplication of the same properties in different resource files

One solution of this problems is to parametrise the environment dependent values into a central and single file, which is then processed using Ant (or other tool if you prefer).

In this article, Pete Eakle, shows how to use Ant to replace tokens in css files and resource files.

I think it was missing what Ant replacement can do best, to separate the environmental dependencies in one central file per environment (and possibly also per application).

The following figure shows how the approach would work:



The env files (eg: dev.properties, qa.properties) would contain only environmental name=values entries. Those names would be tokens in the central resource files.

For example:

dev.properties:
...
#HOST FOR THE DB
DB_HOST=xdbserver
...

and inside my central resource file:

myspring.properties :

db_host=${DB_HOST}

In this way, i can change the properties on the fly during build, and before deploying. Using the replacement token ${xxx} is not mandatory and can be changed in the ant script.

The example should be used with the following structure:

root
|-resources-|-myspring.properties
| |-x.properties
|-build.xml
|-dev.properties
|-live.properties
|-target-
|-resources

The build file can be something like the following:

<?xml version="1.0"?>
<project name="resources_merging" basedir="." default="filter_resources">
<target name="filter_resources" depends="describe" if="env_type">
<echo message="Replacing tokens for resource files using ${env_type}.properties">
<copy todir="./target/resources/" overwrite="true">
<filterset begintoken="$${" endtoken="}">
<filtersfile file="./${env_type}.properties" />
</filterset>
<fileset dir="./resources" includes="**/*.*" />
</copy>
</target>
<target name="describe" unless="env_type">
<echo message="Deployment type required, eg.: ant -Denv_type=local filter_resources" />
</target>

</project>


To use this scrip, you need to provide an environment parameter, like:

ant -Denv_type=dev filter_resources

This will replace the values provided in dev.properties in all the resource files, and copy all of them to the target/resources folder (that needs to be created beforehand).

It uses the filterset option of the copy ant task, which will search for all the parameters in all the files (in this case in a recursive way).

If you have any doubt about this approach or would like to discuss it, please let me know!!

Emerson Cargnin

3 comments:

Paul said...

This is a bad plan. Its actually much better to have the environment specific variables picked up at runtime, that way a war/ear/jar file you create will work on any environment.

Pick the environment files from the classpath and you can either contain it in the war file or attach it as part of a startup script. This makes releasing a lot easier as you no longer need to do a build for every release. Instead you can publish the release (ala Maven repository or just a file system) and confirm that you have indeed got the right version.

Emerson Cargnin said...

The problem with your approach is that you still need to manage all this system variables per box.
By the way, my war will work in any environment, it doesn't contain the resources. The resources are kept separated in a common area inside the web container.

2photons said...

There are some problems with the Ant/Maven approach, but in the absence of something better, there is nothing else to do.

For Eclipse developers, they will have working property files in their workspace, and then they have to manually create a template version for each file. This template can get out of sync in addition to it being an additional manual configuration step.

In larger companies, the production engineering team will want to control some of those values, such as log file locations, and this approach doesn't provide them any control.

The best approach I've seen is one company that managed to pull in all config parameters via JNS - a lot of work, but worth it - no environment specific files.

After that, I saw developers and production engineering each have a separate Excel XML spreadsheet. An XSLT was done to select values for the appropriate environment and values from the developer's local Eclipse workspace were overridden using property names and XPaths. Developers had little work to do to manage values for other environments...Nice blog!