@@ -1,6 +1,6 @@
<html>
<head>
<title>Tutorial: Tasks using Properties & Fileset s</title>
<title>Tutorial: Tasks using Properties, Filesets & Path s</title>
<meta name="author" content="Jan Matèrne">
<style type="text/css">
<!--
@@ -10,15 +10,23 @@
</style>
</head>
<body>
<h1>Tutorial: Tasks using Properties & Fileset s</h1>
<h1>Tutorial: Tasks using Properties, Filesets & Path s</h1>
<p>After reading the tutorial about <a href="tutorial-writing-tasks.html">writing
tasks</a> this tutorial explains how to get and set properties and how to use
nested filesets and paths.</p>
tasks [1] </a> this tutorial explains how to get and set properties and how to use
nested filesets and paths. Finally it explains how to contribute tasks to Ant. </p>
<h2>Content</h2>
<p><ul>
<li><a href="#s">s</a></li>
<li><a href="#goal">The goal</a></li>
<li><a href="#buildenvironment">Build environment</a></li>
<li><a href="#propertyaccess">Property access</a></li>
<li><a href="#filesets">Using filesets</a></li>
<li><a href="#path">Using nested paths</a></li>
<li><a href="#returning-list">Returning a list</a></li>
<li><a href="#documentation">Documentation</a></li>
<li><a href="#contribute">Contribute the new task</a></li>
<li><a href="#resources">Resources</a></li>
</ul></p>
@@ -45,11 +53,15 @@ That
</project>
</pre>
<p>The buildfile is in the archive <a href="tutorial-tasks-filesets-properties.zip">
tutorial-tasks-filesets-properties.zip [2]</a> in <tt>/build.xml.01-propertyaccess</tt>
(future version saved as *.02..., final version as build.xml; same for sources).</p>
<a name="propertyaccess"/>
<h2>Property access</h2>
<p>Our first step is to set a property to a value and print the value of property. So our scenario
would be
<p>Our first step is to set a property to a value and print the value of that property.
So our scenario would be
<pre class="code">
<find property="test" value="test-value"/>
<find print="test"/>
@@ -60,8 +72,8 @@ ok, can be rewritten with the core tasks
<echo message="${test}"/>
</pre>
but I have to start on known ground :-)</p>
<p>So what to do? Handling three attributes (property, value, print) and an execute. Because this
is only an introduction example I don´t do much checking:
<p>So what to do? Handling three attributes (property, value, print) and an execute method.
Because this is only an introduction example I don´t do much checking:
<pre class="code">
import org.apache.tools.ant.BuildException;
@@ -92,10 +104,10 @@ public class Find extends Task {
</pre>
As said in the other tutorial, the property access is done via Project instance.
This instance we get via the public <tt>getProject()</tt> method which we inherit from
We get this instance via the public <tt>getProject()</tt> method which we inherit from
<tt>Task</tt> (more precise from ProjectComponent). Reading a property is done via
<tt>getProperty(<i>propertyname</i>)</tt> (very simple, isn´t it?). This property returns
the value (String) or <i>null</i> if not set.<br>
the value as String or <i>null</i> if not set.<br>
Setting a property is ... not really difficult, but there is more than one setter. You can
use the <tt>setProperty()</tt> method which will do the job like expected. But there is
a golden rule in Ant: <i>properties are immutable</i>. And this method sets the property
@@ -153,8 +165,8 @@ would be
</p>
<p>What do we need? A task with two attributes (file, location) and nested
filesets. Because we had attribute handling already in the example above and the handling
of nested elements is described in the other tutorial the code should be very easy:
filesets. Because we had attribute handling already explained in the example above and the
handling of nested elements is described in the other tutorial the code should be very easy:
<pre class="code">
public class Find extends Task {
@@ -281,9 +293,9 @@ can implement our task, so that these test cases will pass.</p>
<p>On <b>//1</b> we check the prerequisites for our task. Doing that in a <tt>validate</tt>-method
is a common way, because we separate the prerequisites from the real work. On <b>//2</b> we iterate
over all nested filesets. We we don´t want to handle multiple filesets, the <tt>addFileset()</tt>
method has to reject the further calls. We can get the result of fileset via its DirectoryScanner
like done <b>//3</b>. After that we create a plattform independend String representation of
over all nested filesets. If we don´t want to handle multiple filesets, the <tt>addFileset()</tt>
method has to reject the further calls. We can get the result of a fileset via its DirectoryScanner
like done in <b>//3</b>. After that we create a plattform independend String representation of
the file path (<b>//4</b>, can be done in other ways of course). We have to do the <tt>replace()</tt>,
because we work with a simple string comparison. Ant itself is platform independant and can
therefore run on filesystems with slash (/, e.g. Linux) or backslash (\, e.g. Windows) as
@@ -298,9 +310,10 @@ location of the file as property, if we had found one (<b>//6</b>).</p>
whithout being complex :-)</p>
<p>The test case uses the ant property <i>ant.home</i> as reference. This property is set by the
<tt>Launcher</tt> class which starts ant. We can use that property in our buildfiles as a build-in
property (see [XXX]). But if we create a new ant environment we have to set that value for our own.
And we use the <junit< task in fork-mode. Therefore we have do modify our buildfile:
<tt>Launcher</tt> class which starts ant. We can use that property in our buildfiles as a
<a href="using.html#built-in-props">build-in property [3]</a>. But if we create a new ant
environment we have to set that value for our own. And we use the <junit> task in fork-mode.
Therefore we have do modify our buildfile:
<pre class="code">
<target name="junit" description="Runs the unit tests" depends="jar">
<delete dir="${junit.out.dir.xml}" />
@@ -323,9 +336,10 @@ And we use the <junit< task in fork-mode. Therefore we have do modify our
possibility of bundling files: the <path>. Fileset are easy if the files are all under
a common base directory. But if this is not the case you have a problem. Another disadvantage
is its speed: if you have only a few files in a huge directory structure, why not use a
<fileset> instead? <path>s combines these datatypes in that way that a path contains
other paths, filesets, dirsets and filelists. This is way <a href="">Ant-Contribs [XXX]</a>
<foreach> task is modified to support paths instead of filesets. So we want that, too.</p>
<filelist> instead? <path>s combines these datatypes in that way that a path contains
other paths, filesets, dirsets and filelists. This is why <a href="http://ant-contrib.sourceforge.net/">
Ant-Contribs [4]</a> <foreach> task is modified to support paths instead of filesets. So we want that,
too.</p>
<p>Changing from fileset to path support is very easy:</p>
<pre class="code">
@@ -353,7 +367,7 @@ other paths, filesets, dirsets and filelists. This is way <a href="">Ant-Contrib
<p>On <b>*1</b> we rename only the vector. It´s just for better reading the source. On <b>*2</b>
we have to provide the right method: an add<i>Name</i>(<i>Type</i> t). Therefore replace the
fileset with path here. Finally we have to modify our buildfile on <b>*3</b> because our task
don´t support nested filesets any longer. So we wrap the fileset inside a path.</p>
does n´t support nested filesets any longer. So we wrap the fileset inside a path.</p>
<p>And now we modify the testcase. Oh, not very much to do :-) Renaming the <tt>testMissingFileset()</tt>
(not really a <i>must-be</i> but better it´s named like the think it does) and update the
@@ -363,9 +377,9 @@ modified in the manner described above.</p>
<p>The test are finished. Now we have to adapt the task implementation. The easiest modification is
in the <tt>validate()</tt> method where we change le last line to <tt>if (paths.size()<1) throw new
BuildException("path not set");</tt>. In the <tt>execute()</tt> method we have a lii tle more work.
... mmmh ... in reality it´s lesser work, because the Path class does a the whole DirectoryScanner-handling
and creating absolute paths stuff for us. So the execute method is just:</p>
BuildException("path not set");</tt>. In the <tt>execute()</tt> method we have a lit tle more work.
... mmmh ... in reality it´s lesser work, because the Path class does the whole DirectoryScanner-handling
and creating-absolute- paths stuff for us. So the execute method is just:</p>
<pre class="code">
public void execute() {
@@ -463,7 +477,7 @@ elements are concatenated and separated with a customizable separator (default '
<p>Now we need a directory structure where we CAN find files with the same
name in different directories. Because we can´t sure to have one we create
one on <b>*1, *2</b>. And of course we clean up that on <b>*4</b>. The creation
one on <b>*1</b> and <b> *2</b>. And of course we clean up that on <b>*4</b>. The creation
can be done inside our test target or in a separate one, which will be better
for reuse later (<b>*3</b>).
@@ -534,7 +548,8 @@ form you do that depends on your favourite. But inside Ant there is a common for
it has advantages if you use that: all task users know that form, this form is requested if
you decide to contribute your task. So we will doc our task in that form.</p>
<p>If you have a look at the manual page of the <a href="">java [XXX]</a> task you will see<ul>
<p>If you have a look at the manual page of the <a href="CoreTasks/java.html">java [5]</a>
task you will see<ul>
<li>it is plain html</li>
<li>starts with the name</li>
<li>has sections: description, parameters, nested elements, (maybe return codes) and (most
@@ -595,7 +610,7 @@ As a template we have:
</html>
</pre>
<p>For our task we have <a href="">that [XXX ]</a>:</p>
<p>For our task we have <a href="CoreTasks/find.html">that [6 ]</a>:</p>
<pre class="code">
<html>
@@ -680,7 +695,8 @@ If we decide to contribute our task, we should do some things:<ul>
<li>create a patch file</li>
<li>publishing that patch file</li>
</ul>
The <a href="">Ant Task Guidelines [XXX]</a> support additional information on that.</p>
The <a href="../ant_task_guidelines.html">Ant Task Guidelines [7]</a> support additional
information on that.</p>
<p>Now we will check the "Checklist before submitting a new task" described in that guideline.
<ul>
@@ -707,53 +723,264 @@ test and patches zipped up to escape the HTML filter. <b><i>to do</i></b></li>
<h3>Package / Directories</h3>
This task does not depend any external library. Therefore we can use this as
a core task. This task contains only one class. So we can use the standardd package
<p> This task does not depend on any external library. Therefore we can use this as
a core task. This task contains only one class. So we can use the standard package
for core tasks: <tt>org.apache.tools.ant.taskdefs</tt>. Implementations are in the
directory <tt>src/main</tt>, tests in <tt>src/testcases</tt> and buildfiles for
tests in <tt>src/etc/testcases</tt>.
tests in <tt>src/etc/testcases</tt>.</p>
<p>Now we integrate our work into Ants distribution. So first we do an update of our
cvs tree. If not done yet, you have to checkout the ant module from Apaches cvs server
as described in <a href="http://ant.apache.org/cvs.html">Access the Source Tree (AnonCVS)
[8]</a> (password is <i>anoncvs</i>):<pre class="output">
cvs -d :pserver:anoncvs@cvs.apache.org:/home/cvspublic login //1
cvs -d :pserver:anoncvs@cvs.apache.org:/home/cvspublic checkout ant //2
</pre>
If you have a local copy of Ants sources just do an update
<pre class="output">
cvs -d :pserver:anoncvs@cvs.apache.org:/home/cvspublic login
cd ant //3
cvs -d :pserver:anoncvs@cvs.apache.org:/home/cvspublic update //4
</pre></p>
<p>We use the <i>-d</i> flag on <b>//1</b> to specifiy the cvs directory. You can
specify the environment variable CVSROOT with that value and after that you haven´t
to use that flag any more. On <b>//2</b> we get the whole cvs tree of ant. (Sorry,
but that uses a lot of time ... 10 up to 30 minutes are not unusual ... but this has
to be done only once :-). A cvs update doesn´t use a modulename but you have to be
inside the directory. Therefore we go into that on <b>//3</b> and do the update
on <b>//4</b>.</p>
<p>Now we will build our Ant distribution and do a test. So we can see if there
are any tests failing on our machine. (We can ignore these failing tests on later
steps; windows syntax used here- translate to xNIX if needed):
<pre class="output">
ANTHOME> build // 1
ANTHOME> set ANT_HOME=%CD%\dist // 2
ANTHOME> ant test -Dtest.haltonfailure=false // 3
</pre>
First we have to build our Ant distribution (<b>//1</b>). On <b>//2</b> we set the ANT_HOME
environment variable to the directory where the new created distribution is stored
(%CD% is expanded to the current directory on Windows 2000 and XP, on 9x and NT
write it out). On <b>//3</b> we let Ant do all the tests (which enforced a compile
of all tests) without stopping on first failure.</p>
<p>Next we apply our work onto Ants sources. Because we haven´t modified any, this is
a relative simple step. <i>(Because I have a local copy of Ant and usually contribute my
work, I work on the local copy just from the beginning. The advantage: this step isn´t
necessary and saves a lot of work if you modify existing source :-)</i>.
<ul>
<li>move the Find.java to ANTHOME/src/main/org/apache/tools/ant/taskdefs/Find.java </li>
<li>move the FindTest.java to ANTHOME/src/testcases/org/apache/tools/ant/taskdefs/FindTest.java </li>
<li>move the build.xml to ANTHOME/src/etc/testcases/taskdefs/<b>find.xml</b> (!!! renamed !!!)</li>
<li>add a <tt>package org.apache.tools.ant.taskdefs;</tt> at the beginning of the two java files </li>
<li>delete all stuff from find.xml keeping the targets "testFileNotPresent", "testFilePresent",
"test.init" and "testMultipleFiles" </li>
<li>delete the dependency to "use.init" in the find.xml </li>
<li>in FindTest.java change the line <tt>configureProject("build.xml");</tt> to
<tt>configureProject("src/etc/testcases/taskdefs/find.xml");</tt> </li>
<li>move the find.html to ANTHOME/docs/manual/CoreTasks/find.html </li>
<li>add a <tt><a href="CoreTasks/find.html">Find</a><br></tt>
in the ANTHOME/docs/manual/coretasklist.html </li>
</ul>
Now our modifications are done and we will retest it:
<pre class="output">
ANTHOME> build
ANTHOME> ant run-single-test // 1
-Dtestcase=org.apache.tools.ant.taskdefs.FindTest // 2
-Dtest.haltonfailure=false
</pre>
Because we only want to test our new class, we use the target for single tests, specify
the test to use and configure not to halt on the first failure - we want to see all
failures of our own test (<b>//1 + 2</b>).</p>
<p>And ... oh, all tests fail: <i>Ant could not find the task or a class this task relies upon.</i></p>
<p>Ok: in the earlier steps we told Ant to use the Find class for the <find> task (remember the
<taskdef> statement in the "use.init" target). But now we want to introduce that task as
a core task. And nobody wants to taskdef the javac, echo, ... So what to do? The answer is the
src/main/.../taskdefs/default.properties. Here is the mapping between taskname and implementing
class done. So we add a <tt>find=org.apache.tools.ant.taskdefs.Find</tt> as the last core
task (just before the <tt># optional tasks</tt> line). Now a second try:
<pre class="output">
ANTHOME> build // 1
ANTHOME> ant run-single-test
-Dtestcase=org.apache.tools.ant.taskdefs.FindTest
-Dtest.haltonfailure=false
</pre>
We have to rebuild (<b>//1</b>) Ant because the test look in the %ANT_HOME%\lib\ant.jar
(more precise: on the classpath) for the properties file. And we have only modified it in the
source path. So we have to rebuild that jar. But now all tests pass and we check whether our class
breaks some other tests.
<pre class="output">
ANTHOME> ant test -Dtest.haltonfailure=false
</pre>
Because there are a lot of tests this step requires a little bit of time. So use the <i>run-single-test</i>
during development and do the <i>test</i> only at the end (maybe sometimes during development too).
We use the <i>-Dtest.haltonfailure=false</i> here because there could be other tests fail and we have
to look into them.</p>
<p>This test run should show us two things: our test will run and the number of failing tests
is the same as directly after the cvs update (without our modifications).</p>
<h3>Apache copyright and license statement</h3>
<p>Simply copy the license text from one the other source from the Ant source tree. But
ensure that the current year is used in the<tt> * Copyright (c) 2000-2003 The Apache Software
Foundation. All rights reserved.</tt> lines.
Foundation. All rights reserved.</tt> lines. Don´t forget to add a license statement at the end
of the find.html. (Can be copied from other manual files.)</p>
<h3>Test on JDK 1.2</h3>
<p>Until version 1.5 Ant must be able to run on a JDK 1.1. With version 1.6 this is not a
requisite any more. But JDK 1.2 is a must be able. So we have to test that. You can download older
JDKs from <a href="http://java.sun.com/products/archive/index.html">Sun [9]</a>.</p>
<p>Clean the ANT_HOME variable, delete the <i>build, bootstrap</i> and <i>dist</i> directory
and point JAVA_HOME to the JDK 1.2 home directory. Then do the <tt>build</tt>, set ANT_HOME
and run <tt>ant test</tt> (like above).</p>
<p>Our test should pass.</p>
<h3>Checkstyle</h3>
There are many things we have to ensure. Indentation with 4 spaces, blanks here and there, ...
(all described in the <a href="">Ant Task Guidelines [XXX]</a> which includes the
<a href="">Sun code style [XXX]</a>. Because there are so many things we would be happy
to have a tool for do the checks. There is one: checkstyle. Checkstyle is available
at <a href="">Sourceforge [XXX]</a> and Ant provides with the <tt>check.xml</tt> a buildfile
which will do the job for us.
<p>There are many things we have to ensure. Indentation with 4 spaces, blanks here and there, ...
(all described in the <a href="../ant_task_guidelines.html">Ant Task Guidelines [7]</a> which
includes the <a href="http://java.sun.com/docs/codeconv/html/CodeConvTOC.doc.html">Sun code style
[10]</a>). Because there are so many things we would be happy to have a tool for do the checks.
There is one: checkstyle. Checkstyle is available at <a href="http://checkstyle.sourceforge.net/">
Sourceforge [11]</a> and Ant provides with the <tt>check.xml</tt> a buildfile which will do the job
for us.</p>
<p>Download it and put the checkstyle-*-all.jar into your %USERPROFILE%\.ant\lib directory.
All jar´s stored there are available to Ant so you haven´t to add it to you %ANT_HOME%\lib
directory (this feature was added with Ant 1.6).</p>
<p>So we will run the tests with
<pre class="output">
ANTHOME> ant -f check.xml checkstyle htmlreport
</pre>
I prefer the HTML report because there are lots of messages and we can navigate faster.
Open the ANTHOME/build/reports/checkstyle/html/index.html and navigate to the Find.java. Now we
see that there are some errors: missing whitespaces, unused imports, missing javadocs. So we have
to do that.</p>
<p>Hint: start at the <b>buttom</b> of the file so the line numbers in the report will keep
up to date and you will find the next error place much more easier without redoing the checkstyle.</p>
<p>After cleaning up the code according to the messages we delete the reports directory and
do a second checkstyle run. Now our task isn´t listed. That´s fine :-)</p>
<h3>Test on JDK 1.2</h3>
<h3>Creating the diff</h3>
<p>Creating a diff for Ant is very easy: just start <tt>ant -f patch.xml</tt> and all is done
automatically. This step requires a cvs executable in your path and internet access (more precise:
cvs access). As a result we get a file <i> XXX </i>.</p>
<h3>Publish the task</h3>
<h3>Publish the task</h3>
<p>Finally we publish that archive. As described in the <a href="../ant_task_guidelines.html">
Ant Task Guidelines [7]</a> we can post it on the developer mailinglist or we create a BugZilla
entry. For both we need some information:</p>
<table border="1">
<tr>
<th>subject</th>
<td><i>short description</i></td>
<td>Task for finding files in a path</td>
</tr>
<tr>
<th>body</th>
<td><i>more details about the path</i></td>
<td>This new task looks inside a nested <path/> for occurrences of a file and stores
all locations as a property. See the included manual for details.</td>
</tr>
<tr>
<th>attachements</th>
<td><i>all files needed to apply the path</td>
<td>Archive containing the path produced with path.xml</td>
</tr>
</table>
<p>Sending an email with these information is very easy and I think I haven´t to show that.
The other way - BugZilla - is slightly more difficult. But it has the advantage that entries
will not be forgotten (once per week a report is generated). So I will show this way.</p>
<p>You must have a BugZilla account for that. So open the <a href="http://nagoya.apache.org/bugzilla/">
BugZilla Main Page [12]</a> and follow the link
<a href="http://nagoya.apache.org/bugzilla/createaccount.cgi">Open a new Bugzilla account [13]</a>
and the steps described there if you haven´t one.</p>
<ol>
<li>From the BugZilla main page choose <a href="http://nagoya.apache.org/bugzilla/enter_bug.cgi">Enter
a new bug report [14]</a></li>
<li>Choose "Ant" as product </li>
<li>Version is the last "Alpha (nightly)" (at this time 1.7)</li>
<li>Component is "Core tasks"</li>
<li>Plattform and Severity are ok with "Other" and "Normal"</li>
<li>Initial State is ok with "New"</li>
<li>Same with the empy "Assigned to"</li>
<li>It is not required to add yourself as CC, because you are the reporter and therefore will be
informed on changes</li>
<li>URL: no url required</li>
<li>Summary: add the <i>subject</i> from the table</li>
<li>Description: add the <i>body</i> from the table</li>
<li>Then press "Commit"</li>
<!-- XXX MAT: Attachement eintragen, wenn ich das online auch mache - dazu brauche ich vorher
das CVS Patch -->
</ol>
<br><br><br><br><br><br><br><br>
- stichpunkte siehe ... manual
- ist das richtige package gewählt worden?
- checkstyle
- tests
- dokumentation
- jdk 1.2
- patch erstellen
- bugzilla / mailingliste
- patch erstellen --> zu hause wg. cvs zugang <br>
- bugzilla einstellen --> zu hause wg. abhängigkeit zu patch-file
<a name="resources"/>
<h2>Resources</h2>
[1] <a href="http://ant.apache.org/manual/using.html#built-in-props">http://ant.apache.org/manual/using.html#built-in-props</a><br/>
[1] <a href="tutorial-writing-tasks.html">tutorial-writing-tasks.html</a><br/>
[2] <a href="tutorial-tasks-filesets-properties.zip">tutorial-tasks-filesets-properties.zip</a><br/>
[3] <a href="using.html#built-in-props">using.html#built-in-props</a><br/>
[4] <a href="http://ant-contrib.sourceforge.net/">http://ant-contrib.sourceforge.net/</a><br/>
[5] <a href="CoreTasks/java.html">CoreTasks/java.html</a><br/>
[6] <a href="CoreTasks/find.html">CoreTasks/find.html</a><br/>
[7] <a href="../ant_task_guidelines.html">../ant_task_guidelines.html</a><br/>
[8] <a href="http://ant.apache.org/cvs.html">http://ant.apache.org/cvs.html</a><br/>
[9] <a href="http://java.sun.com/products/archive/index.html">http://java.sun.com/products/archive/index.html</a><br/>
[10] <a href="http://java.sun.com/docs/codeconv/html/CodeConvTOC.doc.html">http://java.sun.com/docs/codeconv/html/CodeConvTOC.doc.html</a><br/>
[11] <a href="http://checkstyle.sourceforge.net/">http://checkstyle.sourceforge.net/</a><br/>
[12] <a href="http://nagoya.apache.org/bugzilla/">http://nagoya.apache.org/bugzilla/</a><br/>
[13] <a href="http://nagoya.apache.org/bugzilla/createaccount.cgi">http://nagoya.apache.org/bugzilla/createaccount.cgi</a><br/>
[14] <a href="http://nagoya.apache.org/bugzilla/enter_bug.cgi">http://nagoya.apache.org/bugzilla/enter_bug.cgi</a><br/>
<br><br><br>
<!--
[15] <a href=" "> </a><br/>
[16] <a href=" "> </a><br/>
[17] <a href=" "> </a><br/>
[18] <a href=" "> </a><br/>
[19] <a href=" "> </a><br/>
[20] <a href=" "> </a><br/>
[21] <a href=" "> </a><br/>
[22] <a href=" "> </a><br/>
[23] <a href=" "> </a><br/>
[24] <a href=" "> </a><br/>
[25] <a href=" "> </a><br/>
-->
<hr>
<p align="center">Copyright © 2003 Apache Software Foundation. All rights