Separation of concerns in Ant

I'm going to use a classic problem to illustrate this: deployment. Have you seen an Ant build that seemed to know far too much about the innards of the container? First you end up with a lot of properties. Then you need to maintain loads of odd targets to try and make it all work together. We can do better.

Step 1: Break it out. You want to have a totally separate Ant buildfile that you can use for deployment. Acceptance criteria: You can kick off a deploy by calling the separate file with a few properties (like where the deployable is, etc.)

Step 2: Import it. Use Ant's import task in the body of your buildfile. Never inside a task!

Step 3: Prefix. A colon is a legal character in an Ant property or task name. So make the prefix match the name of the project. Each distinct buildfile should have the name attribute of the project element set with a meaningful name. Use that.

Step 4: Maintain discipline. It doesn't matter how you do this. Cold showers, if you like. Just make sure that you keep the properties in the right place with the right name.

Here's an example:

<project name="base" default="deploy">
	<property name="container" value="tomcat" description="The Java container that we use" />
    <import file="${container}.xml" />
</project>

Note that there's no deploy target in the file. That resides elsewhere. Running the default target will kick off a deploy to Tomcat from ...

<project name="tomcat">
	
  	<property name="container:hostname" value="some.great.hostname" />
	<property name="container:remote.user" value="deploy" />
	<property name="tomcat:admin_url" value="http://${container:hostname}/admin" />
	
	<target name="tomcat:deploy" description="This throws a war file at tomcat">
		<echo message="gory details of tomcat deploy go here"/>
	</target>
	<target name="deploy" depends="tomcat:deploy" />
	

</project>


.. here. Note that there are properties with a nice generic prefix. Keep those generic because ...

<project name="jboss">
	
  	<property name="container:hostname" value="some.great.other.hostname" />
	<property name="container:remote.user" value="fleury" />
	<property name="jboss:some.jboss.property" value="Paula is brilliant" />
	
	<target name="jboss:deploy" description="This throws a war file at jboss">
		<echo message="jboss deploy goodness here"/>
	</target>
	<target name="deploy" depends="jboss:deploy" />
	

</project>

... all you need to do is pass a different container property to have it deploy elsewhere. What I love about this is that the two implementations cannot exist side-by-side. Only one can be imported, and the property namespace isn't polluted.

(image thanks to Hansol)

DevOps New Zealand