Apache Ant: Learn Apache Ant from IBM Experts

learn-apache-ant-from-experts

Section 6. Extending Ant with custom tasks

Extending Ant with custom tasks introduction

As we’ve seen throughout the previous sections, Ant is very powerful, with a number of core tasks covering a broad set of functionality. There are a number of additional core tasks not covered here, plus a number of optional tasks providing a wide variety of additional functionality, as well as other tasks that are available as part of the Ant-Contrib project; finally, there are even more externally available tasks listed on the Apache Ant home page. Given all of this, it might seem unlikely that you’d ever need any other tasks, but the real power of Ant lies in its ease of extensibility. In fact, it is precisely this extensibility that has resulted in such a large number of additional tasks being developed.

There can be occasions when creating your own custom task is a good idea. For example, imagine that you have created a command-line tool to perform a particular operation; this tool might be a good candidate to be made available as an Ant task (particularly if the tool was written in the Java language, although this doesn’t have to be the case). Instead of having Ant call the tool externally using the exec task (which introduces dependencies and makes it harder to use your build file across different environments), you can incorporate it directly into the build file. The regular fileset and wildcard matching capabilities of Ant can also be made available to your custom task.

In this section, we’ll take a look at the construction of a simple custom task. This task will perform a sort operation on the lines of a file, and write the sorted set of lines out to a new file.

Creating a custom task

To implement a simple custom task, all we need to do is extend the org.apache.tools.ant.Task class and override the execute() method. So, as a skeleton of our file sorter custom task, we have the following:

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

public class FileSorter extends Task {
// The method executing the task
public void execute() throws BuildException {}
}
Notice that the execute() method is declared to throw a BuildException. If anything goes wrong with our task, we throw this to signal the failure back to Ant.

Most tasks, core and custom alike, make use of attributes to control their behavior. For our simple task, we need an attribute to specify the file to be sorted, and another to specify the output file for the sorted contents. Let’s call these attributes file and tofile, respectively.

Ant makes it very easy to support attributes in custom tasks. We do this simply by implementing a method with a specifically formatted name that Ant can call with the values of the corresponding attribute specified in the build file. The name of the method needs to be set plus the name of the attribute, so in our case we need methods called setFile() and setTofile(). When Ant encounters an attribute setting in the build file, it looks for a method of the appropriate name, known as a setter method, in the associated task.

Attributes in the build file are specified as strings, so the argument to our setter methods can be a string. In such a case, Ant will call our method with the string value of the attribute, after expanding any properties referenced by the value. But sometimes we want to treat the value of the attribute as a different type. This is true for our sample task, where the attribute values refer to files on the filesystem rather than just arbitrary strings. We can do this very easily by declaring our method argument to be of type java.io.File. Ant will take the string value of the attribute and interpret it as a file, and then pass this to our method. If the file is specified with a relative path name, this will be converted into an absolute path name relative to the project’s base directory. Ant can perform similar conversions for other types, such as boolean s and int s. If you provide two methods with the same name but with different argument types, Ant will use the more specific one, so a file type will be preferred to a string type.

The two setter methods we need for our custom task look like this:

// The setter for the “file” attribute
public void setFile(File file) {}

// The setter for the “tofile” attribute
public void setTofile(File tofile) {}

Implementing a custom task

Using the skeleton developed in the previous section, we can now complete the implementation of our simple file sorter task:

import java.io.*;
import java.util.*;
import org.apache.tools.ant.BuildException;
import org.apache.tools.ant.Task;

/**
* A simple example task to sort a file
*/
public class FileSorter extends Task {
private File file, tofile;

// The method executing the task
public void execute() throws BuildException {
System.out.println(“Sorting file=”+file);
try {
BufferedReader from =
new BufferedReader(new FileReader(file));
BufferedWriter to =
new BufferedWriter(new FileWriter(tofile));
List allLines = new ArrayList();
// read in the input file
String line = from.readLine();
while (line != null) {
allLines.add(line);
line = from.readLine();
}
from.close();
// sort the list
Collections.sort(allLines);
// write out the sorted list
for (ListIterator i=allLines.listIterator(); i.hasNext(); ) {
String s = (String)i.next();
to.write(s); to.newLine();
}
to.close();
} catch (FileNotFoundException e) {
throw new BuildException(e);
} catch (IOException e) {
throw new BuildException(e);
}
}

// The setter for the “file” attribute
public void setFile(File file) {
this.file = file;
}
// The setter for the “tofile” attribute
public void setTofile(File tofile) {
this.tofile = tofile;
}
}

The two setter methods simply store the values of the attributes so they can be used in the execute() method. Here, the input file is read line by line into a list, which is then sorted and written out line by line to the output file. Note that to keep things simple we perform very little error checking — for example, we don’t even check that the required attributes have actually been set by the build file. We do at least catch any I/O exceptions from the operations performed, and rethrow these as Ant BuildExceptions.

We can now compile our custom task with the javac compiler, or from within an IDE. In order to resolve the Ant classes we have used, you need to add the location of the ant.jar file to your classpath. This is should be in the lib directory of your Ant installation.

Using a custom task

Now that we’ve developed and compiled our custom task, we are in a position to make use of it from a build file.

Before we can call our custom task, we need to define it by giving it a name, and telling Ant the classfile that implements it and any classpath setting required to locate that classfile. This is done using the taskdef task, like so:

classname=”FileSorter”
classpath=”.”/>

That’s it! The task can now be used in the same way as Ant’s core tasks. Here is a complete build file, showing the definition and use of our custom task:

 

classname=”FileSorter”
classpath=”.”/>

Now, create an input.txt file in the present working directory to test the custom task. For example:

Hello there
This is a line
And here is another one

Here is the console output after running the above build file:

Buildfile: build.xml

main:
[filesorter] Sorting file=E:\tutorial\custom\input.txt

BUILD SUCCESSFUL
Total time: 0 seconds

 

Notice that our relative pathname of input.txt gets converted into an absolute pathname in the current directory. This is because we specified the argument to the setter method to be of type java.io.File instead of java.lang.String.

Now let’s see if the task actually worked. A file called output.txt should have been created in the same directory with the following contents:

And here is another one
Hello there
This is a line

You might try specifying an input file that doesn’t exist to see how a “file not found” exception is reported back to Ant.

Congratulations: you have now developed and used a custom Ant task! There are many additional aspects involved in creating more complex tasks, and Resources contains links to sources of further information in this area.

Tagged : / / / / / / / / / / / / / / / / / / / / / / /

Leave a Reply