Chapter 9. Beyond Ant’s core tasks

9.1 The many different categories of Ant tasks
9.2 Installing optional tasks
9.3 Optional tasks in action
9.4 Software configuration management under Ant
9.5 Using third-party tasks
9.6 The Ant-contrib tasks
9.7 Code auditing with Checkstyle
9.8 Summary

At this point in the book, we have a build file that can build, test, package, run, and distribute a Java library. In the next few chapters, we’ll use it in a bigger application, building, deploying, and testing web and Enterprise Java applications.

Before then we’re going to look at more of Ant’s tasks, exploring the different kinds of tasks that Ant supports: built-in, optional, and third-party. The tasks we’re going to cover can all improve our application’s build in little ways: automating other steps in the process such as patching property and text files, or auditing the source. Together they can automate even more steps of the process of getting software out the door. They also will give the reader a clearer understanding of the nature of the tasks that constitute Ant’s functionality.

Concepts covered in this chapter, especially the different types of tasks and how to load tasks that do not come with Ant, are going to be used throughout the rest of the book. Therefore, even if you aren’t interested in the specific tasks we cover here, please skim the chapter and become familiar with the terminology.

9.1. The many different categories of Ant tasks

What is Ant? It’s an XML language to describe a build, an engine that interprets the language, and the tasks that the runtime executes to perform useful work. It is the tasks that build the project. Accordingly, Ant is only as useful as its tasks.

You can accomplish a great deal with an out-of-the-box Ant installation. However, eventually you’ll need more than it offers through its built-in tasks. Ant’s optional tasks provide a set of extra features, many of which need extra libraries installed in Ant’s library directory.

There also are a growing number of Ant tasks—third-party tasks—which are written by other people. These are often part of Java projects and make their applications more usable under Ant. The broad variety of third-party tasks gives Ant its real power.

Altogether, there are four types of Ant tasks and datatypes:

  • Core— Tasks that work out of the box and are immediately available for use. We’ve used many core tasks, such as <javac>, <jar>, and <copy>.
  • Optional— Tasks that ship with Ant but typically require libraries or external programs that do not ship with Ant. We’ve already been using some of these tasks—<junit>, <junitreport>, <ssh>, and <ftp> are all optional.
  • Third-party— Tasks that were developed by others and which can be dropped into an Ant installation.
  • Custom— Tasks developed for your own projects. We’ll cover these in chapter 17.

This chapter introduces optional and third-party tasks. We also cover a few technical hitches that can occur when using optional and third-party tasks. First, we’ll dig into the optional tasks.

Optional Tasks

In early versions of Ant, “optional” tasks were distributed as an add-on library (optional.jar) that users had to download separately. Currently, Ant ships with a complete set of core and optional tasks. Even so, there are still distinctions between the two task types. Ant’s optional tasks come in different JAR files and the online documentation splits the tasks into two lists, core and optional. With current distributions, this distinction may seem odd or unnecessary, but there are some remaining differences. Table 9.1 shows most of Ant’s optional tasks.

Table 9.1. Ant’s optional tasks. Most of these tasks require installation of additional components.

Task Category

Examples

Covered

Source Code Management ClearCase, Continuus, Perforce, PVCS, StarTeam, and Visual SourceSafe tasks. <cvs> is a core task Chapter 9
Packaging <cab>, <rpm>, <ejbjar>  
Compilers and Grammars <antlr>, <depend>, <javacc>, <javah>, <jspc>, <icontract>, <netrexxc>, <jdepend>  
Utilities <propertyfile>, <image>, <sound>, <splash>  
Text manipulation <native2ascii>, <replaceregexp>, <translate>  
Testing <junit>, <junitreport> Chapter 4
XML Processing <xmlvalidate>, <schemavalidate> Chapter 13
Networking <telnet>, <ftp>, <sshexec>, <scp>, <setproxy>, <rexec> Chapter 7
Scripting <script>, <scriptdef> Chapter 18

Most optional tasks need an extra library or program to work, and they come in JAR files that group them by their dependencies. For example, the <scp> task of chapter 7 lives in ant-jsch.jar and depends upon the jsch.jar SSH library.

Optional tasks that don’t depend on third-party JAR files are all packaged into ant-optional.jar. Normally this is invisible to users, unless they spend time browsing around Ant’s directories.

Those tasks that depend on extra libraries usually need them in Ant’s own classpath. That means they must be in one of the directories from which Ant loads JAR files, a directory named on the command line with the -lib option, or listed in the CLASSPATH environment variable. Ant’s documentation lists dependencies, which can change from release to release. As Ant is built with the latest released versions of the external libraries, it’s good to upgrade these libraries when upgrading Ant.

The final difference between core and optional tasks is that many optional tasks do not get used or tested as much as the core set. New tasks are added into the optional group either when they have an external dependency or when they’re viewed as less important than the core set. In both cases, the number of users may be less than for other tasks, which means the support for and documentation of those tasks may be behind that of the main Ant tasks. May does not mean that this is always the case. The JUnit tasks <junit> and <junitreport> are both examples of broadly used and well-supported Ant tasks. You just need to be aware that if you use an optional task, you might have to do more of the maintenance yourself.

Third-party tasks

The Ant team maintains the Ant runtime and the many core and optional tasks, but it lacks the time and skills to maintain all the other tasks that projects may need. Third-party tasks are a common addition to many Ant-based projects. Many Java libraries and applications include Ant tasks. This gives Ant users tasks that are tightly bound to the product and are maintained by the same development team, which results in high-quality tasks.

As a consequence, many of the most interesting things you can now do with Ant require third-party tasks. We’ll explore some of these tasks throughout this section of the book.

Some people fear third-party tasks, preferring to use only the tasks that ship with Ant itself. They do this because they believe that third-party tasks are lower in quality than those that Ant bundles, and that Ant builds should use only those tasks that Ant bundles. This isn’t the case. Many open source and commercial products provide Ant tasks to work with their products, and these are often well-written tasks that integrate seamlessly with the products themselves. The quality, integration, and documentation for these tasks can be equal to or better than those that ship with Ant. Don’t be afraid of using them.

Third-party tasks, like optional tasks, are distributed in JAR files. Traditionally, you’ve had to modify Ant build files to tell Ant about any new types in the JAR by using the <tasksdef> and <typedef> tasks.

Since Ant 1.6, a new task-loading mechanism, Antlib, makes this process much simpler. An Antlib is a JAR file whose type and task declarations are picked up by Ant when you declare the appropriate package URL in an XML namespace declaration. The mechanism also lets library developers declare more than just the tasks; they can declare datatypes and define tasks in scripting language or XML, rather than just Java. The Ant project itself is starting to distribute extension libraries as Antlibs, blurring the distinction between optional and third-party tasks.

After looking at some of Ant’s optional tasks, we’ll explore some third-party tasks. First, we’ll look at the problem of getting an optional task installed.

9.2. Installing optional tasks

To use most optional tasks, you must download and install extra libraries or programs. The JAR containing the task itself isn’t always enough: any extra libraries that an optional task depends on must be on the classpath of Ant or you’ll see an error message.

If the optional task is present but a library that it depends on is missing, Ant will inform you of this fact. Here, for example, is the error message if the <scp> task cannot load a version of the jsch.jar library:

build.xml:288: Problem: failed to create task or type scp
Cause: Could not load a dependent class com/jcraft/jsch/UserInfo
    It is not enough to have Ant's optional JARs
    you need the JAR files that the optional tasks depend upon.
    Ant's optional task dependencies are listed in the manual.
Action: Determine what extra JAR files are needed, and place
 them in one of:
   -C:JavaApacheAntlib
   -C:Documents and Settingsant.antlib
   -a directory added on the command line with the -lib argument

Do not panic, this is a common problem.
The commonest cause is a missing JAR.

This is not a bug; it is a configuration problem

Ant doesn’t directly identify or download the missing library—you need to consult Ant’s documentation to see what’s needed and where it can be retrieved.

If the ant-jsch.jar file itself was missing, the class implementing the <scp> task wouldn’t be found, so the message and suggested action would be slightly different:

build.xml:288: Problem: failed to create task or type scp
Cause: the class org.apache.tools.ant.taskdefs.optional.ssh.Scp
    was not found.
    This looks like one of Ant's optional components.
Action: Check that the appropriate optional JAR exists in
   -C:JavaApacheAntlib
   -C:Documents and Settingsant.antlib
   -a directory added on the command line with the -lib argument

Do not panic, this is a common problem.
The commonest cause is a missing JAR.

This is not a bug; it is a configuration problem

Ant assumes that any task implemented in its own packages is one of its own tasks, so Ant prints this special message. It has not recognized that the task will need jsch.jar; it will only discover that fact once ant-jsch.jar is present.

Once you have the main set of optional JAR files for your projects, these error messages should be rare. The only time they’ll surface is if you’re running Ant under an IDE and the relevant JAR files aren’t on the classpath. In this situation, you’ll need to add them to the classpath through the appropriate IDE settings dialogs.

You can explicitly probe for a task by using the <typefound> condition. This condition verifies that there’s a task matching that of the name attribute and that Ant can instantiate it, and it lets the build file fail with a helpful error message:

<target name="check-scp" >
  <fail>
    SCP support not found; the scp task needs
      1. ant-jsch.jar
      2. jsch.jar from
      http://www.jcraft.com/jsch/
    <condition>
      <not><typefound name="scp" /></not>
    </condition>
  </fail>
</target>

This example checks for the presence of the <scp> task and shows where the task and JAR come from. If you run the target with verbose output (ant -v check-scp) you’ll see the detailed diagnostics that Ant normally prints when a task cannot be created.

If you encounter problems, the cause is likely to be one of a common set.

9.2.1. Troubleshooting

Here are the special problems you may encounter with optional tasks.

Missing mandatory helper library

This is the most common problem. When working with optional tasks, check the online documentation for information on which libraries are needed.

Missing optional helper library

Some tasks will load but not work if a library is absent. For example, the <mail> task needs mail.jar and activation.jar to send MIME messages, and it can send only plain text with attachments as UUENCODE-encoded files. Tasks usually degrade gracefully or fail with task-specific error messages in these situations.

Out-of-date helper library

Ant is built and tested against the latest release and development versions of all other open source Java libraries. The latest versions of those libraries will have more fixed, new features and sometimes a changed API. Check for library updates whenever you upgrade Ant.

Missing task implementation

Ant packages optional tasks by the names of their dependencies into files such as ant-junit.jar. If one of these libraries is missing, none of the Ant tasks in it will be found. Even if the JAR is there, it may not contain all the implementation classes. This happens when someone makes their own build of Ant. Official Ant releases should not suffer from this.

Library version conflicts

If there are different versions of library classes on the classpath, Ant may end up running with an older version of the library. This may happen if a JAR file—such as jython.jar—ships with some library classes (in this case the Jakarta-ORO regular expression classes). The Ant documentation warns whenever a JAR file has this problem. The only fix is to strip the particular library files, usually in a Zip file editor.

Failed build when running Ant under an IDE

We recommend setting up your IDEs to use the version of Ant that you use on the command line, with all the files in ANT_HOME/lib on the classpath. Not all IDEs load JAR files under ${user.home}/.ant/lib. You may need to manually add these files to the IDE.

Missing executables

Tasks that depend on an executable being on the path will fail with a platform-specific error if the program is absent or if the user’s path isn’t set up correctly. On Windows, a message with “Error code 2” is the usual cue for this problem.

The power tool for tracking down many of these problems is ant -diagnostics. If users of your build file are encountering problems, ask them to save the output of a diagnostics run to a file so that you can examine their configuration at your leisure.

There is also a <diagnostics> task, which runs the diagnostics code inside Ant itself:

<target name="diagnostics" description="diagnostics">
  <diagnostics/>
</target>

This task is invaluable for troubleshooting Ant under an IDE, as it shows how the IDE has configured the tool.

Now that we’ve covered how to troubleshoot optional tasks, let’s use some.

9.3. Optional tasks in action

We’ve already used a few optional tasks in the book, first <junit> and <junitreport> in chapter 4; then <ftp>, <scp>, and <sshexec> in chapter 7. Here we’ll explore a few more that often come in handy in a project:

  • <propertyfile>
  • <depend>
  • <replaceregexp>

Most of these tasks illustrate the optional nature of the tasks and require additional components to be installed in order to function properly. For each task, we discuss the specific requirements it has and how to configure your system to run it.

9.3.1. Manipulating property files

One of the easiest and most common methods of configuring Ant and Java are Java property files. The <propertyfile> task lets you create and manipulate property files, even incrementing numbers and dates. Java programs can then read these files via the java.util.Properties class.

We can use <propertyfile> to save the build date and time, machine name, user, and operating system into a properties file. Listing 9.1 shows how to do this.

Listing 9.1. Using <propertyfile> to capture build-time information
<property environment="env"/>
<property name="env.COMPUTERNAME" value="${env.HOSTNAME}"/>

<propertyfile comment="Build Information"
              file="${build.classes.dir}/build.properties">
  <entry key="build.date"
     type="date"
     pattern="EEEE MMM dd, yyyy"
     value="now"/>
  <entry key="build.time"
     type="date"
     pattern="kk:mm:ss"
     value="now"/>
  <entry key="build.host" value="${env.COMPUTERNAME}"/>
  <entry key="build.user.name" value="${user.name}"/>
  <entry key="build.os.name" value="${os.name}"/>
</propertyfile>

The <propertyfile> task takes the name of a file to create an optional comment string to appear at the top of the task. It then takes a list of <entry> elements, all of which must have a key and value. Every entry may also have a type, which can be "string", "int", or "date". The default type, string, just saves the value straight to the properties file—escaping it according to the rules of the java.util.Properties class. With this file created under ${build.classes.dir}, it will be pulled into the JAR we create, and so provide an audit trail as to who built the application, and when.

The date type is interesting, because it dynamically evaluates and formats the date; with value="now" it sets it to the current date or time. When we get a support call related to this JAR, we can find out who built it, and when. We could even have some Java code in the application to load the properties from the classpath, and print the information in a diagnostics screen.

While updating the file, the task doesn’t set any Ant properties. To use the properties in Ant, we have to load the file using <property file="..."/>, perhaps setting the prefix attribute to keep the entries from clashing with already-existing properties.

It’s nice to have a properties file in a JAR with audit information—but how about setting some advanced properties, such as an auto-incrementing build number? Whenever the application is rebuilt, the number would increment. We might also want to include an expiration date that our software could use to restrict the life of a demo version, or act as a trigger to probe for an updated release.

The <propertyfile> task can do both of these things, as it has the ability to increment numbers and dates. Ant also includes a <buildnumber> task to increment build numbers more concisely. In listing 9.2, we use both tasks to create/update a properties file at build-time, which not only stores the build number, but also sets an expiration date in the file.

Listing 9.2. Using <propertyfile> to increment a build number and set a future date in the same file

The <entry> element of the <propertyfile> task supports an operation attribute, which must be either "+", "-", or the default of "=". Date types also support a unit attribute to define the value of the addition. By setting the expiration.date entry to be the operation of “now+1 month”, we’ve set it to a date in the future.

Existing properties are untouched unless modified explicitly with an <entry> item. All comments are lost, however; they are stripped out when the original is read in, and so they cannot be recreated.

The next optional task we’re going to look at is <depend>, which adds extra dependency logic onto <javac>.

9.3.2. Improving <javac> with dependency checking

The <javac> task passes .java files to the compiler if the corresponding .class file is older or absent. It doesn’t rebuild classes—such as a parent class or an imported class—when the files that they depend upon change. The <depend> task fixes this. Rather, it looks at the generated .class files, extracts the references to other classes from them, and then deletes the class files if they’re out of date compared to their references. This process clears out files for <javac> to rebuild. One disadvantage of this is that because compile-time constants, such as primitive datatype values and string literals, are inlined at compile time, neither <javac> nor <depend> can tell when a definition such as Constants.DEBUG has changed from true to false.

In situations where there’s a large number of Java source files and when clean builds take a long time, the <depend> task is a great benefit to ensure incremental builds compile the correct files. Adding the dependency check to the build process is fairly simple; we just paste it into the compile target above the <javac> call, as shown here:

<depend srcdir="src"
  destdir="${build.classes.dir}"
  cache="${build.dir}/dependencies"
  closure="true">
  <classpath refid="compile.classpath"/>
</depend>
<javac destdir="${build.classes.dir}"
  debug="true"
  debuglevel="${build.debuglevel}"
  includeAntRuntime="false"
  srcdir="src">
  <classpath refid="compile.classpath"/>
</javac>

The <depend> task requires two attributes, srcdir, which points to the Java source, and destdir, which points to the classes. The cache attribute names a directory that is used to cache dependency information between runs; this directory is created on demand.

The <depend> task analyzes the .class files to determine which classes they depend on, and as this information doesn’t change when the source is unchanged, it can be safely cached from run to run to speed up the process. Because it does speed up the process, however, we highly recommend that you always specify a cache directory.

The final attribute we’re using is closure, which tells the task whether to delete .class files if an indirect dependency has changed. The merits of this attribute are unclear: it may be safer to set closure=true, but faster to leave it unset.

There’s also a nested attribute to specify a classpath. This isn’t mandatory; <depend> isn’t compiling the source and it doesn’t need to know where all the packages the source depends upon are stored. Instead, the task uses any supplied classpath as a list of classes that may also have changed. It looks inside JAR files to see the timestamps of classes, deleting local .class files if imports from the JAR have changed. For faster dependency checking, list those JAR files that change regularly.

You can also include or exclude source files from the dependency checking by using nested <includes> and <excludes> elements, though we never bother with this.

Running the target adds one more line to the compilation target’s output, here stating that seven files were deleted:

compile:
  [depend] Deleted 7 out of date files in 0 seconds
  [javac] Compiling 7 source files to
          /home/ant/diary/core/build/classes

This task makes incremental builds more effective. Keep in mind, however, that it cannot detect dependencies on imported constants—primitive types declared as static and final. Therefore, a regular clean build is always a good idea.

These two tasks give a bit of the flavor of Ant’s optional tasks. It’s very much a mixed bag of tasks that are viewed as “sometimes useful” alongside other tasks that are almost essential—<junit> being one of these—but which have external dependencies.

One set of useful optional tasks is the Software Configuration Management task family, which can be used to work with version control repositories to let a build run, check out, check in, or tag files.

9.4. Software configuration management under Ant

Ant works with most software configuration management (SCM) systems. There are a multitude of tasks that enable developers to make calls to the repositories from inside Ant. These tasks can check in and check out code, and sometimes even add labels. The exact set of services available depends on the particular SCM tool in use: each tool has a unique set of corresponding Ant tasks, most of which are implemented as optional tasks.

Ant directly supports CVS, Perforce, ClearCase, SourceSafe, SourceOffsite, StarTeam, Serena PVCS, and Continuus. Each has its own tasks and its own set of operations. Table 9.2 lists the corresponding Ant tasks showing which SCM systems support tasks for the comment actions of update, check out, check in, and label.

Table 9.2. Ant-supported SCM systems and their common operations

SCM System

Update

Check out

Check in

Label

ClearCase <ccupdate> <cccheckout> <cccheckin>  
Continuus   <ccmcheckout> <ccmcheckin>  
CVS <cvs command="update"> <cvs command="checkout"> <cvs command="commit"> <cvs command="label">
Perforce <p4sync> <p4edit> <p4submit> <p4label>
PVCS <pvcs>      
SourceOffSite <sosget> <soscheckout> <soscheckin> <soslabel>
SourceSafe <vssget> <vsscheckout> <vsscheckin> <vsslabel>
StarTeam   <stcheckout> <stcheckin> <stlabel>
Subversion <svn subcommand="update" /> <svn subcommand="checkout"/> <svn subcommand="commit"/> <svn subcommand="copy"/>

We’re not going to cover all the tasks, because there are many of them and some of the products are pretty obscure. We’ll just look at CVS and mention its forthcoming successor, Subversion.

All the SCM tasks need external programs or libraries to run. Most rely on native executables on the path, such as cvs, p4, svn, or cleartool.

During the development of this book, we used CVS servers as the repositories for source and the book’s chapters themselves. We added a task to check out the code from the repository into a temporary directory, which makes it easy for developers to create a read-only copy of the source. This method also enables you to check that the contents of the repository build properly. Chapter 15 will automate this checkout and build as part of the continuous integration process, but a manual checkout is still useful to isolate SCM problems from those of the continuous integration server.

Here is the Ant script to check out the application using the <cvs> task:

<property name="root.dir" location="${env.TEMP}"/>
<property name="cvs.user" value="anonymous"/>
<property name="cvs.host" value="antbook.sourceforge.net"/>
<property name="cvs.root"
  value=":pserver:${cvs.user}@${cvs.host}:/cvsroot/antbook"/>
<property name="cvs.passfile" value="../.cvspass"/>
<property name="cvs.dir" location="${root.dir}"/>
<property name="cvs.package" value="examples/diary"/>

<cvs cvsRoot="${cvs.root}"
     command="checkout"
     dest="${root.dir}"
     package="${cvs.package}"
     passfile="${cvs.passfile}"
     failonerror="true" />

We’ve told <cvs> to check the files out into a temporary directory, and set failonerror="true" to ensure that a <cvs> failure is fatal. When run, the <cvs> task will check out our project using the normal Windows/Unix CVS program, or fail with an error.

The <cvs> command also lets us label the repository and check in files. Adventurous Ant users could have builds that automatically label the repository after a build, or check files in after updating them.

There are also two CVS reporting tasks, <cvschangelog> and <cvstagdiff>. The <cvschangelog> task generates an XML file containing all the changes that have occurred within a specified date range on CVS modules. The <cvstagdiff> task generates an XML file containing the differences between two CVS tags. Ant ships with two XSL files, changelog.xsl and tagdiff.xsl, both in ANT_HOME/etc, which turn these XML files into attractive hypertext markup language (HTML) reports. You’ll need to refer to Ant’s documentation for more details on these tasks, but we leave you with the following example of how to generate a report from a CVS change log:

<cvschangelog destfile="build/changelog.xml"/>
<xslt in="build/changelog.xml"
       out="build/changelog.html"
       style="${ant.home}/etc/changelog.xsl">
  <param name="title" expression="AntBook ChangeLog"/>
  <param name="module" expression="AntBook"/>
</xslt>

Chapter 13 covers the <xslt> task in more detail.

If CVS isn’t on the path, the <cvs> tasks—as with the other SCM tasks—fail with an OS-specific error code. In Windows, “Create Process” and “code=2” usually appear somewhere in the message.

The standard way of debugging any of these SCM tasks is to

  1. Type cvs, svn, or the equivalent on the command line to verify the program is found.
  2. Run the Ant target in -verbose mode to see the full command.
  3. Run this full command on the command line to see why it doesn’t work.

If the executable is present, the usual cause of failure is either misconfiguration of the Ant task, or, for those tools that need some kind of password, incomplete authentication of the user. Don’t try to use any of the SCM tasks until you have it working on the command line.

You can also give up on the tasks and just call the programs using <exec>:

<exec executable="cvs" failonerror="true">
  <arg line="-r -z7 update -dP" />
</exec>

We generally delegate SCM work (especially updates and labeling) to the continuous integration server. Even so, it’s nice to have a task to use in some parts of the process, such as when labeling the repository before we make a release.

Although the book’s source was developed using CVS, at some point it will be migrated to Subversion. Subversion, also known as svn, is the ultimate successor to CVS for open source and in-house projects. Ant does not and will not ship with Subversion support out of the box. Instead, a separate Antlib provides tasks that act as a thin wrapper around the svn command-line tool. This library can be found on the Ant web site. It’s effectively a third-party task, which we’ll explore next.

9.5. Using third-party tasks

Ant’s core and optional tasks ship with Ant. However, you can also use third-party tasks, which are any tasks that don’t come with Ant itself. We mentioned them earlier in the chapter, and now it’s time to look at some in more detail.

There is an incredibly broad set of third-party Ant tasks. In fact, there are more third-party tasks than there are core and optional tasks. They cover almost everything you need to do in a project, from auditing your source code style to deploying web applications. In this chapter we’ll look at logic and iteration tasks, then at code auditing. In later chapters we’ll explore many other third-party task suites.

Three steps are all it takes to make third-party tasks usable in a build file:

1.  Finding the tasks

2.  Adding the libraries to Ant’s classpath

3.  Telling Ant about the tasks

The final two stages can be merged into one if you keep the JAR files in a well-known place, such as a directory in your SCM repository, and declare this classpath when you tell Ant about the tasks.

The first step is finding the tasks you want. If you download a development tool or other product you want to use, look for the Ant tasks in the download or see if they’re mentioned on the web site. The online documentation of Ant lists some tasks, but, as with any web site, the information listed at http://ant.apache.org/external.html soon goes out of date. If you can’t find what you’re looking for online, you can turn to search engines and the Ant user mailing list.

The Ant tasks should come in a JAR file, perhaps with some other library dependencies, and they need to be on the classpath. The standard ways to make this happen are

  • Drop the JAR files into ANT_HOME/lib. Do this only if the library is to be used by all users of a machine on all projects. Otherwise, it may lead to library version conflicts.

  • Drop the JAR files into ${user.home}/.ant/lib. This is per-user, but it still applies across all projects and is still dangerous.
  • Keep the files in a private directory and add the directory’s contents to the path via Ant’s -lib option. You can include this directory in the ANT_ARGS environment variable for automatic inclusion.

These three options automatically add the task libraries to the classpath, so all you need to do in your build file is declare the mapping from task name to implementation class. However, there’s one more trick you can use, though it requires a little extra coding in the build file: you can explicitly load the tasks using a classpath you set up in the build file. This is the best tactic for a team project, because the JAR files can live in the SCM repository, and the build file declares them. There’s no need for any work by the individual developers.

Adding the files to Ant’s classpath doesn’t make the tasks automatically available inside the build file. Every build file that uses a third-party task must tell Ant to load the tasks, providing the names and/or XML namespaces to use. This is called defining a task, which can be done in several ways.

9.5.1. Defining tasks with <taskdef>

Ant is preconfigured with the knowledge of which Java class implements each of the core and optional tasks. To use a new third-party task in a build file, you need to tell Ant about it. This is what the core tasks <taskdef> and <typedef> do. To define a single task, you must specify the task’s name and the class behind it. The task name can be arbitrary, but it must be unique within the build file.

To demonstrate how to declare a third-party task, we’ll use the Ant-contrib tasks—tasks that provide a lot of extra functionality to Ant. We’ll be exploring them in detail, but first let’s cover the ways of loading them. Given the name of the implementing class, we can tell Ant to bind <if> to this class using <taskdef>:

<taskdef name="if"
   classname="net.sf.antcontrib.logic.IfTask"
   classpath="${ant-contrib.jar}"/>

Our build file now has the capability to use the <if> task in the same manner any other task is used. To use the other tasks in the library, we could add other <taskdef> declarations, but this is needless work. It’s better if the task library lists all its tasks in a properties file, which can be loaded in bulk.

9.5.2. Declaring tasks defined in property files

The Ant-contrib tasks are all listed in a properties file stored as a resource in the JAR: net/sf/antcontrib/antcontrib.properties. It lists the tasks in the JAR and the names by which they should be used, including the <if> task we declared earlier. Here’s a fragment of the file:

# Logic tasks
if=net.sf.antcontrib.logic.IfTask
foreach=net.sf.antcontrib.logic.ForEach
throw=net.sf.antcontrib.logic.Throw
trycatch=net.sf.antcontrib.logic.TryCatchTask
switch=net.sf.antcontrib.logic.Switch

The <taskdef> command can do the bulk of this entire file, defining all the tasks in one go:

<taskdef resource="net/sf/antcontrib/antcontrib.properties"/>
  <classpath refid="tasks.classpath"/>
</taskdef>

Some projects also define datatypes in a separate file, which are declared with a <typedef> statement:

<typedef resource="org/example/types.properties">
  <classpath refid="tasks.classpath"/>
</typedef>

This statement will declare the Ant datatypes, but do this in a new classloader, not the one used for the tasks, even though the same classpath was used in both declarations. Having the classes loaded in separate classloaders can stop the tasks from using the types properly. To load them in the same classloader, set the loaderref attribute of both <typedef> and <taskdef> declarations to the same string. It doesn’t matter what the string is, only that it’s a unique name for the shared classloader.

Bulk loading of tasks through a properties file is efficient, and most third-party libraries come with a properties file for this purpose. One risk with this mechanism is that as the properties files name the tasks, there is no way to prevent clashes between the names of tasks defined in different libraries. This may seem unlikely, but as Ant continually adds new tasks and types, over time it’s almost inevitable—unless we do something to avoid the problem. We need a way of telling Ant about third-party tasks yet keeping them separate from Ant’s own tasks. This is where XML namespaces can help.

9.5.3. Defining tasks into a unique namespace

What happens if you try to define a task or type if something of the same name has already been defined?

  • If the new definition is identical to the original, then the redefinition is silently ignored.
  • If the new definition is identical except for a different classpath, a warning message “Trying to override old definition” appears, and the new definition is accepted.
  • In any other circumstance, the definition is silently accepted.

The result is that you can redefine new tasks with the same name as old ones. This guarantees that a new version of Ant will not break your old build files, even if a third-party task used a name that is now claimed by Ant itself. There’s a price of course: the price of confusion. You won’t be able to use the new tasks, and tools and users of the build file may not realize that a third-party task is being used.

One solution is to give tasks very unique names, often a prefix such as ac—for the Ant-contrib tasks—a prefix that has to be used inside the properties file that defines the tasks. The alternative is to use XML namespaces. Since Ant 1.6, it’s been possible to declare tasks into their own namespace. Once that’s done, its “local” name can be the same as any task in any other namespace, yet there will be no name collision.

To recap the basic rules of XML namespaces that are covered in Appendix B: XML elements and attributes can be declared in a namespace, by first defining a prefix mapping to a URI, then qualifying the element or attribute declaration with the prefix.

For example, when Ant-contrib’s <switch> task is declared in the default Ant namespace, its appears in the file as just the task’s name:

<switch> ... </switch>

If the task were declared in a namespace, the task would need to be accessed via a prefixed name:

Elements and attributes inside the task do not need to be prefixed, unless you want to include a type or task from a different namespace, or to make things clearer to readers. Furthermore, the name of the prefix doesn’t matter, only the URI to which it’s bound. You can reuse the same namespace prefix in different parts of the build file, and in different build files.

To declare a task or type in a namespace, set the uri attribute of the <taskdef> or <typedef> to the namespace you want the task to be defined in. For the task above, we would need to declare it in the http://antcontrib.sf.net namespace:

<target name="define-ac">
  <taskdef resource="net/sf/antcontrib/antcontrib.properties"
    uri="http://antcontrib.sf.net" />
</target>

One complication with XML namespaces is that the namespace needs to be declared, either in the element using the namespace, or in an XML element that contains it. In the example above we’ve declared it in the target, but usually we declare them in the <project> declaration. This lets you use the task anywhere in the project without having to re-declare the namespace.

In this book, we declare most third-party tasks into new namespaces. It may seem complicated at first, but there are long-term benefits with guaranteed unique names. The real benefit comes with Antlib libraries, which we are about to explore.

9.5.4. Defining tasks from an Antlib

Prior to Ant 1.6, the main way for declaring third-party tasks was via property files. This has now been supplemented with Antlibs, which are JARs containing Ant tasks and types—tasks and types that are listed in an XML file. Antlib task declarations are much better because

  • A single Antlib XML file can contain <taskdef>, and <typedef> declarations of tasks, types, and conditions, and <macrodef> and <presetdef> declarations that build up tasks from other Ant tasks. We’ll cover the latter two tasks in the next chapter.
  • Everything in the library is loaded in the same classloader.
  • Ant can automatically load Antlib declarations from special antlib: URIs.

Automatic library loading can dramatically simplify how you use third-party tasks. Instead of having to use <taskdef> or <typedef> in a build file, all you need to do is declare the namespace:

xmlns:ac="antlib:net.sf.antcontrib"

From then on, you can declare tasks or types in that namespace. When Ant first encounters an Antlib namespace declaration, it tries to load the library but silently ignores all failures (it’s exactly equivalent to setting onerror="ignore" inside <typedef>). The moment Ant actually tries to instantiate a task or type in the namespace, it will try again, this time failing with an error message if something went wrong. This will be the usual “failed to create task or type” error message, with an extra Antlib-specific paragraph:

This appears to be an antlib declaration.
Action: Check that the implementing library exists in ANT_HOME/lib or in
        C:Documents and Settingsant.antlib

Antlib libraries can still be manually installed using the <typedef> task. This allows skilled developers to do advanced things such as set up an explicit path if the library isn’t already in Ant’s classpath, or to use the library in the same build file in which the tasks are compiled and packaged.

Let’s explore this in a real set of tasks, the Ant-contrib suite, which provides useful functionality to build files.

9.6. The Ant-contrib tasks

One of the most popular suites of third-party tasks is the Ant-contrib Antlib, from http://ant-contrib.sourceforge.net.

These are invaluable in a big project because they add extra logic and decision-making operations to Ant, including iteration and exception handling. Ant-contrib is a long-standing project, with stable tasks that are widely used. It contains some tasks that aren’t in the Ant core. The reason is that they’re viewed as “too procedural,” which is in opposition to the declarative goal of Ant. Another reason is that they violate Ant’s “rules,” such as the one about properties being immutable. They also cover things that Ant doesn’t, such as C++ compilation and mathematical operations. As a result, any complex project is likely to find something of use in these third-party tasks.

The core of the Ant-contrib suite is its logic tasks. These provide extra structure to Ant, with <for>, <if><then><else>, and <try>/<catch> tasks. Table 9.3 lists the tasks that extend Ant’s logic and execution abilities.

Table 9.3. The Logic/Execution tasks offered by Ant-contrib

Task

Function

<antcallback> <antcall> with declared properties extracted from the called build and passed into the calling project. This lets you get return values from <antcall>
<antfetch> <ant> with return values.
<assert> An assertion facility for Ant. Very similar to <fail> with nested conditions, but you can enable or disable all assertions in a build file by setting the property ant.enable.asserts to true or false.
<foreach> Ant-contrib’s original iterator task: calls a target, <antcall> style, once for each element in a list, fileset, or iterator supplied.
<for> Iterative execution of a nested sequence of tasks. This is the successor to <foreach> and is much more efficient.
<forget> Start a sequence of tasks in a separate thread.
<if> Conditional if/then/else/elseif execution of tasks.
<limit> Run (nested) tasks with a specified time limit.
<outofdate> Compare target files with source files, and execute nested tasks if the target files are considered out of date.
<runtarget> Run a new target (and any dependencies) within the current environment.
<switch> Switch on the value of a string, executing different tasks for each declared case.
<throw> <fail> with the added ability to throw an exception caught in a <catch> statement.
<timestampselector> Not a task: a new selector that selects the oldest or youngest file(s), from a larger set.
<trycatch> Exception handling with try/catch/finally.

Alongside logic come the property operations of table 9.4, tasks that offer simple mathematics and dynamic property evaluation.

Table 9.4. Property tasks of Ant-contrib

Task

Function

<math> Simple equation evaluation.
<osfamily> Set a property to the OS family; the <os family> condition renders this obsolete.
<propertycopy> Copy properties. Property expansion can be used in naming the source or destination, which lets you do simple array-like retrieval.
<propertyselector> A selector of property names that match a specified regular expression.
<pathtofileset> Create a fileset from a path.
<propertyregex> Evaluate a regexp expression.
<sortlist> Sort a list of items into their natural order as defined by the current locale of Ant. Sort orders are not consistent across languages.
<urlencode> Convert a string into an x-www-form-urlencoded escaped representation.
<var> Make a variable or unset Ant properties.

The <var> task does something that’s completely against all of Ant’s immutability rules. Chapter 10 looks at when it’s appropriate to use <var> and how.

There are some extra conditions in the library, all listed in table 9.5. They can be used in <condition>, <fail>, <waitfor>, and Ant-contrib’s own <if> tasks—anywhere a normal Ant condition can be used.

Table 9.5. Extra Ant-contrib conditions to use in build files

Task

Function

<isgreaterthan> Numeric or string (platform’s locale) comparison.
<islessthan> Numeric or string (platform’s locale) comparison.
<ispropertyfalse> Test for a property being false. Ant’s own <isfalse> condition can determine this.
<ispropertytrue> Test for a property being false. Ant’s own <istrue> condition can determine this.

Finally, there are a few other tasks, which are not so easy to classify. We’re not going to cover any of them, but they’re available if ever you have a need for them. There’s also a listener that records how long tasks take to execute. Table 9.6 lists all the tasks.

All these tasks are a download away, at http://ant-contrib.sourceforge.net/. At the time of writing, the version of their library was 1.0b2. Some of the tasks also require extra libraries and dependencies that are listed in the documentation, which you should pick up alongside the ant-contrib.jar file.

Table 9.6. Ant-contrib’s other tasks

Task

Function

<anteclipse> Extract classpaths from an eclipse project file.
<antserver>/<remoteant> Listen for Ant build requests from outside hosts and execute them. There’s no security around these tasks, so they should be treated with caution.
<cc> Published separately: a powerful, portable C/C++ compilation module. Also supports IDL compiles. We covered <cc> in the first edition of this book, but have now removed the chapter from this edition. Download it from the book’s website.
<inifile> Edit windows .ini files.
<post> HTTP post.
<shellscript> Declare a shell-script/batch file inline; this will be saved to a file and executed. Not portable, but possibly useful in some low-level situations.
<stopwatch> Start and stop timing of task execution.
<verifydesign> Examines .class files to verify that parts of a project are decoupled and that there are no imports of some packages by classes in others. This can enforce a strict separation of model and view, or persistence layer and model.

The easiest way to use the tasks is to copy ant-contrib.jar into Ant’s lib directory. We may also need to tell the IDE about it too, adding it to its Ant classpath. We can then use the libraries in any project, by declaring an XML namespace with the URI antlib:net.sf.antcontrib. This is because it’s an Antlib library:

<project name="core-chapter-09"
  basedir="." default="default"
  xmlns:ac="antlib:net.sf.antcontrib" >

To explicitly import the tasks from a different location, we would need to set a path to the location of the JAR, then use a <typedef> declaration:

<target name="load-antcontrib" >
  <typedef
    resource="net/sf/antcontrib/antlib.xml"
    uri="antlib:net.sf.antcontrib"
    onerror="failall">
    <classpath path="${ant-contrib.jar}" />
  </typedef>
</target>

The onerror attribute of <typedef>/<taskdef> has four values: "ignore", "report", "fail", and "failall". The first one does nothing if the tasks cannot be defined for any reason. The report option prints errors but continues the build, while the final two will halt the build if trouble is detected. The default is "fail". It will raise an exception if the task or type does not load but do nothing if the resource itself isn’t found. The strictest option, failall, raises an immediate error if the identified resource/URI isn’t resolvable, and is the best choice for immediately finding a problem. The other options are relevant only for backwards compatibility or for skipping a premature <typedef> in projects in which custom tasks are actually compiled.

Once the tasks are loaded, they can be used alongside core and optional tasks.

9.6.1. The Ant-contrib tasks in action

We’ll now have a quick look at some of the most useful Ant-contrib tasks. This only scratches the surface of the suite, so don’t be afraid to look around the documentation whenever you get a chance. If there’s something you want to do in Ant that it doesn’t appear to do out of the box, the Ant-contrib library should be the next place to look.

For now, we’ll look at how to copy properties, some of the extra logic operations, and exception handling to give readers an idea of what the Antlib can do.

Copying properties

One thing Ant lacks is pointers (or array indexing), there’s no way to refer to a property by way of another property. That’s the kind of weakness that the Ant-contrib team view as a challenge. Their <propertycopy> task lets you use property names to indirectly reference a property, which is a crude way of having pointers or arrays:

<target name="property-copy">
  <property name="X" value="Y"/>
  <property name="Y" value="Z"/>
  <ac:propertycopy name="A" from="${X}"/>
  <echo message="A = ${A}"/>
</target>

The value of ${X} is "Y". The from attribute of <propertycopy> refers to an Ant property name, "Y" in this example. The value of the property Y is "Z", so the output is "A = Z". People use this to do array-like tricks, using <propertycopy> to read and write properties like "hostname[${index}]".

Using if/then/else logic

Another recurrent problem in Ant is performing if/then/else tests and switching logic. This is something the language was never designed for. The logic tasks from Ant-contrib make it possible, so we could fetch a web page only if the server is present and serving up pages without an error code:

<target name="if-get" >
  <property name="localhost" value="http://localhost:8080/" />
  <ac:if>
    <http url="${localhost}" />
    <ac:then>
      <tempfile property="temp.file" suffix=".html"/>
      <get src="${localhost}" dest="${temp.file}" />
    </ac:then>
    <ac:else>
      <echo >skipping fetch of ${localhost}</echo>
    </ac:else>
  </ac:if>
</target>

The <ac:if> element takes a single condition, which can be anything that the <condition> task accepts, including the <and> or <or> constructs. If the condition evaluates to true, all tasks within the <ac:then> section are executed; otherwise the ones within the <ac:else> section execute.

You can accomplish the same thing with conditional targets with their if and unless attributes set to the name of a condition evaluated in a predecessor. The Ant-contrib approach eliminates the temporary property and the need for multiple targets at the expense of hiding the conditional nature of the execution from Ant-aware tools.

Multiple value switching

Along the same vein as the <if> task, the <switch> task controls the execution flow based on a string value:

The <case> task container specifies the value that must equal the <switch> value for the containing tasks to be executed. The tasks under <default> run if the value doesn’t match any of the <case> values. Unlike Java, there’s no fall-through from one case to another, so there’s no need for a <break/> element.

Catching task exceptions

A failing Ant task normally immediately stops the build with a BUILD FAILED banner. If you want the build to continue when a task fails, turn to Ant-contrib’s <trycatch> task. Mirroring Java’s exception handling facilities, <trycatch> has nested <catch> and <finally> containers to allow tasks to execute in those two conditions. Returning to the example of a web page download, we could use it to clean up if a <get> request fails:

<target name="trycatch" >
  <property name="localhost" value="http://localhost:8080/" />
  <tempfile property="temp.file" suffix=".html"/>
  <ac:trycatch
    property="exception.message"
    reference="exception.ref" >

    <ac:try>
      <get src="${localhost}" dest="${temp.file}" />
      <!-- more processing here -->
    </ac:try>

    <ac:catch>
      <echo>Caught: ${exception.message}</echo>
      <ac:throw refid="exception.ref" />
    </ac:catch>

    <ac:finally>
      <echo>cleanup</echo>
      <delete file="${temp.file}" />
    </ac:finally>
  </ac:trycatch>
</target>

Executing this target produces this output when there’s no server:

trycatch:
  [get] Getting: http://localhost:8080/
  [get] To: C:diarycore
ull1319877082.html
  [get] Error getting http://localhost:8080/ to
        C:diarycore
ull1319877082.html
 [echo] Caught: java.net.ConnectException: Connection refused: connect
 [echo] cleanup

BUILD FAILED
C:diarycorecore-chapter-09.xml:118:
  java.net.ConnectException: Connection refused: connect

The <get> failed, invoking the <catch> clause. After printing out the error, we threw the exception again using the <throw> task, which also comes from Ant-contrib. This is an extension of <fail> that takes a reference to an existing exception to throw. At that point, the <finally> clause was executed, invoking the <echo> and <delete> tasks.

If there was a functional HTTP server, the <finally> clause would still run after all tasks in the <try> section:

trycatch:
  [get] Getting: http://localhost:8080/
  [get] To: C:diarycore
ull2021604881.html

 [echo] cleanup
[delete] Deleting: C:diarycore
ull2021604881.html

BUILD SUCCESSFUL

The <trycatch> task is invaluable where you need to clean up when something goes wrong, such as stopping a program or removing temporary files after tests fail.

Iterative execution

Sometimes, you may find yourself wishing there was a way to perform a set of Ant tasks for every file in a fileset, or iterating over a list of values. With the Ant-contrib <for> and <foreach> tasks, such iteration is easy. Both of these tasks will take anything iterable: a list, a list of paths, or any Java datatype that has a method Iterator iterator(). The <for> task is the oldest one; it will invoke a named target once for every item by invoking <ant> and creating a full clone of the project’s current state. This is needlessly inefficient, hence its replacement, the <foreach> task. This takes a sequence of tasks inline, running them once for every item.

Here’s an iteration over the list [1, 5, 10], sleeping for the specific number of milliseconds:

<target name="for">
  <ac:for list="1,5,10" param="delay"
              delimiter=",">
   <sequential>
    <sleep milliseconds="@{delay}" />
    <tstamp>
      <format pattern="HH:mm:ss.SSSS"
        property="tstamp.@{delay}" />
    </tstamp>
    <echo>@{delay}: ${tstamp.@{delay}}</echo>
   </sequential>
  </ac:for>
</target>

The name of the iteration’s value is specified in the parameter attribute. This names a special "@" parameter for the nested sequence. This parameter isn’t an immutable property; it’s a value that changes every iteration. When used in the <sleep> statement, setting the delay to @{delay}, it’s expanded to the current value.

Properties set in the sequence are still immutable; any property set in the first iteration will not change the next time through the loop. We’ll look at this problem with Ant’s macro facility in chapter 10.

for:
      [echo] 1
      [echo] 5
      [echo] 10

The task also takes a path, running the sequence for every file in the path. This is useful for some bulk operations. Here’s a target that will print out every source file:

<target name="for-files">
  <ac:for param="name">
   <path>
    <fileset dir="src" includes="**/*.java" />
   </path>
   <sequential>
    <echo>@{name}</echo>
   </sequential>
  </ac:for>
</target>

Developers could use this task to distribute files to a list of FTP sites or to pass a set of files to a <java> program, one after the other. If the option parallel="true" is set, the iterations are run in parallel. Most Ant tasks are never designed to be thread-safe, so be very careful with this.

Handling out-of-date artifacts

The final Ant-contrib task that we’ll cover is the <outofdate> task. This task runs a nested sequence of tasks if any target files are out of date compared to a set of source files.

Ant’s <uptodate> task compares a set of source files against a single destination file, setting a property if the destination file is up-to-date compared to all the source files. A successor target can be made conditional on this scenario. Ant-contrib’s <outofdate> task extends that with multiple destination files, an optional mapper to specify a mapping from source to destination files, and the ability to declare the actions inline.

One use for it is to add dependency logic to tasks that lack it, such as javadoc:

<target name="javadocs-outofdate" depends="compile"
  description="make the java docs" >
  <ac:outofdate>
    <sourcefiles>
      <fileset dir="src" includes="**/*.java" />
    </sourcefiles>
    <mapper type="glob"
      dir="src" from="*.java" to="${javadoc.dir}/*.html"/>
    <sequential>
      <javadoc
         access="private"
         destdir="${javadoc.dir}"
         packagenames="d1.*"
         sourcepath="src"
         use="true"
         version="true"
         windowtitle="${ant.project.name}"
         failonerror="true">
        <classpath refid="compile.classpath"/>
      </javadoc>
    </sequential>
  </ac:outofdate>
</target>

Wrapping the <javadoc> task with an <outofdate> task, we declare that the source files are all our Java files. Instead of declaring any destination files, we list a mapper from our Java files to the generated HTML pages. The result? javadoc runs only when one of the HTML pages is older than the Java file it documents or when it’s actually absent. This is a simple use of the task; it offers many more options. Note that <outofdate> is not only a task—it’s a condition. You can use the test part of the task inside anything conditional, including <fail>, <condition>, and <waitfor>.

Overall, Ant-contrib represents a way to boost Ant with a broadly usable set of tasks. This quick overview of the library has only explored some of the main tasks it offers. If you’re writing build files for complex projects, there is likely to be something of immediate relevance in there. Download it and see!

Alongside such general-purpose libraries come many that are designed for one specific purpose. We’ll encounter some of those in future chapters. We’ll close this chapter with one such library, Checkstyle, to audit Java source code, as it’s a useful addition to any project.

9.7. Code auditing with Checkstyle

Imagine, whenever you wrote some Java source, the “code police” would review your code and see if it broke any rules set by the team. They could enforce code style policies and highlight possible design errors. This could either be very irritating or very useful, especially on a large project. With consistent code it’s easier for people to work on different parts of the application, and no bits of the application would degrade to low-quality code while the rest of the team was distracted.

Few projects have any full time code police. It’s exactly the kind of repetitive work best delegated to software—in this case a tool called Checkstyle, from http://checkstyle.sourceforge.net. It works as a command-line tool, an Ant task, and an IDE-plugin. It has the capability to check the following, and more:

  • Unused and duplicate import statements
  • Imports of forbidden libraries (such as sun.*)
  • Proper and preferred javadoc tag usage
  • The presence of license headers in all modules
  • The preferred placement of curly brackets
  • The existence of tabs
  • Line lengths
  • Naming conventions for classes, methods, and variables
  • The ordering of modifiers such as public final static
  • Code policies, such as final parameters, whether inline ?: conditions that are allowed or “magic numbers” in the source
  • Bad class designs, including mutable exceptions and public fields

Checkstyle is configured by an XML file that declares which of its built-in or plug-in modules should be run over your code and what options each module has. It ships with a default policy that implements Sun’s coding conventions (http://java.sun.com/docs/codeconv/) and the style rules of Effective Java (2001). Listing 9.3 shows the targets to audit our application.

Listing 9.3. Checkstyle.xml: checking our coding style standards

The <checkstyle> task takes multiple source filesets; we omitted the test fileset for simplicity and because we may want some different rules there. Passing down the classpath of the compiled code is optional, but it helps with some design checks, particularly one that examines thrown exceptions to see if they’re derived from RuntimeException and hence don’t need to be declared. Like <junitreport>, the task has formatters write its output to the console or log file, as well as to XML format for integrated reporting. We can then use XSLT to generate HTML pages. The XSL file to do this is one of those that comes with Checkstyle.

We’ve taken both the XSL sheets and the sample Checkstyle policy file and put them in a directory under SCM. We then commented out most of the checks, especially the whitespace ones. When you start adding this to an existing project, you get a lot of whitespace errors, most of which are irrelevant. Listing 9.4 shows our minimal coding policy.

Listing 9.4. Our checkstyle-policy.xml policy file
<!DOCTYPE module PUBLIC
    "-//Puppy Crawl//DTD Check Configuration 1.2//EN"
    "http://www.puppycrawl.com/dtds/configuration_1_2.dtd">

<module name="Checker">
  <module name="TreeWalker">
    <module name="ConstantName"/>
    <module name="LocalVariableName"/>
    <module name="MemberName"/>
    <module name="MethodName"/>
    <module name="PackageName">
      <property name="format"
          value="[a-z]+(.[a-z_0-9]*)*$"/>
    </module>
    <module name="ParameterName"/>
    <module name="StaticVariableName"/>
    <module name="TypeName"/>
    <module name="EmptyStatement"/>
    <module name="IllegalInstantiation"/>
    <module name="InnerAssignment"/>
    <module name="MagicNumber"/>
    <module name="MissingSwitchDefault"/>
    <module name="IllegalImport"/>
    <module name="RedundantImport"/>
    <module name="UnusedImports"/>
    <module name="LineLength"/>
    <module name="ModifierOrder"/>
    <module name="RedundantModifier"/>
    <module name="InterfaceIsType"/>
    <module name="VisibilityModifier"/>
    <module name="UpperEll"/>
  </module>
</module>

It’s up to every project to choose the rules and the options they want. Individuals cannot override these, which is exactly what you want. Project-wide code rules, by their very nature, should not be something that developers can customize. If you want to see a stricter policy, Ant’s own checkstyle-config can be found in its source tree, under src/etc/checkstyle.config. In an open source project, having machine-enforced rules is one way of making it easier for outsiders to read the source. It can also ensure that any patches submitted to the project follow the rules.

Installing and running Checkstyle

You can obtain the latest Checkstyle release version from http://checkstyle.sourceforge.net; we used version 4.0beta5. The tool comes with a lot of extra JAR files; we were reluctant to put them all into our main antlib directory. Instead we just add Checkstyle’s directory to the classpath when we run the targets.

ant -lib ~/bin/checkstyle-4.0-beta5 checkstyle

When we run this, we get a trace of errors, some generated reports and finally a halted build. Figure 9.1 shows a report on one file.

Figure 9.1. Stop! It’s the code police! The tool also has caught the fact that we’ve forgotten to make a constant final. The case rule of constants (all capitals) did not match that of a variable.

Checkstyle is useful in any team project. Adopting it can be painful, as it can throw many errors at the beginning. Start by commenting out most of the rules and focusing on the serious problems. Ant itself uses a far stricter policy set than that of listing 9.4, but it doesn’t require the task to succeed before releasing a distribution. A very strict project may want to do just that.

One thing to remember about Checkstyle is that it’s a style checker. It doesn’t check that the code works, only that the source looks right. Other tools, such as EMMA, can do that (http://emma.sourceforge.net). However, Checkstyle does look at the code and, as figure 9.1 demonstrates, it can find many defects. Indeed, the biggest problem in retrofitting it to an existing project is just how many problems it finds.

It takes only a few more lines in a build file to add code-auditing from Checkstyle to a project, and it can find bugs that haven’t been caught in testing. It’s a fantastic example of a new feature a team can add to a build once Ant is in charge of the compile and test process. It also shows how third-party tasks can radically improve what the build file does for the developer team.

9.8. Summary

Ant provides core tasks compiled into ant.jar and also ships with optional tasks that typically require additional components in order to function properly. Many third-party tasks also exist, adding extra functionality to your build process.

To use third-party tasks, you need to add the libraries to the classpath, either by adding them to a suitable directory, using the -lib argument or by adding them explicitly in a <taskdef> or <typedef> statement. These two tasks are how you can tell Ant about new task libraries. With the Antlib feature, you can even have task libraries loaded just by stating the right URL in an XML namespace declaration.

In this chapter, we looked at the various SCM tasks as a sample of Ant’s optional tasks, then we looked at two useful third-party libraries: Ant-contrib, and Checkstyle. Ant-contrib provides many extra features to Ant—features big and complex projects can use—while Checkstyle automates the “code police.” Both are great.

Ant’s web site documentation and Ant’s web site itself list many third-party tasks. If Ant doesn’t provide what you need, check with the Ant web site or with the vendor of the product you’re automating around. If all else fails, check with the Ant user email list.

Best practices with third-party and optional tasks

We routinely use Ant’s optional tasks, as well as third-party and custom tasks. Some of the optional tasks, such as <junit> and <junitreport>, are almost mandatory.

Don’t be put off by tasks that require you to download additional dependencies. Typically, dropping JAR files into ANT_HOME/lib is all that it takes to get up and running with the optional tasks that require an external library, such as <ftp>. Third-party tasks can also be added to this directory, or added to the classpath with a -lib argument. Explicitly declaring the classpath in a <taskdef> declaration adds some work, but doing so can help the build file being used by other developers.

In this book, we declare all third-party tasks into their own namespaces. This will cleanly separate Ant tasks from third-party tasks. It also enables the Antlib library loading mechanism, whereby the tasks and types of a library are automatically loaded just by identifying the package in an antlib: URL. More and more third-party tasks provide antlib.xml descriptors to enable this feature.

If you can, keep task libraries and their dependencies under source code control. Building your system should be as easy as checking out the repository and typing ant or maybe ant -lib with an SCM-managed directory.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset