Automate your build process using Java and Ant

Introducing the powerful XML-based scripting tool, Ant

Page 2 of 2

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 Listener:

    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);

The 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".

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:

      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.

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:

<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"/>

The 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.

The remoteAnt task has been defined by using a similar taskdef object:

      <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:

  1. Use the Ant buildfile build.xml jar to build the article code on the local machine
  2. Copy the jar file to the remote machine
  3. Execute the remote.bat (or rework for a Linux/Unix machine on the remote machine)
  4. Edit the build.xml file to use machine names and ports appropriate to your environment
  5. 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.

Conclusion

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.

Michael Cymerman is director of research and development at GroupServe, a Washington, DC-based telecommunications firm that creates Internet-based applications to facilitate group communication.

Learn more about this topic

| 1 2 Page 2