Cari di Apache Ant 
    Apache Ant User Manual
Daftar Isi
(Sebelumnya) Selectors-programTasks Designed for Extension (Berikutnya)
Developing with Apache Ant

Writing Your Own Task

Developing with Apache Ant

Writing Your Own Task

It is very easy to write your own task:

  1. Create a Java class that extends org.apache.tools.ant.Task or another class that was designed to be extended.
  2. For each attribute, write a setter method. The setter method must be a public void method that takes a single argument. The name of the method must begin with set, followed by the attribute name, with the first character of the name in uppercase, and the rest in lowercase*. That is, to support an attribute named file you create a method setFile. Depending on the type of the argument, Ant will perform some conversions for you, see below.
  3. If your task shall contain other tasks as nested elements (like parallel), your class must implement the interface org.apache.tools.ant.TaskContainer. If you do so, your task can not support any other nested elements. See below.
  4. If the task should support character data (text nested between thestart end end tags), write a public void addText(String)method. Note that Ant does not expand properties onthe text it passes to the task.
  5. For each nested element, write a create, add oraddConfigured method. A create method must be apublic method that takes no arguments and returns anObject type. The name of the create method must beginwith create, followed by the element name. An add (oraddConfigured) method must be a public void method thattakes a single argument of an Object type with ano-argument constructor. The name of the add (addConfigured) methodmust begin with add (addConfigured),followed by the element name. For a more complete discussion see below.
  6. Write a public void execute method, with no arguments, thatthrows a BuildException. This method implements the taskitself.

* Actually the case of the letters afterthe first one doesn't really matter to Ant, using all lower case is agood convention, though.

The Life-cycle of a Task

  1. The xml element that contains the tag corresponding to the task gets converted to an UnknownElement at parser time. This UnknownElement gets placed in a list within a target object, or recursivly within another UnknownElement.
  2. When the target is executed, each UnknownElement is invoked using an perform() method. This instantiates the task. This means that tasks only gets instantiated at run time.
  3. The task gets references to its project and location inside the buildfile via its inherited project and location variables.
  4. If the user specified an id attribute to this task, the project registers a reference to this newly created task, at run time.
  5. The task gets a reference to the target it belongs to via its inherited target variable.
  6. init() is called at run time.
  7. All child elements of the XML element corresponding to this task are created via this task's createXXX() methods or instantiated and added to this task via its addXXX() methods, at run time. Child elements corresponding to addConfiguredXXX() are created at this point but the actual addCondifgired method is not called.
  8. All attributes of this task get set via their corresponding setXXX methods, at runtime.
  9. The content character data sections inside the XML element corresponding to this task is added to the task via its addText method, at runtime.
  10. All attributes of all child elements get set via their corresponding setXXX methods, at runtime.
  11. If child elements of the XML element corresponding to this task have been created for addConfiguredXXX() methods, those methods get invoked now.
  12. execute() is called at runtime. If target1 and target2 both depend on target3, then running 'ant target1 target2' will run all tasks in target3 twice.

Conversions Ant will perform for attributes

Ant will always expand properties before it passes the value of anattribute to the corresponding setter method. Since Ant 1.8, it ispossible to extend Ant's property handlingsuch that a non-string Object may be the result of the evaluation of a stringcontaining a single property reference. These will be assigned directly viasetter methods of matching type. Since it requires some beyond-the-basicsintervention to enable this behavior, it may be a good idea to flag attributesintended to permit this usage paradigm.

The most common way to write an attribute setter is to use ajava.lang.String argument. In this case Ant will passthe literal value (after property expansion) to your task. But thereis more! If the argument of you setter method is

  • boolean, your method will be passed the value true if the value specified in the build file is one of true, yes, or on and false otherwise.
  • char or java.lang.Character, your method will be passed the first character of the value specified in the build file.
  • any other primitive type (int, short and so on), Ant will convert the value of the attribute into this type, thus making sure that you'll never receive input that is not a number for that attribute.
  • java.io.File, Ant will first determine whether the value given in the build file represents an absolute path name. If not, Ant will interpret the value as a path name relative to the project's basedir.
  • org.apache.tools.ant.types.Resource org.apache.tools.ant.types.Resource, Ant will resolve the string as a java.io.File as above, then pass in as a org.apache.tools.ant.types.resources.FileResource. Since Ant 1.8
  • org.apache.tools.ant.types.Path, Ant will tokenize the value specified in the build file, accepting : and ; as path separators. Relative path names will be interpreted as relative to the project's basedir.
  • java.lang.Class, Ant will interpret the value given in the build file as a Java class name and load the named class from the system class loader.
  • any other type that has a constructor with a single String argument, Ant will use this constructor to create a new instance from the value given in the build file.
  • A subclass of org.apache.tools.ant.types.EnumeratedAttribute, Ant will invoke this classes setValue method. Use this if your task should support enumerated attributes (attributes with values that must be part of a predefined set of values). See org/apache/tools/ant/taskdefs/FixCRLF.java and the inner AddAsisRemove class used in setCr for an example.
  • A (Java 5) enumeration. Ant will call the setter with the enum constant matching the value given in the build file. This is easier than using EnumeratedAttribute and can result in cleaner code, but of course your task will not run on JDK 1.4 or earlier. Note that any override of toString() in the enumeration is ignored; the build file must use the declared name (see Enum.getName()). You may wish to use lowercase enum constant names, in contrast to usual Java style, to look better in build files. As of Ant 1.7.0.

What happens if more than one setter method is present for a givenattribute? A method taking a String argument will alwayslose against the more specific methods. If there are still moresetters Ant could chose from, only one of them will be called, but wedon't know which, this depends on the implementation of your Javavirtual machine.

Supporting nested elements

Let's assume your task shall support nested elements with the nameinner. First of all, you need a class that representsthis nested element. Often you simply want to use one of Ant'sclasses like org.apache.tools.ant.types.FileSet tosupport nested fileset elements.

Attributes of the nested elements or nested child elements of themwill be handled using the same mechanism used for tasks (i.e. settermethods for attributes, addText for nested text andcreate/add/addConfigured methods for child elements).

Now you have a class NestedElement that is supposed tobe used for your nested <inner> elements, you havethree options:

  1. public NestedElement createInner()
  2. public void addInner(NestedElement anInner)
  3. public void addConfiguredInner(NestedElement anInner)

What is the difference?

Option 1 makes the task create the instance ofNestedElement, there are no restrictions on the type.For the options 2 and 3, Ant has to create an instance ofNestedInner before it can pass it to the task, thismeans, NestedInner must have a public no-arg constructor or a public one-arg constructor taking a Project class as a parameter.This is the only difference between options 1 and 2.

The difference between 2 and 3 is what Ant has done to the objectbefore it passes it to the method. addInner will receivean object directly after the constructor has been called, whileaddConfiguredInner gets the object after theattributes and nested children for this new object have beenhandled.

What happens if you use more than one of the options? Only one ofthe methods will be called, but we don't know which, this depends onthe implementation of your Java virtual machine.

Nested Types

If your task needs to nest an arbitary type that has been defined using <typedef> you have two options.
  1. public void add(Type type)
  2. public void addConfigured(Type type)
The difference between 1 and 2 is the same as between 2 and 3 in the previous section.

For example suppose one wanted to handle objects object of type org.apache.tools.ant.taskdefs.condition.Condition, one may have a class:

public class MyTask extends Task { private List conditions = new ArrayList(); public void add(Condition c) { conditions.add(c); } public void execute() { // iterator over the conditions }}  

One may define and use this class like this:

<taskdef name="mytask" classname="MyTask" classpath="classes"/><typedef name="condition.equals" classname="org.apache.tools.ant.taskdefs.conditions.Equals"/><mytask> <condition.equals arg1="${debug}" arg2="true"/></mytask> 

A more complicated example follows:

public class Sample { public static class MyFileSelector implements FileSelector { public void setAttrA(int a) {} public void setAttrB(int b) {} public void add(Path path) {} public boolean isSelected(File basedir, String filename, File file) { return true; } } interface MyInterface { void setVerbose(boolean val); } public static class BuildPath extends Path { public BuildPath(Project project) { super(project); } public void add(MyInterface inter) {} public void setUrl(String url) {} } public static class XInterface implements MyInterface { public void setVerbose(boolean x) {} public void setCount(int c) {} }} 

This class defines a number of static classes that implement/extend Path, MyFileSelector and MyInterface. These may be defined and used as follows:

 
<typedef name="myfileselector" classname="Sample$MyFileSelector" classpath="classes" loaderref="classes"/><typedef name="buildpath" classname="Sample$BuildPath" classpath="classes" loaderref="classes"/><typedef name="xinterface" classname="Sample$XInterface" classpath="classes" loaderref="classes"/><copy todir="copy-classes"> <fileset dir="classes"> <myfileselector attra="10" attrB="-10"> <buildpath path="." url="abc"> <xinterface count="4"/> </buildpath> </myfileselector> </fileset></copy>

TaskContainer

The TaskContainer consists of a single method,addTask that basically is the same as an add method for nested elements. The taskinstances will be configured (their attributes and nested elementshave been handled) when your task's execute method getsinvoked, but not before that.

When we said execute would becalled, we lied ;-). In fact, Ant will call the performmethod in org.apache.tools.ant.Task, which in turn callsexecute. This method makes sure that Build Events will be triggered. If youexecute the task instances nested into your task, you should alsoinvoke perform on these instances instead ofexecute.

Example

Let's write our own task, which prints a message on theSystem.out stream.Thetask has one attribute, called message.

package com.mydomain;import org.apache.tools.ant.BuildException;import org.apache.tools.ant.Task;public class MyVeryOwnTask extends Task { private String msg; // The method executing the task public void execute() throws BuildException { System.out.println(msg); } // The setter for the "message" attribute public void setMessage(String msg) { this.msg = msg; }}

It's really this simple ;-)

Adding your task to the system is rather simple too:

  1. Make sure the class that implements your task is in the classpath when starting Ant.
  2. Add a <taskdef> element to your project. This actually adds your task to the system.
  3. Use your task in the rest of the buildfile.

Example

<?xml version="1.0"?><project name="OwnTaskExample" default="main" basedir=".">  <taskdef name="mytask" classname="com.mydomain.MyVeryOwnTask"/>  <target name="main"> <mytask message="Hello World! MyVeryOwnTask works!"/>  </target></project>

Example 2

To use a task directly from the buildfile which created it, place the<taskdef> declaration inside a targetafter the compilation. Use the classpath attribute of<taskdef> to point to where the code has just beencompiled.
<?xml version="1.0"?><project name="OwnTaskExample2" default="main" basedir=".">  <target name="build" > <mkdir dir="build"/> <javac srcdir="source" destdir="build"/>  </target>  <target name="declare" depends="build"> <taskdef name="mytask" classname="com.mydomain.MyVeryOwnTask" classpath="build"/>  </target>  <target name="main" depends="declare"> <mytask message="Hello World! MyVeryOwnTask works!"/>  </target></project>

Another way to add a task (more permanently), is to add the task name andimplementing class name to the default.properties file in theorg.apache.tools.ant.taskdefspackage. Then you can use it as if it were a built-in task.


Build Events

Ant is capable of generating build events as it performs the tasks necessary to build a project.Listeners can be attached to Ant to receive these events. This capability could be used, for example,to connect Ant to a GUI or to integrate Ant with an IDE.

To use build events you need to create an ant Project object. You can then call theaddBuildListener method to add your listener to the project. Your listener must implementthe org.apache.tools.antBuildListener interface. The listener will receive BuildEventsfor the following events

  • Build started
  • Build finished
  • Target started
  • Target finished
  • Task started
  • Task finished
  • Message logged

If the build file invokes another build file via <ant> or <subant> or uses <antcall>, you are creating anew Ant "project" that will send target and task level events of itsown but never sends build started/finished events. Ant 1.6.2introduces an extension of the BuildListener interface namedSubBuildListener that will receive two new events for

  • SubBuild started
  • SubBuild finished

If you are interested in those events, all you need to do is toimplement the new interface instead of BuildListener (and register thelistener, of course).

If you wish to attach a listener from the command line you may use the-listener option. For example:

ant -listener org.apache.tools.ant.XmlLogger

will run Ant with a listener that generates an XML representation of the build progress. Thislistener is included with Ant, as is the default listener, which generates the logging to standard output.

Note: A listener must not access System.out and System.err directly since ouput on these streams is redirected by Ant's core to the build event system. Accessing these streams can cause an infinite loop in Ant. Depending on the version of Ant, this willeither cause the build to terminate or the Java VM to run out of Stack space. A logger, also, may not access System.out and System.err directly. It must use the streams with which it hasbeen configured.

Note2: All methods of a BuildListener except for the "Build Started" and "Build Finished" events may occur on several threads simultaneously - for example while Ant is executing a <parallel> task.


Source code integration

The other way to extend Ant through Java is to make changes to existing tasks, which is positively encouraged.Both changes to the existing source and new tasks can be incorporated back into the Ant codebase, whichbenefits all users and spreads the maintenance load around.

Please consult theGetting Involved pages on the Jakarta web sitefor details on how to fetch the latest source and how to submit changes for reincorporation into thesource tree.

Ant also has sometask guidelineswhich provides some advice to people developing and testing tasks. Even if you intend tokeep your tasks to yourself, you should still read this as it should be informative.

(Sebelumnya) Selectors-programTasks Designed for Extension (Berikutnya)