If you wanted to extend Ant's functionality to provide notification when certain steps in the build process are completed or are in progress, you can create a class to listen to the Ant process as shown in the following example.
You can create a class that implements the
BuildListener interface. Using this class, you can catch each event that is part of the
public void buildStarted(BuildEvent event); public void buildFinished(BuildEvent event); public void targetStarted(BuildEvent event); public void targetFinished(BuildEvent event); public void taskStarted(BuildEvent event); public void taskFinished(BuildEvent event); public void messageLogged(BuildEvent event);
BuildEvent event object contains the following methods by which you can obtain information about the current status of the build:
public Project getProject() ; public Target getTarget() ; public Task getTask(); public String getMessage(); public int getPriority(); public Throwable getException();
So if you wanted to write a reporting tool, you need only create a class that implements the
BuildListener interface and process the
BuildEvents as needed by your design. Because Ant initiates the Java classloader, you must specify the listener as part of your command line arguments. For example:
ant -listener org.apache.tools.ant.XmlLogger
This listener is included with the Ant distribution and outputs an XML representation of the build process to a file called "log.xml".
The above example shows how to extend the functionality of build reporting. It is of more interest to show how to extend the functionality of the build itself. For that, I've chosen to work through an example in which two files are copied from one machine to another machine, which then performs the Ant operation.
To do that, you can extend Ant by creating custom
Task objects for the remote
copy and remote
ant commands. Here's an explanation of the remote copy task definition.
RemoteTask object extends the
Task object. The
RemoteTask object performs all of the functionality necessary for the maintenance of the connection between the local and remote machines. In this case, the connection is a socket-level connection.
RemoteTask object contains the following method declaration that is necessary for any object that is to extend the
public void execute() throws BuildException
The Ant processor calls this method after all of the attributes have been set. Any custom
Task object must override this method.
RemoteCopyTask performs the steps required to execute the remote copy operation. The copy operation is on the local machine and transfers files from the local machine to the remote machine. Some key things to notice in the
RemoteCopyTask code are the three accessor methods that allow the creator of the Ant buildfile to set the name, directory, and file type of the file to be transferred.
RemoteCopyTask, which is run on the local machine, creates a
Command object. The
Command object loads the file into a byte array to prepare for the transfer to the server. On execute, this command object is serialized and passed to the remote machine.
RemoteAntHandler object receives the
Command object from the
RemoteAntHandler then deserializes the object and determines what command to execute. At this point, I have simplified the example and included both commands in the same handler as different branches of the
if statement. Ideally, another framework would let the server process those commands more efficiently.
Because the received command is a
copy command, the handler will write the file to disk with the filename and directory as specified in the
In addition to the remote
copy command, I have included a remote
ant command. In this case, the local machine can execute the
ant command on the remote machine.
I use the
RemoteAntTask, which again extends the
RemoteTask object. The
RemoteAntTask simply sets the command to Ant. Future expansions of this task include the addition of a buildfile specification and additional functionality contained in the original
ant command itself.
RemoteAntHandler object receives and then deserializes the
Command object, it will determine that it should invoke the
ant command. That is handled by having the server spawn another process calling
ant. Why spawn another process? Due to the architecture of the Ant code, it's not currently possible to call
ant and maintain the JVM. So I've spawned another process, using the
RunProcess object. Please note that the script that invokes the
RemoteServer specifies some command line arguments such as deployment directory. That was done to limit the potential harm that an errant buildfile could cause on the remote machine.
The last step in extending Ant to perform the remote commands is to incorporate these commands in the buildfile. The following buildfile includes the new commands:
<project name="foo" default="ant" basedir="."> <target name="init" > <taskdef name="remoteCopy" classname="local.RemoteCopyTask"/> <taskdef name="remoteAnt" classname="local.RemoteAntTask"/> </target> <target name="deploy" depends="init2"> <remoteCopy machine="machinename.groupserve.com" port="9090" directory="e:\deve\ant\article\deploy" filetype="text" filename="build.xml" /> <remoteCopy machine="machinename.groupserve.com" port="9090" directory="e:\deve\ant\article\deploy" filetype="binary" filename="app.jar" /> </target> <target name="ant" depends="deploy"> <remoteAnt machine="machinename.groupserve.com" port="9090" /> </target> </project>
Here's the first line of interest:
<taskdef name="remoteCopy" classname="local.RemoteCopyTask" />
This line creates a
taskdef task that associates the name
remoteCopy with the class contained in the file
local.RemoteCopyTask. From this point forward, I can call the task
remoteCopy, and my new code will be executed. For example:
<remoteCopy machine="machinename.groupserve.com" port="9090" directory="e:\deve\ant\article\deploy" filetype="text" filename="build.xml"/>
remoteCopy task will execute the
RemoteCopyTask object and in doing so, connect to the machine/port specified and copy the file over. That task includes properties identifying the server/port of the
RemoteAntServer. Also, please note that the three file-related properties correspond with the accessor methods contained in the
RemoteCopyTask object. Those values are converted into calls to those methods.
remoteAnt task has been defined by using a similar
<taskdef name="remoteAnt" classname="local.RemoteAntTask"/>
The following task will execute the
ant command on the remote machine:
<remoteAnt machine="machinename.groupserve.com" port="9090" />
Again, note that several command line tags are given when the server starts to set up the Ant task.
To run the example, please perform the following steps:
- Use the Ant buildfile build.xml jar to build the article code on the local machine
- Copy the jar file to the remote machine
- Execute the remote.bat (or rework for a Linux/Unix machine on the remote machine)
- Edit the build.xml file to use machine names and ports appropriate to your environment
- Use the Ant buildfile build.xml to execute the build and deploy functionality on the local machine
If you've performed the above steps, you should notice that the jar file and build file have been transferred to the remote machine and that the Ant task has been performed on the remote machine.
So, you've read through all of this. What have you learned?
The biggest takeaway from this article should be the importance of a build process to construct your environment in an effective and efficient manner. With that understanding, it is less important that you use Ant or some other homegrown scripting mechanism. However, I feel that Ant is an easy-to-learn platform-independent tool that provides expansion as needed. The XML involved in the buildfile is easy to read and understand, and a large number of already supported commands perform the vast majority of your build tasks without expansion. If you find a limitation, you can expand Ant to include your modifications.
Please review the Ant user guide found in Resources below to expand your understanding of the predefined tasks included with Ant. I hope I have given you a starting point for your future investigation of Ant and have inspired you to incorporate this tool into your development process.
Learn more about this topic
- Download the source code for this article from
- You can also download the source code from
- The JDK and XML parser can be downloaded from
- The current binary distribution of Ant can be downloaded from
- The Ant user guide is included with the Ant source distribution, or can be found at
- "Benefit from Platform-Independent Builds," Sanjay Mahapatra (JavaWorld, August 2000) for more on build processes and Ant
- Here are more JavaWorld articles by Michael Cymerman:
- "Secure a Web Application, Java-Style," (April 2000)
- "Building a Java Servlet Framework Using Reflection, Part 1," (November 1999)
- "Building a Java Servlet Framework Using Reflection, Part 2," (February 2000)
- "Smarter Java Development," (August 1999)