Invoking Apache Ant programmatically

Making use of Ant buildfiles from within your Java code

This article demonstrates how to invoke Apache Ant buildfiles via Java. You will examine the necessary Java code and see how to run this code within WebSphere Application Developer.

Nell Gawor (ngawor@us.ibm.com), Advisory Software Engineer, IBM

Nell GaworNell Gawor is an advisory software engineer for IBM in Research Triangle Park, North Carolina, in the Software Group Advanced Design and Technology Group. She received a Masters Degree in Computer Science from the University of Illinois at Urbana-Champaign. Contact Nell at ngawor@us.ibm.com.



09 February 2005

Introduction

Ant is an open-source Java-based build utility from the Apache Software Foundation. It is often compared to Make, which has long been used to help automate the build process. As Ant has progressed through various versions, it has developed a rich library of function that makes it the appropriate tool in many cases. As an example, a few of the many tasks provided by the current version of Ant, V1.6.2, include the ability to manipulate file contents, execute command-line and Java programs, and initiate SSH and FTP connections.

Because Ant buildfiles, which define all of the build logic, are written in XML, there is no code to recompile if the logic needs to change, and no language-specific syntax to understand. In addition, Ant is highly extensible. It provides the ability to create your own custom tasks using Java™ which can then be used the same way as any other Ant tasks. All of this taken together means Ant can be an excellent choice for carrying out many tasks.

In most cases, once buildfiles have been created, they are executed either by calling the Ant executable from the command line with the appropriate target (for example, ant dist) or using the WebSphere® Studio Application Developer (Application Developer) built-in Run Ant command. There may be times, however, when it is more appropriate to launch Ant from within a Java program. This may be to kick off a sequence of actions based on some set of conditions your Java program detects, or it may be just to take advantage of Ant's rich set of built-in function from within your program. In this case, you would go ahead and create a buildfile just as you would when invoking Ant from the command line, but you would then insert the correct calls to the Ant Java APIs into your program. This article will help you handle this process by examining the necessary Java code and showing you how to run this code within Application Developer.

This article does not cover invoking Ant tasks from Java without using a buildfile. For more information on that process, see Using Ant Tasks Outside of Ant in the Apache Ant user manual.


Invoking Ant Programmatically

The examples in this article make use of WebSphere Studio Application Developer 5.1.2. They may work with other versions, but this has not been tested. You can download a free trial version.

Creating the Ant buildfile from within Application Developer

We will briefly review the creation of buildfiles and other Ant basics in this article, but not cover them in detail. Please see the Related content and Resources sections for more information.

First, create a new Java project. Select File => New => Project, then select Java => Java Project and click on Next. Enter antSample for your project name and then click Finish. If asked about switching to the Java perspective, click Yes.

Figure 1: Creating a new Java project
Figure 1: Creating a new Java project in Application Developer

Now create a simple Ant buildfile so that you have something to test invoking. An Ant buildfile is just an xml file, so right-click on the antSample project, and choose New => Other... Then choose XML => XML and click on Next. Choose Create an XML file from scratch and then click on Next. Name your file build.xml and click Finish.

Figure 2: The package explorer after the buildfile has been created
Figure 2: The package explorer after the buildfile has been created

Copy the following into your xml file to create a simple buildfile.

<project name="testproject" default="test" basedir=".">
	<target name="test">
		<echo message="Hello World" />
	</target>
</project>

Save your file. To verify that it is a valid buildfile, run it using Application Developer's built-in Ant. Right-click on your build.xml file in the Package Explorer and choose Run Ant... The target "test" should already be checked to run since it is the default target so click Run. You should see output similar to this in your console:

Buildfile: C:\tempWorkspace\antSample\build.xml

test:
        [echo] Hello World
BUILD SUCCESSFUL
Total time: 4 seconds

Creating a class to invoke Ant

Now that you've verified that the buildfile is correct, you will need to create a Java class that you can use to invoke it via the Ant Java APIs. Right-click on the antSample project and choose New => Class. Enter AntSample as the name of the class and check the box next to public static void main(String[] args) under the question Which method stubs would you like to create? Click Finish and the AntSample class will be created for you along with its main method.

Because you will be using Ant APIs the next thing you need to do is add the jar files Ant needs to your classpath. Right-click on the antSample project and choose Properties from the menu. Click on Java Build Path, choose the Libraries tab, and choose Add External Jars.... Locate the folder where you installed WebSphere Studio and then navigate to eclipse => plugins => org.apache.ant_1.5.3. Choose ant.jar and click Open. Choose Add External Jars again and then navigate to eclipse => plugins => org.apache.xerces_4.0.13.

Choose xerces.jar and xmlParserAPIs.jar and then click Open again. Your build path should now look similar to:

Figure 3: The new Java build path
Figure 3: The new Java build path

Click OK on the Properties dialog to return to your AntSample class. Add the following code to the main method of AntSample:

File buildFile = new File("build.xml");
Project p = new Project();
p.setUserProperty("ant.file", buildFile.getAbsolutePath());
p.init();
ProjectHelper helper = ProjectHelper.getProjectHelper();
p.addReference("ant.projectHelper", helper);
helper.parse(p, buildFile);
p.executeTarget(p.getDefaultTarget());

Right-click anywhere within the file and choose Source => Organize Imports. This should clear any errors. Save the file.

Run the code by highlighting AntSample.java in the package explorer and then going to Run => Run As => Java Application. If you switch to the console, you'll see that the program runs and exits without any errors, but it doesn't produce any output.

In order to figure out why, let's examine the code to see what it is doing. First, it creates a file object from the build file and then creates a new Project object. Project is the Java class that Ant uses to represent an Ant project with all of its targets, tasks, and properties. It sets a property so that the project knows where the buildfile is located. It initializes that project, which causes the project to do some internal setup. Then it locates the default ProjectHelper, and uses that to parse the buildfile and populate the project with information from the buildfile. Next it executes the default target of the project.

You could also execute any target in the buildfile (in this buildfile there happens to be only one), by passing in the name of the target, for example, p.executeTarget("test"). Even though all of this happened successfully, you didn't see any output because Ant didn't know where the output should go. When Ant is executed from the command line or through Application Developer, the output automatically goes to the console. But here you need to be explicit. You need to add a BuildLogger and add it as a listener so it will receive notification of events that happen during the build.

Loggers

There are several different BuildLoggers you can use including DefaultLogger, AnsiColorLogger, MailLogger, and NoBannerLogger. Or you could write your own and register it with the project. In fact, you can register any class that implements the BuildListener interface to receive build events and react to them within your class (for example, receive a taskCompleted event and increment a progress bar). For this case, register the simplest logger, the DefaultLogger, and direct output messages to standard out and error messages to standard error. To do this, add the following lines of code after creating the project but before initializing the project:

DefaultLogger consoleLogger = new DefaultLogger();
consoleLogger.setErrorPrintStream(System.err);
consoleLogger.setOutputPrintStream(System.out);
consoleLogger.setMessageOutputLevel(Project.MSG_INFO);
p.addBuildListener(consoleLogger);

The message output level is configurable to several different levels. Right-click anywhere in the class file and choose Source => Organize Imports and then save. Now run the program again by going to the Run menu and choosing Run Last Launched. You'll see that you are now getting some output, but it is not exactly the same as the output you were getting before. Now you should see in the console:

test:
     [echo] Hello World

Build Events

So now you are getting the output, but you aren't getting any information about whether or not the build was successful or not, or the running time of the build. It turns out you also need to fire some events to let Ant know that the build is starting and that it has finished. You also need to include any exception that was thrown by the execute() method in the fireBuildFinished method so that it gets passed to the listener. After adding this code, the main method looks like this:

File buildFile = new File("build.xml");
Project p = new Project();
p.setUserProperty("ant.file", buildFile.getAbsolutePath());		
DefaultLogger consoleLogger = new DefaultLogger();
consoleLogger.setErrorPrintStream(System.err);
consoleLogger.setOutputPrintStream(System.out);
consoleLogger.setMessageOutputLevel(Project.MSG_INFO);
p.addBuildListener(consoleLogger);

try {
	p.fireBuildStarted();
	p.init();
	ProjectHelper helper = ProjectHelper.getProjectHelper();
	p.addReference("ant.projectHelper", helper);
	helper.parse(p, buildFile);
	p.executeTarget(p.getDefaultTarget());
	p.fireBuildFinished(null);
} catch (BuildException e) {
	p.fireBuildFinished(e);
}

Right-click anywhere in the class file and choose Source => Organize Imports and then save. Now when you run the code you should see the output you expect. To change the amount of information in the output, change the message output level (try Project.MSG_VERBOSE instead).

A note regarding older versions of Ant

The code in this article works for Apache Ant 1.5 or later. If you need to work with older versions of Ant, you need to make some minor changes. First instead of this code:

ProjectHelper helper = 
  ProjectHelper.getProjectHelper();
helper.parse(p, buildFile);

Use this deprecated code to accomplish the same thing:

Project.configureProject(p, buildFile);

Second, in Ant 1.4, the methods to fire events were protected. If you only need to work with 1.4 or earlier, you can just leave these out. But if you need to work with versions from 1.4 through 1.6 or later, extend the Project class to make these methods public and then use your own Project class instead.

Properties

Just like in any other Ant buildfile you can define properties or import properties files into the buildfile. But there may be cases where you want to supply the properties programmatically, or override the values in the buildfile. Edit the build.xml file and replace Hello World with Hello ${name}. This is the syntax that tells Ant to perform variable substitution at runtime. Save the file.

If you run your program again (Run => Run Last Launched), you'll see that it outputs "Hello ${name}" because the value of the variable "name" hasn't been set yet. In AntSample.java, immediately below where you created the Project object, add the line:

p.setProperty("name", "Bob");

Save the file. Now run AntSample again (Run => Run Last Launched). You should now see output similar to:

test:
     [echo] Hello Bob

BUILD SUCCESSFUL
Total time: 1 second

You can even add the line <property name="name" value="Sally" /> to your build.xml file before the echo task, but it won't make any difference. The properties set programmatically will always override those set in the buildfile.


Conclusion

Now that you have the basics, the best way to learn more is to get your hands on the Ant javadoc in order to expand what you've done. Unfortunately, the Ant javadoc is not available as part of Application Developer, and is not available for browsing online at the Apache Ant Web site. This is to make sure that you are looking at javadocs that match your version of Ant. In order to obtain the javadocs, you will need to download Apache Ant V1.5.3 (the version that comes with Application Developer V5.1.2) from: http://archive.apache.org/dist/ant/binaries/. The javadocs are then in ANT_HOME\docs\manual\api. If you wish to use a newer version of Ant than the one that comes with Application Developer 5.1.2, search in the Application Developer help for "Upgrading Ant" or "Using a Different Version of Ant."

Resources

Comments

developerWorks: Sign in

Required fields are indicated with an asterisk (*).


Need an IBM ID?
Forgot your IBM ID?


Forgot your password?
Change your password

By clicking Submit, you agree to the developerWorks terms of use.

 


The first time you sign into developerWorks, a profile is created for you. Information in your profile (your name, country/region, and company name) is displayed to the public and will accompany any content you post, unless you opt to hide your company name. You may update your IBM account at any time.

All information submitted is secure.

Choose your display name



The first time you sign in to developerWorks, a profile is created for you, so you need to choose a display name. Your display name accompanies the content you post on developerWorks.

Please choose a display name between 3-31 characters. Your display name must be unique in the developerWorks community and should not be your email address for privacy reasons.

Required fields are indicated with an asterisk (*).

(Must be between 3 – 31 characters.)

By clicking Submit, you agree to the developerWorks terms of use.

 


All information submitted is secure.

Dig deeper into WebSphere on developerWorks


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=1
Zone=WebSphere, Java technology
ArticleID=35115
ArticleTitle=Invoking Apache Ant programmatically
publish-date=02092005