Groovy AntBuilder in Ant Scriptdef to Replace Props

Groovy AntBuilder can be used in scriptdef to allow iterated construction of ant task declarations.

Intro

In a build process, you have a properties file, the former one, and another one, the latter file.   Now you want to replace all properties in the former that exists in the latter.   A kind of SED for property files.

This can be done easily in any programming language, but Property files in Java follow a spec and we rather not deal with the complexities.  Ant has the PropertyFile Task for this.

Apache Ant provides an optional task for editing property files. This is very useful when wanting to make unattended modifications to configuration files for application servers and applications.

Need to iterate
The problem is that the PropertyFile task requires the explicit listing of each key value to edit (the task can do more then just replace). AFAIK there is no way to use a resource collection. Simple example:

Listing 1 – example of PropertyFile use

   <propertyfile file="old.properties">
      <entry key="tag.name" value="release_1"/>
   </propertyfile>

We instead require the creation of the elements while we iterate thru the new file.

Propertiesreplace scriptdef
Groovy is a perfect fit for this. The Builder DSL allows a more ‘natural’ approach. I thought it would be a challenge to code this, but in a few minutes, it was done!

The trick is to create a new AntBuilder in the script. However, this may be a bad or non-idiomatic approach. Perhaps a Groovy expert can comment on it?

Listing 2 – the scriptdef

<!-- Replace properties in oldfile 
whose key match those in newfile -->

<scriptdef name="propertiesreplace" language="groovy"
    classpathref="libs" uri="http://com.jbetancourt1.ant">

    <attribute name="oldfile"/>
    <attribute name="newfile"/>

    <![CDATA[
     def ant = new AntBuilder(project)
     ant.propertyfile( file:attributes.get("oldfile") ){
          def nps = new Properties()
          nps.load(
               new File( attributes.get("newfile") )
                     .newReader()
          )

          nps.each{ cur ->
               entry( key:cur.key,value:cur.value )
          }
     }
    ]]>
</scriptdef>

I should have named it replaceproperties. Note that it would be better to put the Groovy script external, such as:

<!-- Replace properties in oldfile 
whose key match those in newfile -->

<scriptdef name="propertiesreplace" language="groovy"
    classpathref="libs" uri="http://com.jbetancourt1.ant" 
    src="srcReplaceProperties.groovy">

    the rest of the declarations from listing 2 ...
</scriptdef> 
This is cool; Ant’s XML declarative language is used to create a programmatic macro using a script language, Groovy, that in turn is using a DSL to generate Ant code. A code Ouroborous.

Listing 3 – the old properties file

one=1111
#four=4444
two=2222
five=5555
three=3333

Listing 4 – the new properties file

one=abcd
two=efgh
three=ijkl
four=mnop

Listing 5 – run example Ant script

C:temp\antpropMerge&gt;ant
Buildfile: C:temp\antpropMerge\build.xml

init:
     [copy] Copying 1 file to C:temp\antpropMerge

merge:
[propertyfile] Updating property file: C:\temp\antpropMerge\oldProps.properties

list:
     [exec] one=abcd
     [exec] #four=4444
     [exec] two=efgh
     [exec] five=5555
     [exec] three=ijkl
     [exec]
     [exec] four=mnop

all:

BUILD SUCCESSFUL
Total time: 1 second

We actually save these files as templates, so that as shown in the init task of listing 6, we can recreate the test files. Then in target ‘list’, we print the modified old properties file.

Listing 6 – Ant script for dev of solution

<project name="merge" default="all" basedir=".">
  <!-- File: build.xml, 
       author: J.Betancourt, 
       Date:2011-08-17T20:59-0500 -->

  <path id="libs">
    <fileset dir="lib">
      <include name="*.jar" />
        </fileset>
      </path>

      <!-- Groovy library -->
      <typedef resource="org/codehaus/groovy/antlib.xml" 
         classpathref="libs" />

      <!-- Replace properties in oldfile whose 
         key match those in newfile -->

  <scriptdef name="propertiesreplace" 
      language="groovy" classpathref="libs" 
        uri="http://com.jbetancourt1.ant">

  <attribute name="oldfile"/>
  <attribute name="newfile"/>

    <![CDATA[
    def ant = new AntBuilder(project)
    ant.propertyfile( file:attributes.get("oldfile") ){
      def nps = new Properties()
      nps.load(new File( 
        attributes.get("newfile")).newReader())
        nps.each{ cur ->
         entry( key:cur.key,value:cur.value )
      }
     }
  ]]>
</scriptdef>
	
	<!-- use the "propertiesreplace" scriptdef -->
	<target name="merge" depends="init" 
                       xmlns:s="http://com.jbetancourt1.ant">

		<s:propertiesreplace oldfile="oldProps.properties" 
                       newfile="newProps.properties"/>		
	</target>

	<target name="all" depends="init,merge,list"/>		
	
	<target name="init">
		<copy file="oldProps.properties-template" 
                  tofile="oldProps.properties"/>
	</target>	
	
	<target name="list">
	  <exec executable="cmd.exe" >
	    <arg line="/c type oldProps.properties"/>
	  </exec>	
	</target>	
	
</project>

Why version control?
I code this up, then I decided to share it for anyone who may appreciate some examples (and for my own dev notes). I edit a file. It stops working. Can’t figure out why quickly enough. My editor’s (notepad++) undo is not helping.

Fixed it, but to avoid this scenario again I do:
git init
git commit -a -m “initial commit”

Careful
Did you see the future human operator error waiting to happen? In the example, the modified properties file has two entries:
#four=4444
four=mnop

Lets say this file is, for example, involved in code controlling a 800 ton crane, and an admin removes comment mark from the first “four”. If the file is processed with Ant, the first ‘four’ entry wins. Control code is deployed and now crane is waiting to drop its crate at bad moment. A Road Runner coyote scenario. Beep beep.

Updates
2011-0819: Got stumped regarding exception invoking cvs task in Ant and losing all existing properties at the BuildFinished handler. Did a search and found that someone already created a merge properties task:
. Merging property files…
. A version with fixes
2012-04-02: It may be possible that the Groovy scriptdef taskdef already includes an AntBuilder.

Further Reading

One thought on “Groovy AntBuilder in Ant Scriptdef to Replace Props”

  1. Pingback: thank you :D

Leave a Reply

Your email address will not be published. Required fields are marked *