write an advanced apache ant task, that perform operations with file set and relative paths
ant, task, apache, advanced task, filesets
ant write advanced task
Here we show how to write an advanced ant task that search inside a FileSet
, and that can return found files giving their path, relative to a given base path.
- First we write and test a simple task using filesets
- then we improve the task by managing relative paths.
At the end you can download all source code as a zipped project.
Ant Task using filesets
Suppose you want to write a task that search for a filename, over a large set of files.
Ant provides <fileset/>
to represent a collection of files.
So, to follow standards, your task should looks like the following.
<my-advanced-task find="foobar" outproperty="found.list"> <fileset dir="input" includes="**/*" /> </my-advanced-task>
in my idea the <my-advanced-task/>
search for files whose contain “foobar” in the name, and write the list of matching files into the “found.list” ant property.
skeleton
Ant provides a common way of bundling files: the fileset. To build the java class modeling the task we write a skeleton class like following
/** My advanced ant task that manage filesets */ public class MyAdvancedTask extends Task{ private String find; private String outproperty; private Vector filesets = new Vector(); public void setFind(String find){ this.find = find; } public void setOutproperty(String outproperty){ this.outproperty = outproperty; } public void addFileset(FileSet fileset){ filesets.add(fileset); } }
Note, the Vector filesets
, allow to add more than one fileset to your task.
validate() and execute()
Before to execute()
we need a validate()
method that checks preconditions, and if some of them are not respected will throw a BuildException
/** Validate passed parameters */ public void validate(){ if( find==null) throw new BuildException("find string not set"); if( outproperty==null) throw new BuildException("out property not set"); if( filesets.size()<1) throw new BuildException("fileset not set"); }
Now we can write the execute()
method, that make a search on the Vector
of filesets added.
/** executes search operation on filesets.*/ @Override public void execute(){ validate(); List matchingFiles = new ArrayList(); // we iterate over one or more <fileset> elements for(Iterator it = filesets.iterator(); it.hasNext(); ){ FileSet fs = (FileSet) it.next(); // refer the result of fileset via the directory scanner DirectoryScanner ds = fs.getDirectoryScanner(getProject()); String[] includedFiles = ds.getIncludedFiles(); for( int i=0; i < includedFiles.length; i++ ){ // get a platform-independent representation of file String filename = includedFiles[i].replace('\\','/'); filename = filename.substring(filename.lastIndexOf("/")+1); // try to match each filename with the "find" expression if( filename.contains( find ) ){ File base = ds.getBasedir(); File found = new File(base,includedFiles[i]); // add the full path to the list matchingFiles.add( found.getAbsolutePath() ); } } } // write the list of matches on the given property String outString = "[]"; if( matchingFiles.size() > 0 ){ outString = Arrays.toString( matchingFiles.toArray() ); } getProject().setNewProperty(outproperty, outString ); }
Following comments you see main points of execution:
validate()
input data
test
Suppose we have an input directory tree like the following image
To test the above task we write a small buildfile
<project name="MyAdvancedTask" default="test"> <target name="use.init"> <taskdef name="my-advanced-task" classname="tut.ant.task.advanced.MyAdvancedTask" classpath="${dir.lib.generated}/${ant.project.name}.jar"/> </target> <target name="use" depends="build,use.init"> <!-- suppose the "input" folder contains files to search --> <my-advanced-task find="foobar" outproperty="found.list"> <fileset dir="input" includes="**/*" /> </my-advanced-task> <!-- output found matching files --> <echo>${found.list}</echo> </target> </project>
The resulting output follows
<code>use.init: use: [echo] [E:\my\work\space\tut.java-ant\input\Dir-1\Dir-1.1\Dir-1.1.1\foobar.xml, E:\my\work\space\tut.java-ant\input\Dir-1\Dir-1.2\hell.foobar, E:\my\work\space\tut.java-ant\input\Dir-2\foobar.txt]
The ouput looks correct, but verbous. In next section we make a small change to relativise paths in output
Relativize filepaths
Now we add a small improvement to the avdanced task. We want that the output file list is relativized with respect to a given basepath.
Finally, we want to call the task with following signatire
<my-advanced-task find="foobar" basepath="input" outproperty="found.list"> <fileset dir="input" includes="**/*" /> </my-advanced-task>
Then we need to:
- add a property
basedir
to the task - add a condition to the
validate()
method - modify the
execute()
method by relativizing output
add property
We add a property and its setters.
private File basepath; /** set the relative basepath, to use when output filenames */ public void setBasepath(File basepath) { this.basepath = basepath; }
Since the property is a file, ant convert automagically the given string to a file
modify validate()
now the validate()
needs a check on the condition: if basepath is defined, and does not match a directory, so throw a build exception
public void validate(){ if( basepath!=null && ! basepath.isDirectory() ) throw new BuildException("basepath defined, but is not a valid directory"); // .. other conditions }
modify the execute()
The execute method now, take into account the new basepath
and, if it is defined, use the method File.toURI().relativize(.)
to get relative path. If the basepath
is not defined, we want the same old behaviour.
So, simply we remove the old line of code:
// .. the rest of code remains the same matchingFiles.add( found.getAbsolutePath() );
and replace it with following snippet
// if given a basepath the file path is relativized if( basepath!=null ){ matchingFiles.add( basepath.toURI().relativize(found.toURI()).getPath() ); } else { matchingFiles.add( found.getAbsolutePath() ); }
Note, we mantain the old behaviour if the basepath
is not defined.
download code and test
You can download and test the code easily:
- download archive tut.java-ant.zip, then unzip to a folder
- from command prompt, move to folder, and invoke
ant -buildfile build.java-ant.xml use
- look at the full process of
build
anduse
of the advanced task