Automate Your Build Process Using Java and Ant
Automate Your Build Process Using Java and Ant
Ant
Introducing the powerful XML-based scripting tool, Ant
By Michael Cymerman, JavaWorld.com, 10/20/00
Here are some useful commands that are built in the Ant distribution.
Command Description
Ant Used to execute another ant
process from within the current
one.
Copydir Used to copy an entire directory.
Copyfile Used to copy a single file.
Cvs Handles packages/modules
retrieved from a CVS repository.
Delete Deletes either a single file or all
files in a specified directory and its
sub-directories.
Deltree Deletes a directory with all its files
and subdirectories.
Exec Executes a system command.
When the os attribute is specified,
then the command is only
executed when Ant is run on one of
the specified operating systems.
Get Gets a file from an URL.
Jar Jars a set of files.
Java Executes a Java class within the
running (Ant) VM or forks another
VM if specified.
Javac Compiles a source tree within the
running (Ant) VM.
Javadoc/Javadoc2 Generates code documentation
using the javadoc tool.
Mkdir Makes a directory.
Property Sets a property (by name and
value), or set of properties (from
file or resource) in the project.
Rmic Runs the rmic compiler for a
certain class.
Tstamp Sets the DSTAMP, TSTAMP, and
TODAY properties in the current
project.
Style Processes a set of documents via
XSLT.
While other tools are available for doing software builds, Ant is easy to
use and can be mastered within minutes. In addition, Ant lets you
create expanded functionality by extending some of its classes. I will
show this expansion in a following example.
In many cases, the XML parser is part of the lib files distributed with
the Servlet runner or the Web Server. If not, the free XML parser from
java.sun.com is sufficient. Continued
Example scenario
This example scenario should help show you the value of Ant and
provide insight into its benefits and how you can use it.
A common scheme for doing this build would involve the development
of small scripts in platform-specific languages based on the server's
operating system. For example, a developer working on an NT machine
could create a batch file that performs the compilation tasks and then
runs the deployment. However, if the production environment had Unix
or Linux, the developer would have to rewrite the script, ensuring that
the scripts were in sync.
The first line contains information about the overall project that is to
be built.
The most important elements of the project line are the default and
the basedir.
The default attribute references the default target that is to be
executed. Because Ant is a command-line build tool, it is possible to
execute only a subset of the target steps in the Ant file. For example, I
could perform the following command:
<target name="init">
<target name="clean" depends="init">
The target element contains four attributes: name, if, unless, and
depends. Ant requires the name attribute, but the other three
attributes are optional.
Using depends, you can stack the Ant tasks so that a dependent task
is not initiated until the task that it depends on is completed. In the
above example, the clean task will not start until the init task has
completed. The depends attribute may also contain a list of comma-
separated values indicating several tasks that the task in discussion
depends on.
The if and unless commands let you specify commands that are to
be performed either if a certain property is set or unless that property
is set. The if will execute when the property value is set, and the
unless will execute if the value is not set. You can use the available
command to set those properties as shown in a following example, or
you can set them via the command line.
The init target from the simple example contains four lines of
property commands as shown here:
These commands are used to ensure that there are no extraneous files
in the outputDir (or classes directory when dereferenced as
mentioned above). The first command removes the entire tree
contained under the outputDir. The second command creates the
directory again.
The last line of major interest to the developer is the following
compilation line:
<javac srcdir="${src.dir}"
destdir="${build.classes}"
classpath="${classpath}"
debug="on"
deprecation="off"
optimize="on" >
<include name="**/*.java"/>
<exclude name="**/Script.java" unless="bsf.present" />
<exclude name="**/version.txt" />
</javac>
You can use the include/exclude entities inside the javac task to
include/exclude files matching the pattern in the name attribute from
the compilation. From the above example, you want to include files
contained in any directory ending in .java but, at the same time, you
want to exclude files named Script.java unless a property bsf.present
is set to true.
You set the bsf.present property using the following task that
searches the classpath for the classname specified and sets
bsf.present according to the search results:
<available property="bsf.present"
classname="com.ibm.bsf.BSFManager" />
The javac command will not include files called version.txt from the
compilation based upon the exclude command above.
Generating javadoc
Another task that Ant can help automate is the generation of javadoc.
You can use the following command to generate the javadoc:
<javadoc packagenames="${packages}"
sourcepath="${basedir}/${src.dir}"
destdir="${build.javadocs}"
author="true"
version="true"
windowtitle="${Name} API"
doctitle="${Name}"
bottom="Copyright © 2000 GroupServe. All Rights
Reserved."
/>
The packages specify the overall packages that the javadoc will
include. The sourcepath attribute points towards the location of the
source files. The javadoc command also provides attributes allowing
you to specify the title of the window and the document. You can also
include a copyright notice at the bottom of each javadoc page, using
the bottom attribute. Continued
The simple answer to the question "Can Ant do XYZ?" is "Yes, but you
may have to customize it."
Extending Ant
Two Ant extensions are interesting to discuss at this point. They are
increased reporting and the ability to distribute code remotely using
Ant.
Reporting enhancements
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.
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:
This listener is included with the Ant distribution and outputs an XML
representation of the build process to a file called "log.xml".
Multiple machines
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.
The 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.
The RemoteTask object contains the following method declaration that
is necessary for any object that is to extend the Task object:
Continued
The Ant processor calls this method after all of the attributes have
been set. Any custom Task object must override this method.
The 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.
The 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.
The RemoteAntHandler object receives the Command object from the
ObjectInputStream. 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
Command object.
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.
When the 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:
<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"/>
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.
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.
Conclusion
So, you've read through all of this. What have you learned?
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.