Ant Sequential Tasks
This article will show you how to implement an Ant task which consume a sequence of sub-task and call them with a parameter of the result from the execution.
As an example we can consider a task taking a path to a directory as a parameter, fetching files in the directory and running a sequence of sub-task for each file.
The task will be implemented as a Java library.
Considering the example, we will have in our Ant script something like:
<fetch-files path="/Windows" suffix=".exe"> <sequential> <echo>My file print: @{file}</echo> <antcall target="process-file"> <param name="filename" value="@{file}" /> </antcall> </sequential> </fetch-files>
This should process all the .exe
files in the /Windows
directory and print the name of the file before processing.
Implementation in Java
Let's create a new Maven project, all we need is a dependency to the Ant API:
<dependency> <groupId>org.apache.ant</groupId> <artifactId>ant</artifactId> <version>1.9.7</version> </dependency>
Sequential Task
First of all we will create an abstract class which allows us to put the <sequential>
part into the task:
abstract class SequentialTask extends Task { private MacroDef macroDef; private Target owningTarget; abstract String getAttributeName(); @Override public void setOwningTarget(Target owningTarget) { this.owningTarget = owningTarget; } public Object createSequential() { macroDef = new MacroDef(); macroDef.setProject(getProject()); MacroDef.Attribute attribute = new MacroDef.Attribute(); attribute.setName(getAttributeName()); macroDef.addConfiguredAttribute(attribute); return macroDef.createSequential(); } void executeSequential(String attrValue) { MacroInstance instance = new MacroInstance(); instance.setProject(getProject()); instance.setOwningTarget(owningTarget); instance.setMacroDef(macroDef); instance.setDynamicAttribute(getAttributeName(), attrValue); instance.execute(); } }
All the task exending the class must define the declared getAttributeName()
method which returns the name of the attribute ("file"
in our example).
Then we cann call the method executeSequential(String attrValue)
for each item in the task result.
Fetching Files Task
Our concrete task implementation could look like:
public class FetchFilesTask extends SequentialTask { private static final String ATTR_NAME = "file"; private String path; private String suffix; public void setPath(String path) { this.path = path; } public void setSuffix(String suffix) { this.suffix = suffix; } @Override String getAttributeName() { return ATTR_NAME; } @Override public void execute() { if (path == null || path.trim().isEmpty()) { throw new BuildException("Parameter 'path' must be specified."); } List<String> filesList = getFilesList(Paths.get(path), suffix); for (String file : filesList) { executeSequential(file.toString()); } } List<String> getFilesList(Path sourcePath, String suffix) { final List<String> toReturn = new ArrayList<>(); try (DirectoryStream<Path> stream = Files.newDirectoryStream(sourcePath)) { for (Path file: stream) { if (Files.isRegularFile(file)) { if (suffix == null || file.toString().endsWith(suffix)) { toReturn.add(file.toString()); } } } } catch (IOException | DirectoryIteratorException e) { throw new BuildException("Error by reading '" + sourcePath + "'.", e); } return toReturn; } }
The class extends the SequentialTask
, defines its abstract method and use the executeSequential
method.
Additionaly defines the class two task parameter path
and suffix
by the defining the getters for them.
Using the task in an Ant script
To call the task in an Ant script as showned in the example we have to provide the JAR library and define the task by its class:
<taskdef name="fetch-files" classname="cz.net21.ttulka.ant.FetchFilesTask" classpath="AntTasks-1.0.jar" />
Have fun!