Advanced Ant Techniques, Part II
by Ajith Kallambella
Part II of a series

Introduction

This January Ant celebrated its 6th year birthday. In January of 2000, the Ant tool, which was created by James Duncan Davidson to build his Apache project called Tomcat, was moved out of the Tomcat source and into a separate Apache project. Since then it has brought the words "build" and "manageability" closer than ever. Ant gained popularity because of its simplicity, extensibility and cross platform support and within a short span of time has become the build tool of choice for a large number of commercial and open source projects alike.

Although Ant has been extremely successful as an enterprise build management tool, most Ant implementations do not use some of the advanced features provided by the tool. In this article, we'll look at a few advanced techniques that can unleash the true powers of Ant, and turn this mere build tool into an indispensable enterprise asset. Examples are provided where applicable, but lack of detailed code is intentional. It is important to focus on the concepts, not so much on the implementation. Software is a writing. Ideas expressed in software, just like in art, music, plays and other media, can be done in many unique ways. Familiarity with the Ant tool and some experimental spirit is all that you need to turn these techniques into real working code.

Automating tests

One of the tenets of Agile development is "test early, test often". It is about implementing common sense testing strategies that make the overall quality of the software that you ship better. By incorporating frequent testing (preferably automated testing) and frequent build/test/deploy cycles, you will detect bugs earlier. To quote Martin Fowler - the key to successful testing is "test to show an application, or parts of it, doesn't work" as opposed to "test to show it works", which is not as rigorous, and hides the real problems till you deploy and start getting support calls.

Ant's support for standard testing suites such as JUnit, HTTPUnit, Cactus and a host of other testing software lets you integrate testing cycles with the build process and get into the groove of Test Driven Development. Although TDD has been around for a while, I have personally seen a large number of projects that don't leverage this integration. Running tests manually is a wasteful use of a talented developer, and it is error prone due to the human factor. A well written suite of tests should be a part of every build process. Running automated tests is especially useful for typical software development companies that have globally distributed development teams.

If you are in the Ant enthusiasts camp, or simply sitting on the testing fence for a while, here are some strategies worthy of consideration:

Continuous automation and continuous integration

Activities involved in a typical SDLC workflow offers several opportunities for automation, and Ant offers unlimited possibilities. Here we focus on integrating with Source Code Management(SCM) systems.

Ant tasks are available to talk to most popular SCM systems, including CVS, Subversion, ClearCase, Continuus/Synergy, Microsoft Visual SourceSafe, Perforce SCM, PVCS, SourceOffSite and StarTeam. With Ant's powerful SCM support, you can hook up parts of the build process to your source control system and perform continuous integration tasks such as check out, check in, baselining etc. Marry that with automated testing, and you can detect bugs as early as the day they got in to the source stream. Once you plumb the verification suite from your build file, you can test after every edit to make sure that nothing breaks. The benefits are substantial. Beyond process maturity and repeatability, a rigorous and portable build process the incorporates elements of continuous integration often results in fewer bugs, higher quality and dollars saved.

If you are not yet on the Agile bandwagon, here are some interesting possibilities to tease you into trying this out:

You can even build automated edit analysis scripts that checks for modified source files and submits them for a verified check-in process. Ant project contributors use Ant's own source code analysis tool:

ant -f patch.xml 

to automatically generate a patch file to Ant. With some ingenuous hacking, you can actually make this work for your project too. For the sake of brevity, I am omitting the details, but the patch.xml file distributed with Ant source download should serve as a good starting point. If you still need help, drop me a line.

Technique: Stretching Ant

Ant isn't a silver bullet and there are times when you reach a dead end with its capabilities and you may need to take things into your own hands. For example, your build script may need some authentication information from an internal LDAP or Active Directory server in order to promote code from development to production; or you are required to retrieve a build number from an internal repository for baselining an integration stream in CVS. Such needs often call for functionality that is not available from an out-of-the-box Ant installation.

There are two main approaches to extending Ant: invoke an executable or OS command from within Ant, or write a custom task (i.e., a taskdef.) Let's talk more about the second option: writing custom tasks.

Custom <taskdef>

It helps to know that under the covers, Ant tasks are just Java classes. In theory, any Java class can be cooked into an Ant task simply by extending org.apache.tools.Ant.Task class and implementing an execute() method. Here's where you do the work.

package mytasks;
import org.apache.tools.Ant.BuildException;
import org.apache.tools.Ant.Task;
public class CustomTask extends  Task {
    public void execute() throws  BuildException {
        // ...
    }
}

And then, you simply include it in a build file using the <taskdef> tag to adds the new task definition to the current project.

<target name="run" >
    <!-- Define task -->
    <taskdef name="customTask"
             classname= " mytasks.CustomTask"
             classpath="${src.mytasks.dir}"/>
    <!-- Invoke task -->
    <customTask>
        <!-- . . . —>
    </customTask>
</target>

We've glossed over a lot of details here. Although execute() method is where the crux of the work is executed, a lot of things happen before it is invoked. It is important for task writers to understand, among other things, the lifecycle of a custom task, custom datatypes, and the concept of TaskContainer. See http://ant.apache.org/manual/develop.html for more details.

Approach task writing with caution - it should not be an everyday affair. With an army of free Ant extensions (distributed as custom tasks) available for use, first search for a tool that meets your needs. Resort to creating your own as a last resort.

In order to be a successful task writer, follow these guidelines:

Distributing custom tasks

Consider creating an antlib to group all custom tasks and types together. Ant libraries or "antlibs" are JAR files with a special XML descriptor that describes the contents, somewhat analogous to a deployment descriptor in a WAR or an EAR file. The XML file defines custom tasks and types defined in the library, and follow a simple URI-to-package name mapping that makes it easier for the Ant runtime to locate the custom implementation. For instance, an URI reference antlib:com.customtasks:mytask suggests Ant to load com/customtasks/mytask/antlib.xml from the JAR file, also containing the class files that make up the custom tasks and types.

Antlibs serve as a practical way to keep custom tasks and types in sync and distribute them in a standard packaged format. Simply drop custom Antlibs in your ANT_HOME/lib directory and Ant will find them, load them and use them.

If you'd like to make your custom task available to users as if it were a core Ant task, you can do so by adding the implementing class (org.apache.tools.Ant.Task) to default.properties file in the org.apache.tools.Ant.taskdefs package. This may mean you'll have to build a binary distribution yourself after modifying the default.properties. By the way, default.properties file is a great resource for finding out taskdef implementation classes.

Using Ant's Java API

Since Ant is also a Java library, it can be used programmatically. It means Ant targets, both core and custom, can be invoked through any Java program. If you have a significant investment in Ant as an enterprise asset and have written custom tasks specific to your software development organization, the Java API allows painlessly reusing them, and increases your return on investment.

Lets say you have a custom task that requests a build number from an enterprise repository. You could now write a Java client program that uses the Ant API to invoke the same task externally, and distributed the Java client to Project Managers so that they can request and use the build number in task management artifacts.

Technique: Intelligently resolve dependencies using <jarlib-resolve>

Let's go back to our hypothetical financial organization (see Part I of this article) one more time and augment the software development organization with a new architecture group. This group is tasked with building common shared artifacts for other application groups to consume. This group is busy producing shared jar files, and new revisions of them. As they research and discover better ways to accomplish things, they roll out revisions to existing libraries - typically shared jar files, for internal consumers. An extended logging tool, a library of XML utilities, a pattern box, a .NET bridge to access company wide Active Directory server etc are all good examples of shared components provisioned by the architecture group.

You've probably noticed a problem here. We need to manage change without adding chaos to the equation. How can we ensure that application projects have an option to continue using old (and compatible) jar files until they are ready to move over to new revisions? How can we manage dependencies without shaking up the foundation every time there is a new internal release of a shared component?

Enter the Ant core task <jar-lib-resolve> and the concept of expanded tagging. Version 1.3 of the Java 2 Platform introduced support for tagging an expanded set of Jar-file manifest attributes (http://java.sun.com/j2se/1.3/docs/guide/extensions/versioning.html) information to enable accurate downloading of jar files by the Java plug-in tools. The idea is to let the Java plug-in access these attributes before downloading the jar file to determine if a specific optional package matches the vendor and version criteria specified by the client. The pre-download introspection avoids downloading a wrong library and therefore, ensures perfect compatibility even when multiple library versions are available.

The following portion of a sample manifest file from ant.jar illustrates some extended attributes as defined by Sun's "Optional Package" specification.

Manifest-Version: 1.0
Ant-Version: Apache Ant 1.5.3 
Created-By: 1.4.1_01-b01 (Sun Microsystems Inc.)
Main-Class: org.apache.tools.Ant.Main
Class-Path: xml-apis.jar xercesImpl.jar optional.jar xalan.jar
Name: org/apache/tools/Ant/
Extension-name: org.apache.tools.Ant
Specification-Title: Apache Ant
Specification-Version: 1.5.3
Specification-Vendor: Apache Software Foundation
Implementation-Title: org.apache.tools.Ant
Implementation-Version: 1.5.3
Implementation-Vendor: Apache Software Foundation

Ant folks followed suit and invented the <jarlib-resolve> task. They brought in the same intelligence found in Java plug-in tools of locating a specific jar file based on extended manifest attributes in to the new <jarlib-resolve> task.

<jarlib-resolve> does two things: it locates a jar file based on a specific set of extension values, and assigns the location to an Ant property. It works with the concept of an <Extension> and <ExtensionSet>; these are custom data types that represent all available extension attributes for the jar file manifest. Since both <Extension> and <ExtensionSet> can be assigned with an unique ID, just like a classpath refid, they can be reused. This means, you can cook <Extension> or <ExtensionSet> definitions one per every revision of a shared jar file, and globally publish them so that internal consumers simply refer to the appropriate ids in their call to <jarlib-resolve>.

As may now be apparent to you, property value assigned by successful <jarlib-resolve> can be preserved for the entire scope of the build process for managing both compile time and runtime dependencies. Combine this with strategies for designing by contract, chaining and failing, you can easily envision a build system that not only resolves dependencies, but also verifies them at relevant places and fails gracefully.

Technique: Beyond building: automating configuration, setup and installation tasks

It's a shame if you haven't been using Ant for building. No, let me rephrase that - if you have been using it only for building. Why? Because the rich set of tasks, both core and extended, can be used accomplish a lot more useful things beyond mere building. Simple tasks can be scripted to automate mundane, repetitive and error prone jobs.

If you are running a software shop, consider the process of setting up a new development environment for a new hire. A typical setup process constitutes configuring a database, version control system (SCM), IDE, the application server, and perhaps a few environment variables. Setting up each tool can quickly get complicated and may involve multiple steps. And don't forget that the guy is new, so there is a large probability of a mistake. In this scenario, would you rather hand him a script to run, or a 27 page document with instructions and screen shots? Although the latter works, it is error prone because of the human factor involved. Most setup and configuration tasks that I have seen can be automated, and Ant proves as an excellent tool for such automation.

Using Ant for automating complex configuration tasks is especially useful when the process involves multiple steps, an array of software tools, and a fair amount of interdependency in how each tool is setup and configured.

For instance, you can script an Ant file to setup the database using <sql> task. Use another script with Ant SCM tasks to pull base-lined code from your SCM. Then use app server specific extensions to Ant to configure a local server, setup JDBC connection pools, JMS connection factories, logger settings etc. Spend a few hours in writing ( and of course testing! ) these scripts, and then weave them together into a master module. Hand it over to the new hire and I bet she'll be impressed. Not to forget the fact that you have increased developer productivity and saved money you'd otherwise spend in both documenting the setup instructions and, more often than you think, fixing a corrupted setup.

As an illustration of how Ant can be scripted to perform a complex configuration task, take a look at Weblogic's extensions to Ant here at http://e-docs.bea.com/wls/docs81/admin_ref/ant_tasks.html.

Using standardized scripts to automate installation and configuration also offers other benefits such as better manageability, easy tool migration, smooth rollouts for configuration changes, repeatability, process maturity and tangible improvements to the overall bottom line.

Closing Remarks

Complexity is a fact of life in modern software development. Organizational structure, geographically dispersed teams, changes within the software development team, vendor dependency, and managing post-production changes all negatively affect the efficiency of both the process and economics of writing software.

While Ant is not a tool for reducing complexity, it can be for put to good use for managing complexity. Hopefully the techniques and strategies presented here in will inspire you in to thinking about creative uses for the tool. If you already use Ant in your team, hopefully I have convinced you that utilitarian appeal of Ant extends beyond its original intent as a build tool, and that you will start expanding its usage to include other opportunities such as configuration automation or adopting continuous integration methodology.