Writing Your Own Task

# Developing with 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 desgined 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 the start end end tags), write a `public void addText(String)` method. Note that Ant does not expand properties on the text it passes to the task.
5. For each nested element, write a create, add or addConfigured method. A create method must be a `public` method that takes no arguments and returns an `Object` type. The name of the create method must begin with `create`, followed by the element name. An add (or addConfigured) method must be a `public void` method that takes a single argument of an `Object` type with a no-argument constructor. The name of the add (addConfigured) method must 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, that throws a `BuildException`. This method implements the task itself.

### The Life-cycle of a Task

1. The task gets instantiated using a no-argument constructor, at parser time. This means even tasks that are never executed get instantiated.
2. The task gets references to its project and location inside the buildfile via its inherited `project` and `location` variables.
3. If the user specified an `id` attribute to this task, the project registers a reference to this newly created task, at parser time.
4. The task gets a reference to the target it belongs to via its inherited `target` variable.
5. `init()` is called at parser time.
6. 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 parser time.
7. All attributes of this task get set via their corresponding `setXXX` methods, at runtime.
8. The content character data sections inside the XML element corresponding to this task is added to the task via its `addText` method, at runtime.
9. All attributes of all child elements get set via their corresponding `setXXX` methods, at runtime.
10. `execute()` is called at runtime. While the above initialization steps only occur once, the execute() method may be called more than once, if the task is invoked more than once. For example, 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 an attribute to the corresponding setter method.

The most common way to write an attribute setter is to use a `java.lang.String` argument. In this case Ant will pass the literal value (after property expansion) to your task. But there is 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.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.

What happens if more than one setter method is present for a given attribute? A method taking a `String` argument will always lose against the more specific methods. If there are still more setters Ant could chose from, only one of them will be called, but we don't know which, this depends on the implementation of your Java virtual machine.

### Supporting nested elements

Let's assume your task shall support nested elements with the name `inner`. First of all, you need a class that represents this nested element. Often you simply want to use one of Ant's classes like `org.apache.tools.ant.types.FileSet` to support nested `fileset` elements.

Attributes of the nested elements or nested child elements of them will be handled using the same mechanism used for tasks (i.e. setter methods for attributes, addText for nested text and create/add/addConfigured methods for child elements).

Now you have a class `NestedElement` that is supposed to be used for your nested `<inner>` elements, you have three 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 of `NestedElement`, there are no restrictions on the type. For the options 2 and 3, Ant has to create an instance of `NestedInner` before it can pass it to the task, this means, `NestedInner` must have a `public` no-arg constructor. This is the only difference between options 1 and 2.

The difference between 2 and 3 is what Ant has done to the object before it passes it to the method. `addInner` will receive an object directly after the constructor has been called, while `addConfiguredInner` gets the object after the attributes and nested children for this new object have been handled.

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

### TaskContainer

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

When we said `execute` would be called, we lied ;-). In fact, Ant will call the `perform` method in `org.apache.tools.ant.Task`, which in turn calls `execute`. This method makes sure that Build Events will be triggered. If you execute the task instances nested into your task, you should also invoke `perform` on these instances instead of `execute`.

### Example

Let's write our own task, which prints a message on the `System.out` stream. The task 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 target after the compilation. Use the `classpath` attribute of `<taskdef>` to point to where the code has just been compiled.

>     <?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 and implementing class name to the `default.properties` file in the `org.apache.tools.ant.taskdefs` package. 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 the `addBuildListener` method to add your listener to the project. Your listener must implement the `org.apache.tools.antBuildListener` interface. The listener will receive BuildEvents for the following events

- Build started
- Build finished
- Target started
- Target finished
- Task started
- Task finished
- Message logged

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. This listener 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 will either 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 has been configured.

## 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, which benefits all users and spreads the maintenance load around.

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

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

Copyright © 2000-2003 Apache Software Foundation. All rights Reserved.