One of the principles of good software engineering is the daily build. If you build your software every day then it acts as a barometer, showing the state of the project. If something’s broken, it gets noticed faster, so it gets fixed faster. The daily build becomes the heartbeat of the project. These are the reasons why Microsoft builds the five gigabytes of Windows source code every day, even though it takes twelve hours — on very powerful and expensive servers — to do so.
As you might imagine, in order to be able to perform daily builds, the build process has to be automatic and repeatable. It’s no good to rely on some harangued colleague typing a complicated command in a Command Prompt window to invoke the Java compiler with a classpath as long as this article. What’s needed is Another Neat Tool in the developer’s armoury. What’s needed is Apache Ant.
Part 1 of this tutorial starts below.
Part 2 of the tutorial starts here.
Key Takeaways
- Apache Ant is a Java-based, open-source build tool from the Apache Software Foundation, widely adopted for J2EE projects and supported across various Java development environments.
- Ant automates the build process using XML files to describe the build tasks, ensuring repeatability and platform independence, crucial for daily builds in software development.
- Beyond Java compilation, Ant can manage diverse build tasks like fetching code, running tests, generating documentation, packaging applications, and more, all triggered by a single command.
- Setting up Ant involves downloading the binary distribution, configuring environmental variables like ANT_HOME and JAVA_HOME, and ensuring Ant commands can run from any command line interface.
- Ant structures its build process around projects, targets, and tasks, where a project contains at least one target, each target comprises multiple tasks, and tasks are the actual commands executed by Ant.
- Advanced Ant functionalities include managing target dependencies, utilizing properties to avoid repetition, and customizing builds with command-line overrides, enhancing flexibility and maintainability of build configurations.
About Ant
Ant is an open-source Java-based build tool from the Apache Software Foundation. It’s rapidly become the de-facto build tool for J2EE projects, so for developers, it’s certainly worth becoming familiar with the basics of Ant, at the very least. Many modern Java development environments also feature Ant support.
Ant uses build files, which are simply XML files that tell the Ant program what to do. And because Ant is a Java program, it’s a cross-platform tool; you can move your build files from one operating system to another unchanged, providing you haven’t used any platform-specific features.
Although Ant is a build tool, don’t think that it’s only good for compiling Java code. Its name is apt; think of all the heavy loads a line of ants can carry on their backs. Ant is a veritable Swiss army knife of functionality. For example, it’s entirely feasible to use Ant to get the latest code from your source code repository, compile that code, run the unit tests and generate a report with the results, generate the Javadoc documentation, package the code into a J2EE application, deploy that application to an application server …and then send yourself an email when that’s all done. Phew! Ant can do all of that in response to a single command from you if you want it to. Not bad, eh? Make no mistake: being able to write and maintain Ant build files is a very useful skill for any Java developer to possess.
Obtain and Install Ant
Ant can be downloaded from the Downloads page of the Apache Ant Website. Like all Apache downloads, it’s available in both binary (pre-compiled) and source code form. I recommend that you download the binary distribution, at least to start with.
Locate the file that you downloaded, which will be named something like apache-ant-1.6.2-bin.zip. At the time of writing, 1.6.2 is the latest version of Ant. There is no installer program with Ant, so all you need to do is unzip the download to the directory in which you want to install the software, for example, C:Program Files. If you use WinZip to extract the files from the Zip file, make sure that the Use Folder Names option is checked so that the Ant installation folder structure is preserved.
Before we can get started with Ant, we need to modify the system path so that Ant can be started from any Command Prompt, irrespective of what the current directory is on the file system. We also need to add some system-wide environment variables that Ant needs if it is to operate correctly. The following instructions are for Windows XP; users of other operating systems should consult their documentation to find out how to modify the system path, and add and edit environment variables.
Right-click on the My Computer icon and select Properties. Then click the Advanced tab and select the Environment Variables button. Depending upon whether you’re the administrator of the computer or not, you can either add the Ant installation folder to the global path — which affects all users — or to the path for just your user account. If the New, Edit and Delete buttons are greyed out in the System Variables group, then you don’t have permissions to modify the global path and can only set Ant up for your own user account. Don’t worry, it will still work perfectly well. Let’s assume that you’re going to do just that.
If you haven’t already got a user variable named Path, click the New button and set the Variable Name to Path
and the Variable Value to the full path to the bin folder in the Ant installation, for example, C:Program FilesApache Groupapache-ant-1.6.2bin, then click OK. If you already have a Path
user variable, highlight it in the list, then click the Edit button. Add a semi-colon to the end of the existing path, then add the full path to the bin folder as described above. You might end up with a path looking vaguely like this: C:Program FilesWidgetWareWidget; C:Program FilesApache Groupapache-ant-1.6.2bin
Click OK when you’ve finished making the edit.
The next step is to set up a new environment variable called ANT_HOME
. While you’re still within the Environment Variables dialogue, click the New button in the User variables group and create a new variable with a name of ANT_HOME
and a value that corresponds to the Ant installation folder. For example, C:Program Filesapache-ant-1.6.2 Note that there’s no bin on the end this time. Click OK.
Finally, we need to set up another new environment variable: one that will tell Ant where your Java Development Kit (JDK) is installed. Follow the procedure described above for the ANT_HOME
environment variable, but this time, set the Variable name to JAVA_HOME
and the Variable value to the location at which your JDK is installed. On my machine it’s C:Program FilesJavajdk1.5.0, but yours may be different. Now click OK several times to close all the open dialogues and property sheets.
You can confirm that these steps have been successful by opening a Command Prompt and entering set ant_home
followed by Enter, and then set java_home
followed by Enter. The respective paths that you entered in the steps above should be displayed. If this doesn’t work, try logging off and back on again.
We also need to verify that Ant is installed correctly and ready to go. In the same Command Prompt window, type ant –version
followed by Enter. If all is well, then Ant should respond by telling you which version it is and when it was compiled. If this is the case then we’re ready to start learning more about Ant and to write our first build file!
Good Things Come In Threes: Projects, Targets And Tasks
The three basic concepts in Ant are the project, targets and tasks. Actually, there are also properties, but who ever heard of good things coming in fours? The concepts are really simple: each Ant build file contains one and only one project, and that project must contain at least one target, but will usually contain more. Also, each target contains tasks. Let’s take a look at the project part of all that by diving right in and starting to write a build file.
Use a text editor to create a new file named build.xml and save it somewhere. You can use Notepad for this task, or you can use a fancy syntax-highlighting editor — it really doesn’t matter. Type or paste in the following:
<?xml version="1.0" encoding="UTF-8"?>
<project name="helloworld" default="compile" basedir=".">
<description>
Build file for the Hello World application.
</description>
</project>
The first line is simply a standard declaration that says that this is an XML 1.0 document that uses the UTF-8 character encoding. You might have come across this line, or something similar, before. Ant doesn’t actually require this to be present, but it’s a good idea to include it because Ant build files are XML documents and valid XML documents must start with this declaration.
Next, there’s a project tag with a name attribute
. This is simply a name that we want to give the project; in this case, it’s the spectacularly unimaginative "helloworld"
. The project’s name is used when we call Ant build files from other Ant build files, which we don’t need to worry about for now.
The default
attribute tells Ant which target to execute when no specific target is supplied on the command line. More about that later.
Finally, the basedir
attribute is the path to the directory that Ant should use when working out paths. More often than not, it will be set to .
(a single period), which is Windows and UNIX shorthand for referring to the same directory that the file itself is in. In other words, if you saved your build.xml file in C:SitePoint TutorialsAnt, then the basedir
attribute would have the value C:SitePoint TutorialsAnt
; when Ant works out any relative paths later, it uses that as a starting point.
Nested within the project
tag is a description
tag. This is where you can provide a more detailed description of the project that will get output if the person using the build file requests help on the build file.
As things stand, this build file isn’t very useful. In fact, it’s not even valid because we haven’t defined any targets. A target is a set of tasks that you want to be executed. You can tell Ant which target(s) you want to be executed and, when no target is given, the project’s default target is used. Let’s make our build file do something useful and add a target:
<?xml version="1.0" encoding="UTF-8"?>
<project name="helloworld" default="compile" basedir=".">
<description>
Build file for the Hello World application.
</description>
<target name="compile" description="Compile all sources.">
<mkdir dir="classes" />
<javac srcdir="src" destdir="classes" />
</target>
</project>
Here, we’ve nested a target
tag within the project
tag and set its name
and description
attributes. Again, the description gets output if the user of the build file requests help on it. We’ll see how to do that later. Two tasks are nested within the target
tag: mkdir
and javac
. Ant comes with more built-in tasks than you can shake a stick at; see the online manual to get an idea. If these aren’t enough, you can always extend Ant with optional tasks that others have written. If that’s still not enough for you, not only are you pretty demanding, but you can always write your own Ant tasks using Java (naturally).
The mkdir
task, as its name suggests, makes a directory. In this case, it makes the classes
output directory into which our compiled Java classes will go. Remember that this happens relative to the location specified using the basedir
attribute of the project
tag. So, using the example from earlier, our mkdir
task would create a classes
directory directly under C:SitePoint TutorialsAnt.
Most Ant build files will use the javac
task at some point. As you might have guessed, it’s a wrapper around javac.exe — the Java compiler. In this case, we’re using the srcdir
attribute to tell it to find Java source code under the src
directory, and to output the compiled classes to the classes
directory.
Important: Ant is a harsh mistress; if you don’t create the classes directory first, the javac task will fail when it attempts to output some files there. That’s why we had to use the mkdir task first.
That’s it! We’ve defined our first target and used two built-in Ant tasks. If you have some simple Java source code that doesn’t depend on any other libraries on the classpath — I’m talking about a “Hello World” level of sophistication here — and that source code is located in the src
directory, then this build file will compile the classes into the classes directory. The only part of Ant that’s left to learn is how to run the build file! Well, not quite. But let’s see how to do it anyway.
Using Ant
As I said earlier, many modern Java IDEs have integrated support for Ant, but before exploring on your own whether yours does, let’s learn how to run Ant the way nature intended: from the command line.
Open a Command Prompt at the directory in which your build.xml file is saved. Now type ant –projecthelp
, followed by Enter. Ant should display the following information:
Buildfile: build.xml
Build file for the Hello World application.
Main targets:
compile Compiles all sources.
Default target: compile
Ant tells us the name of the build file, the project description, and provides a list of the targets within the build file together with their descriptions. Note that if you don’t supply a description for a target, then that target won’t get listed at all as part of the project help. So unless you’re able to look at the source of the build file, you won’t even know the target is there.
Ant also tells us what the default target is. In this case, it’s the compile
target. Notice that we didn’t have to tell Ant what our build file was called. Ant is hard-wired to look for a build file named build.xml in whichever directory you happen to be in when you type the ant
command. That doesn’t mean that you have to call all your build files build.xml; it’s just a convenient feature. If you use another name, you should use the –f
command line switch to tell Ant what your build file is called. For example, ant –f build_me.xml
.
Now, type ant
and press Enter. You should see something similiar to the following:
Buildfile: build.xml
compile:
[mkdir] Created dir: C:SitePoint TutorialsAntclasses
[javac] Compiling 1 source file to C:SitePoint TutorialsAntclasses
BUILD SUCCESSFUL
Total time: 2 seconds
The above code shows that Ant found build.xml and executed its default target of compile
. If you inspect that classes directory, you should find some 100% pure Java in there. Congratulations! You’ve successfully used Ant to compile some Java code! Ant displays in square brackets the name of each task as it executes, and also helpfully tells us how long the build took. This is a neat feature when your builds take hours and you want to bore people at parties by talking about how insanely complicated your project is.
One last thing to try for now: type ant
again and press Enter. You’ll see this output:
Buildfile: build.xml
compile:
BUILD SUCCESSFUL
Total time: 1 second
In this case, Ant didn’t do anything! It’s intelligent enough to realise that the code was already compiled and didn’t need to be compiled again. Ant practises build avoidance. This means that it checks the timestamps of files to determine whether they’ve changed or not, and only compiles files that have changed, thus reducing build times, often drastically on large projects.
In Part 1 of this series, we learnt why using Ant to do builds is a good idea, and were introduced to the core Ant concepts of the project, targets and tasks. We also wrote and executed a simple build file.
Now, let’s take a look at some of the other ways that Ant can make our lives as Java developers easier.
Target Dependencies
Ant targets can depend on other targets. When you think about it, this is actually really useful. You could have a target for creating a JAR file that depends on a target for compiling the source code. Let’s add two more targets to our build.xml file (note that a complete version of build.xml is available for download):
<?xml version="1.0" encoding="UTF-8"?>
<project name="helloworld" default="compile" basedir=".">
<description>
Build file for the Hello World application.
</description>
<target name="compile" description="Compile all sources.">
<mkdir dir="classes" />
<javac srcdir="src" destdir="classes" />
</target>
<target name="clean" description="Clean up output directories.">
<delete dir="classes" />
</target>
<target name="rebuild" depends="clean,compile"
description="Cleanly compiles all sources." />
</project>
I’ve introduced some new syntax here. Let’s take the clean
target first. This contains a delete
task that simply deletes the classes
output directory and its contents. Ant’s delete
task is a versatile little fellow: it can delete files and directories in various combinations and ways. We also have a rebuild
target that doesn’t contain any tasks at all! What’s going on here?
You’ll notice that the rebuild
target has a new depends
attribute set to clean,compile
. This means that the rebuild
target should first execute the clean
target, before it executes the compile
target. So the classes
output directory will be deleted, then re-created and the Java code re-compiled. In other words, a full rebuild occurs, which is useful when you want to compile absolutely everything, and make sure that you haven’t got any old class files lying about for classes whose source code you’ve subsequently deleted. Having obsolete classes in your classpath without realising it can cause you to waste hours needlessly debugging.
Important: Ant only executes targets once, even when more than one target depends on it.
Run the rebuild
target using ant rebuild
and observe the output. We can now perform a clean build, safe in the knowledge that we’re using only those classes we think we’re using!
DRY Using Properties
When programming, it’s important to follow the DRY principle: Don’t Repeat Yourself. If you’re eagle-eyed, you may have noticed that some duplication is present in our build file as it stands. The classes
directory is mentioned by name three times: twice in the compile
target, and a third time in the clean
target. What we need is a way to introduce some indirection, so that if we suddenly decide we’d like our compiled Java code to end up in a directory named bytecode
, instead of classes
, we only have to make the edit once. Fortunately, Ant’s got it covered with properties.
Let’s examine the syntax. This is what our build file looks like once we add a property to store the name of the output directory:
<?xml version="1.0" encoding="UTF-8"?>
<project name="webforum" default="compile" basedir=".">
<description>
Build file for the WebForum application.
</description>
<property name="classes.dir" location="classes" />
<target name="compile" description="Compile all sources.">
<mkdir dir="${classes.dir}" />
<javac srcdir="src" destdir="${classes.dir}" />
</target>
<target name="clean" description="Clean up output directories.">
<delete dir="${classes.dir}" />
</target>
<target name="rebuild" depends="clean,compile"
description="Cleanly compiles all sources." />
</project>
Properties are simply name/value pairs. In this case, the name is classes.dir
and the value is classes
. We actually use the location
attribute to set the latter because we’re dealing with a file path that we want evaluated relative to the value set for the basedir
attribute. For other types of properties, use the value
attribute. When you want to refer to the actual value that the property represents, simply use the ${property name}
syntax, for example, ${classes.dir}
. If you try to use the build file now, you should find that it works exactly as before; the important point is that it’s easier to maintain.
Let’s suppose that for some bizarre reason we really did want our compiled classes to go into a directory named bytecode
instead of classes
. How would we achieve this? We could edit the property declaration and benefit from having only to make the change in one place, but I want to show you another way that’s very powerful.
Ant lets you override properties on the command line. You may have come across Java’s -D
switch, which lets you set a system property. Ant uses the same syntax. Therefore, run ant –Dclasses.dir=bytecode rebuild
and observe the results.
Our compiled classes end up in bytecode
instead of classes
. Were you expecting the classes
directory to be deleted, because we told Ant to run the rebuild
target, which, in turn, calls the clean
target? If you were, be sure to remember that the clean
target also refers to the value of ${classes.dir}
, which we passed in. For this reason, the old classes
directory is left in place. Also, note that there is no space between the -D
and the property that’s being overridden.
Generally, you’ll only override properties this way if it’s a change that you want to make for that build only. If we permanently wanted to use bytecode in lieu of classes, we would edit the property declaration in the build file. Remember: a good build process is repeatable.
Hiding Targets
Sometimes, when you’re writing build files, you’ll want to hide targets so that they can’t be executed directly. You may wonder what use a target is if it can’t be executed directly. Well, it’s useful to be able to create so-called internal targets that do useful work for other targets that are visible. To hide a target, just place a minus sign before its name. Add the highlighted code below to our build file:
<?xml version="1.0" encoding="UTF-8"?>
<project name="helloworld" default="compile" basedir=".">
<description>
Build file for the Hello World application.
</description>
<property name="classes.dir" location="classes" />
<property name="dist.dir" location="dist" />
<property name="dist.jarfile" value="helloworld.jar" />
<target name="compile" depends="-init" description="Compile all sources.">
<mkdir dir="${classes.dir}" />
<javac srcdir="src" destdir="${classes.dir}" />
</target>
<target name="clean" description="Clean up output directories.">
<delete dir="${classes.dir}" />
</target>
<target name="rebuild" depends="clean,compile"
description="Cleanly compiles all sources." />
<target name="-init">
<!-- Create the time stamp. -->
<tstamp>
<format property="TODAY_UK" pattern="dd MMM yyyy HH.mm" locale="en_GB" />
</tstamp>
</target>
<target name="dist" depends="rebuild"
description="Creates the binary distribution.">
<mkdir dir="${dist.dir}/${TODAY_UK}" />
<jar basedir="${classes.dir}"
destfile="${dist.dir}/${TODAY_UK}/${dist.jarfile}" />
</target>
</project>
There are quite a few changes here! The compile
target now depends upon the hidden init
target. The init
target itself uses Ant’s tstamp
task to assign a date and timestamp to the new property TODAY_UK
using a UK date and time format. We’ll use this property later in the build file, to create a new directory with a name that reflects the date and time at which the build was run. We hide this target because, on its own, it doesn’t do anything very useful, so we don’t want to invoke it from the command line.
Moving back to the top of the build file, we’ve set up a couple of new properties to define a dist
output directory, and the file name of an output JAR file: helloworld.jar
.
Finally, we’ve added a new dist
target that depends on the rebuild
target. Our old friend the mkdir
task is used again. It uses both the dist.dir
and TODAY_UK
properties to create an output directory in which the JAR file will go.
Ant’s jar
task is then used to create the JAR file. The jar
task can accept a lot of different parameters, but here we’re setting just two of them. The basedir
attribute tells the jar
task where to find the compiled Java classes that we want to put into the JAR file, while the destfile
attribute specifies the name of the output JAR file itself.
If you now enter ant dist
into the Command Prompt, you should see output similar to this:
Buildfile: build.xml
clean:
[delete] Deleting directory C:SitePoint TutorialsAntclasses
-init:
compile:
[mkdir] Created dir: C:SitePoint TutorialsAntclasses
[javac] Compiling 1 source file to C:SitePoint TutorialsAntclasses
rebuild:
dist:
[mkdir] Created dir: C:SitePoint TutorialsAntdist26 Feb 2005 12.38
[jar] Building jar: C:SitePoint TutorialsAntdist26 Feb 2005 12.38helloworld.jar
BUILD SUCCESSFUL
Total time: 2 seconds
Notice that Ant traversed the tree of dependencies that we’ve created. We told it to execute the dist
target, which depends on the rebuild
target, which in turn depends on the clean
target, followed by the compile
target. The compile
target itself depends on the –init
target. Ant automatically unravels all this, and executes the targets in the following order: clean
, -init
, compile
, rebuild
and finally dist
.
One final point to note is that, if you peek inside the helloworld.jar
file that Ant built (you can use WinZip to do this), you’ll see that Ant has helpfully automatically added a MANIFEST.MF
file, which contains brief details of the build, such as the version of Ant and the Java compiler that were used. On my machine, it looks like this:
Manifest-Version: 1.0
Ant-Version: Apache Ant 1.6.2
Created-By: 1.5.0-b64 (Sun Microsystems Inc.)
Of course, you can always override this behaviour and get Ant’s jar
task to use your own manifest file if you need to. The details are included in the Ant manual’s documentation for the jar
task.
Target Aliasing
A technique that I often use in my build files is target aliasing. This means that I define a target that does nothing but invoke another. This may seem like a waste of time, but consider the scenario of providing a target that displays some simple help text to the user via Ant’s echo task:
<target name="help" depends="usage" />
<target name="usage" description="Display usage information.">
<echo message=" Execute 'ant -projecthelp' for build file help." />
<echo message=" Execute 'ant -help' for Ant help." />
</target>
The help
target is aliased to the usage
target by creating a dependency on the usage
target. Now the user of our build file can get online help by typing either ant usage
or ant help
. Note that the help
target doesn’t appear in the target listing displayed by Ant’s –projecthelp
option, because we didn’t provide a description for it. For an additional refinement, make either the help
or usage
targets the default target, so that the user can get help if they just enter ant
, with no arguments, on the command line.
Tip: Seriously consider making your build file user-friendly by making its default target one that doesn’t do anything potentially time-consuming and/or dangerous! Displaying help is a good candidate for the default.
Awesome Ant
We’ve really only just scratched the surface of Ant’s capabilities, but I hope I’ve given you a flavour of just how flexible and powerful a tool it is. In fact, you’d be really hard-pressed to think of anything useful that you’d want to do in the realm of Java development that Ant can’t handle.
It’s worth taking the time to look through the list of core tasks that Ant offers, and experimenting with some of them. If you go to the folder in which you installed Ant, you’ll find a docs
folder. Within this is an index.html
file that will open your very own local copy of the Apache Ant Website. This contains a link to a local copy of the Ant manual, which is generally well-written because Ant is a mature program now. The manual also has lots of links to some great external resources on Ant. Happy building!
Frequently Asked Questions about Apache Ant
What is the main purpose of Apache Ant in Java development?
Apache Ant is a Java-based build tool. In theory, it is kind of like the make, but without make’s wrinkles and with the full portability of pure Java code. It is used primarily for building and deploying Java projects but can be used for any task that can be described in terms of targets and tasks. It provides a number of built-in tasks allowing you to compile, assemble, test, and run Java applications. Ant can also be used effectively to build non-Java applications, for instance, C or C++ applications.
How can I get a list of build targets in Ant?
To get a list of build targets in Ant, you can use the command ‘ant -p’ or ‘ant -projecthelp’ in the command line. This will display a list of all available targets in your build file. Each target represents a step in the build process, such as compiling source code or creating a JAR file.
How does the delete task work in Apache Ant?
The delete task in Apache Ant is used to delete files or directories. You can specify the files or directories to be deleted either directly or through a fileset. The delete task will not delete directories that are not empty unless you set the ‘includeEmptyDirs’ attribute to ‘true’. It’s important to use this task carefully, as deleted files cannot be recovered.
What are targets in Apache Ant and how are they used?
Targets in Apache Ant are collections of tasks that are executed as a unit. Each target has a name and contains a series of tasks to be executed. Targets can depend on other targets, and the dependencies are executed before the target itself. Targets are specified in the build file and can be run individually using the command ‘ant target-name’.
How can I use Apache Ant to build non-Java applications?
While Apache Ant is primarily used for building Java applications, it can also be used to build non-Java applications. For instance, you can use Ant to compile C or C++ code by using the ‘cc’ task. You can also use Ant to execute shell scripts or batch files using the ‘exec’ task. The flexibility of Ant makes it a powerful tool for any build process.
How can I specify dependencies between targets in Apache Ant?
In Apache Ant, you can specify dependencies between targets using the ‘depends’ attribute. For example, if you have a target ‘compile’ that depends on another target ‘init’, you would specify this as follows:
How can I use Apache Ant to run unit tests?
Apache Ant can be used to run unit tests by using the ‘junit’ task. This task runs tests written using the JUnit testing framework. You can specify the test classes to be run, and Ant will produce a report of the test results. This makes it easy to integrate unit testing into your build process.
How can I use Apache Ant to create a JAR file?
You can use the ‘jar’ task in Apache Ant to create a JAR file. This task takes a set of files and packages them into a JAR file. You can specify the files to be included in the JAR file either directly or through a fileset. You can also specify the manifest file to be used for the JAR file.
How can I use Apache Ant to compile Java code?
The ‘javac’ task in Apache Ant is used to compile Java code. You can specify the source files to be compiled either directly or through a fileset. You can also specify the classpath to be used for compilation. The ‘javac’ task produces .class files as output.
How can I extend Apache Ant with custom tasks?
Apache Ant can be extended with custom tasks by writing a Java class that extends the ‘org.apache.tools.ant.Task’ class. This class should override the ‘execute’ method to perform the desired task. Once the class is written, it can be used in a build file by using the ‘taskdef’ task to define the new task.
John is a J2EE developer based in the UK, with a particular interest in user interface development and Web standards. John's Weblog and knowledge base is at http://www.johntopley.com/.