Skip to main content

skip to main content

developerWorks  >  Java technology  >

Extending Ant to support interactive builds

Add interactivity to help novices cope and give power users more flexibility

developerWorks
Document options

Document options requiring JavaScript are not displayed

Sample code


Rate this page

Help us improve this content


Level: Introductory

Anthony Young-Garner (ajyoung@us.ibm.com), Software Engineer, IBM

01 Nov 2001

Ant, from Jakarta project at the Apache Foundation, has become a de facto standard for building Java projects. You may already be using it to create automatic builds. It's easy enough to tailor those builds to your needs by customizing the build files; but what if you want to wait until run time to set certain properties? In this article, IBM software engineer Anthony Young-Garner shows you how to extend Ant to produce builds that are interactive at run time; users can follow prompts to change parameters each time they conduct a build without having to deal with unfamiliar build files. You can incorporate this functionality into your builds to provide a smoother and more flexible experience for your end users.

Apache Ant is widely used by the Java community to create automated builds. While it's easy to create customizable build files with Ant, there are times when it might be preferable to prompt the user for information during the build. An interactive build process allows novice users to conduct builds without worry and provides a more convenient way for experienced users to make one-time customizations to build properties. This article guides you through the process of extending Ant to support interactive builds. Before reading this article, you should have basic knowledge of Ant, including an understanding of projects, tasks, and properties; for an introduction to Ant, see Malcolm Davis's article "Incremental development with Ant and JUnit" (additional articles are listed in Resources).

One of the many common uses of property tasks in Ant build files is to specify filesystem targets for build artifacts (see Listing 1). In such a case, the user of the build file may be more familiar with these specific build artifacts than Ant itself. For our example, we'll prompt the user for the proper filesystem locations instead of instructing the user to modify an unfamiliar file. (Download the source files used in this article from Resources.)

Ant properties

Customizable build files are created by using Ant property tasks. As shown in the snippet below, properties allow variable substitution in build files. Editing a property in the build file causes the specified value to be substituted for each reference to that property in the remainder of the build file at run time. Interactive building requires a mechanism that allows run-time assignment of property values.


Listing 1. Ant build file demonstrating property task
1 <?xml version="1.0"?>
2 <project name="PropertyExample" default="main" basedir=".">
3	<target name="main">
4	<property name="documentRoot" value="/usr/apache/htdocs/sample"/>
5	<echo>HTML/JSP pages installing to: ${documentRoot}</echo>
6	<property name="servletDirectory" value="/opt/servlets/sample"/>
7	<echo>Servlet code installing to: ${servletDirectory}</echo>
8	</target>
9 </project>



Back to top


Extending Ant

Ant features are implemented as Java classes called tasks. Tasks (subclassed from org.apache.tools.ant.Task) consist mainly of several attributes accessed through getters and setters and an execute method, which actually conducts a task's work. Listing 2 demonstrates use of the PropertyPrompt task, the Ant extension we are building to support interactivity. Because this is an optional task, it must be explicitly defined with the TaskDef call (Line 4). Once defined, the PropertyPrompt task may be used to allow the user to override property values declared in the build file (Lines 9-10). Line 3 is an optional build file property that is used to specify how long (in seconds) the PropertyPrompt task should wait for user input before resuming the build without modifying the Property value(s).


Listing 2. Interactive Ant build file
1 <?xml version="1.0"?>
2 <project name="PropertyPromptExample" default="main" basedir=".">
3   <property name="promptTimeout" value="10"/>
4   <taskdef name="propertyprompt" 
       classname="com.ibm.samples.apache.tools.ant.taskdefs.optional.PropertyPrompt"/>
5   <target name="main">
6   <property name="documentRoot" value="/usr/apache/htdocs/sample"/>
7   <echo>HTML/JSP pages installing to: ${documentRoot}</echo>
8   <echo>Servlet code installing to: ${servletDirectory}</echo>
9   <propertyprompt propertyname="documentRoot" 
       promptcharacter=":">Enter value for web application document

root</propertyprompt>
10  <propertyprompt propertyname="servletDirectory" 
       defaultvalue="/usr/servlets">Where should servlet classes
 
install</propertyprompt>
11  <echo>HTML/JSP pages installed to ${documentRoot}</echo>
12  <echo>Servlet pages installed to ${servletDirectory}</echo>
13  </target>
14</project>


This example provides a general overview of how the task will be used, but let's get into the details. Our task will have three attributes:

  • propertyname (required). The name of the target property

  • defaultvalue (optional). The value to which the target property should be set if the user provides no input; default behavior leaves the property unchanged

  • promptcharacter (optional). Character(s) used to punctuate the prompt text specified in the body of the task call; default is ?

Remember that task attributes in the build file correspond to accessor methods on the implementation class. With that in mind, we can create the basic data representation of the PropertyPrompt class, as shown in Listing 3.


Listing 3. PropertyPrompt class attribute (accessor methods not shown)
public class PropertyPrompt extends org.apache.tools.ant.Task {

	private String propertyName;
	private String defaultValue;
	private String promptcharacter;

}

The Ant task life cycle consists of two phases: parser time and run time. During parsing of the build file, the no-arg constructor and init() method of each task are called. The default constructor of the org.apache.tools.ant.Task base class gives the task a reference to its project, location, target, and ID. The init() method is expected to contain any general initialization code necessary for the task to run.

As shown in Listing 4, the init() method for PropertyPrompt handles setting the timeout value for the task and sets default values for the optional attributes, defaultValue and promptCharacter. The timeout value is set based on whether a promptTimeout property has been set in the build file. The value is set in this way to allow a single promptTimeout to be set for all of the PropertyPrompt tasks in a build file. If the timeout is set in only one place, it is easy to modify.


Listing 4. PropertyPrompt init() method
public class PropertyPrompt extends org.apache.tools.ant.Task {

	...
	private int timeout;

	public void init() {

		super.init();
		String timeoutProperty = project.getProperty("promptTimeout");

		if (timeoutProperty == null) {
			timeout = 0;
		} else {
			try {
				timeout = Integer.parseInt(timeoutProperty);
			} catch (NumberFormatException e) {
				log("Invalid promptTimeout value: " + timeoutProperty +
				           ". Using default (wait indefinitely).");
				timeout = 0;
			}
		}
		defaultValue = "";
		promptCharacter = "?";
	}

	...
}

Once the entire build file has been parsed, targeted tasks are run. At run time, a task's execute() method is called by Ant. This method performs the actual work of the task. If the timeout is greater than zero, the execute() method for the PropertyPrompt task prompts the user to enter a proposed value for the specified property, as shown in Listing 5. If the user does not enter a value before the timeout expires (or if the user presses Enter without typing anything), then the default value is used if one is specified; the property remains unchanged if there is no default value. If the timeout is zero, then the task will wait indefinitely for the user to respond. If the timeout is negative, then prompting will be canceled altogether.


Listing 5. PropertyPrompt execute() method
public void execute() throws org.apache.tools.ant.BuildException {
	if (timeout > -1) {

		log("Prompting user for " + propertyname + ". " + getDefaultMessage());
		StringBuffer prompt = new StringBuffer();
		prompt.append("\n");
		prompt.append(prompttext);
		prompt.append(" [");
		prompt.append(defaultvalue);
		prompt.append("] ");
		prompt.append(promptcharacter);
		prompt.append(" ");
		System.out.print(prompt.toString());

		/** future version should have hooks for validation of user input.*/
		TimedBufferedReader reader = 
		    new TimedBufferedReader(new InputStreamReader (System.in));
		reader.setTimeout(timeout);
		reader.setDefaultString(defaultvalue);
		try {
			proposedValue  = reader.readLine();
		} catch (IOException e) {
			log("Prompt failed. Using default.");
			proposedValue = defaultvalue;
		}
		if (!proposedValue.equals("")) {
			project.setProperty(propertyname, proposedValue);
		}
	}

The code for the PropertyPrompt task is straightforward, but there are a few details worth mentioning. The TimedBufferedReader class is an inner class on PropertyPrompt that inherits from java.io.BufferedReader. It overrides the BufferedReader readLine method to allow time-limited prompting for user input. Using inner classes, rather than independent helper classes, to serve a task's delegation needs simplifies packaging and classpath concerns.



Back to top


Running the PropertyPrompt task

To run a new Ant task, you must place it on the classpath and correctly reference it in the build file. Assuming the build file shown in Listing 1 is in the current directory (C:\temp\jakarta-ant-1.3\testbuilds) and is called build.xml, it would be executed as shown in Listing 6.


Listing 6. PropertyPrompt task execution
C:\temp\jakarta-ant-1.3\testbuilds>ant
Buildfile: build.xml

main:
     [echo] HTML/JSP pages installing to /usr/apache/htdocs/sample
     [echo] Servlet code installing to ${servletDirectory}
[propertyprompt] Prompting user for documentRoot. No default response specified.

Enter value for web application document root [] : C:\temp\html
[propertyprompt] Prompting user for servletDirectory. Default response is /usr/servlets.

Where should servlet classes install [/usr/servlets] ?
     [echo] HTML/JSP pages installed to C:\temp\html
     [echo] Servlet pages installed to /usr/servlets

BUILD SUCCESSFUL

Total time: 7 seconds



Back to top


Conclusion

Ant has become the de facto standard for building Java projects. And while it makes the build creator's job easier, it does little to assist the person executing the build. The PropertyPrompt task allows a build creator to easily accommodate novice users. The task developed here might also be used to allow the build file user to specify any other values that require customization on a build environment basis but which don't require any knowledge of Ant (or the specific operations of the build file) to set correctly.

In this article, you've been introduced to a simple Ant task to allow interactive builds. You've also learned by example how to code Ant tasks. Using Ant's simple task API to extend its functionality is a quick way to create exactly the build process you need.




Back to top


Downloads

DescriptionNameSizeDownload method
example build.xml for PropertyPrompt taskbuild.zip1 KBHTTP
full source listing for PropertyPrompt.javaPropertyPrompt.zip3 KBHTTP
Information about download methods


Resources



About the author

Anthony Young-Garner is a software engineer at IBM Austin, assisting developers in coding and deploying applications on WebSphere Application Server. Contact him at ajyoung@us.ibm.com.




Rate this page


Please take a moment to complete this form to help us better serve you.



YesNoDon't know
 


 


12345
Not
useful
Extremely
useful
 


Back to top