ONJava.com -- The Independent Source for Enterprise Java
oreilly.comSafari Books Online.Conferences.

advertisement

AddThis Social Bookmark Button

Writing Ant Tasks Writing Ant Tasks

by Michael Fitzgerald
06/02/2004

Apache Ant is an increasingly popular open source, cross-platform build tool written in Java. Ant's build files are written in XML and generally consist of a project and a set of interdependent targets. These targets contain one or more tasks that can perform all kinds of functions, such as compiling Java source code, creating .zip, .gzip, or .bzip2 archives, cleaning up old files, and so on.

A nice feature of Ant is that it is designed to allow you to add your own tasks and use them in an build. This article shows you the basics of writing an Ant task and how to get a task to work.

This article assumes that you are familiar with Ant and with the Java programming language. It walks you through the creation of a simple Ant task in Java, and then looks at a more complex task written by James Clark for Jing, an open source RELAX NG and Schematron validator written in Java. Both RELAX NG and Schematron are schema languages for XML. (I won't demonstrate the features of Schematron in this article). The example code and other files mentioned in this article are available for download from the Resources section below and at www.wyeast.net/task.zip. The examples have been tested with Ant version 1.6.1 (the latest version of Ant at the time of this writing), Java version 1.4.2_03, and Microsoft Windows XP Professional version 5.1.2600. (I'll be using the command-line version of Ant, though some GUIs for the tool are available.)

Writing a Simple Task

Before getting started, download and install Ant version 1.6.1 or later on your system, if it is not already there. When you extract the files from the Ant archive (in .zip, .gzip, or .bzip2 format, depending on what you download), you'll notice a bin directory in the distribution. Place the bin directory in your path, and then type ant -version at a command or shell prompt. If you get back a response like Apache Ant version 1.6.1 compiled on February 12 2004, you're in business.

In the example archive, you'll find a file named Add.java. This file contains an Ant task that simply reports the sum of two integers. The code is not complex, but it provides a skeleton for your own tasks. Here's a listing of the file:

package net.wyeast.ant;

import org.apache.tools.ant.BuildException;
import org.apache.tools.ant.Task;

public class Add extends Task {

    private int op1;
    private int op2;
    private int sum;

    // The method executing the task
       public void execute()
           throws BuildException {
           sum = op1 + op2;
           System.out.println("The sum of the " +
               "operands is " + sum + ".");
       }

   // The setter for the "op1" attribute
       public void setOp1(int op1) {
          this.op1 = op1;
       }

   // The setter for the "op2" attribute
       public void setOp2(int op2) {
          this.op2 = op2;
       }
}

Related Reading

Ant: The Definitive Guide
By Jesse E. Tilly, Eric M. Burke

You don't have to use a package name as I did (net.wyeast.ant), though it's axiomatic to disambiguate class names that could possibly collide with other class names in an open environment. Only two Ant classes are imported: org.apache.tools.ant.BuildException and org.apache.tools.ant.Task, an abstract class. The Add class extends the Ant Task class. Other options are possible, but Task is the base class for all Ant tasks. By the way, because only the class Task is declared as abstract, you do not need to implement all of its methods.

The other imported class is BuildException. You'll notice that the execute method in Add, which overrides the method of the same name from Task, throws a BuildException. A BuildException is thrown if something goes wrong with the build or if the task cannot be properly configured.

The three private variables of type int, op1, op2, and sum, are used to perform the arithmetic you see in the execute method. The execute method is mandatory: it's the method Ant uses to execute a task. The Add task adds two operands and then prints the sum with System.out.println. But where do these operands come from during a build? They are picked up from the values of the attributes op1 and op2 in the file build.xml file, which you will see in a moment. The two setter methods setOp1 and setOp2 correspond with the attributes op1 and op2. Setter methods such as these process attribute values, which is the main way an Ant task gets input and passes it on to an underlying program.

Now it's time to compile and package the task code into a .jar. To compile Add.java, you'll need to add ant.jar to your classpath so the compile can pick up the Task and BuildException classes. You'll find this .jar in the lib subdirectory in the Ant distribution. At a prompt in the working directory where you extracted the files from the example archive, type something similar to the following line, taking care to use the correct path to ant.jar, wherever it might be on your system:

javac -classpath ant.jar net/wyeast/ant/Add.java

Once you are successful at compiling Add.java, package Add.class into a .jar file:

jar cvf add.jar net/wyeast/ant/Add.class

The class or classes you use for the task apparently must be in a .jar file to work -- I've tried just using bare classes in a variety of ways with no success. For convenience, place a copy of add.jar in the lib subdirectory mentioned earlier. This is where all of the Ant .jar files happily live. With add.jar there, Ant will be able to find the class in it at build time. Granted, as long as you make sure add.jar is in your classpath (using Ant's -lib option), Ant will be able to find it, but I find it's most convenient to drop .jars into lib.

Exercising the Task

With the code compiled and the .jar in place, your are ready to try out the task. In the example archive you will also find the following Ant build file, build.xml:

<?xml version="1.0"?>

<project default="main">

<taskdef name="adder" classname="net.wyeast.ant.Add"/>

<target name="main">
 <adder op1="23" op2="77"/>
<target>

<project>

This is a small example of a build file, but it suits the needs of the moment. It contains only one target, called main. The default attribute on project is required and names the default target to be used; that is, main, the only target in the file. The main target contains the task adder, which is dependent on taskdef.

The taskdef element preceding the target defines a name for the task and the class name where the code for the task lives. The value of the name attribute names the task. That name need not match the name of the class, although it must match the name of the task element that triggers it. Also, taskdef does not need to precede the target or targets that use the task.

The value of the classname attribute gives the name of the class that executes the task. The class net.wyeast.ant.Add is found in add.jar, which you previously copied to the Ant lib subdirectory. If add.jar is not there (or elsewhere in the classpath), this task will simply not work.

The adder child element of target executes the task using its two attributes mentioned earlier, op1 and op2. While in the working directory, test out the task by typing ant at the command or shell prompt. Ant automatically invokes the instructions in the file build.xml because build.xml is the default Ant build file name (you can use build files with different names if you use the equivalent -f, -file, or -buildfile option with Ant). After you type ant, you should get the following response:

Buildfile: build.xml

main:
    [adder] The sum of the operands is 100.

BUILD SUCCESSFUL
Total time: 1 second

There you are. You now know the basics of how to write an Ant task in Java, compile and package it as a .jar, and put the .jar in a place where Ant can find it. You also learned how to define a task and invoke it within an Ant build file. Now that you understand the process, it's a good time to do some experimenting. Some things you could try include:

  • Changing the values of op1 and op2 in build.xml.
  • Renaming the task in both taskdef and its element name.
  • Adding another attribute setter method to Add.java, such as setOp3, and then placing the corresponding attribute op3 in the invocation of the task in build.xml (also, add a variable to handle the attribute value and add it to the sum).
  • Invoke the task more than once within a target.
  • Add new targets that invoke the task.

After you play around with the build file and code, try writing an entirely new task yourself with a different and more interesting result than adding integers!

Pages: 1, 2

Next Pagearrow