Location: JWare Software » articles » indepth » Do-ers
Do-ers
One of the never ending discussions for Ant users involves the various methods we can or should use to control how and when a particular task gets executed. For hours of fun-filled reading do an internet search for “Ant for loops” or something similar. The upshot of all the discussion is that Ant does have a very flexible and powerful mechanism for you to control when and how certain tasks work— but, there are two non-trivial issues with this mechanism that are at the root of much of the user discontent. First, the builtin mechanism is based on the notion of sets, specifically data or resource sets, which is not the default way a good portion of us tend to envision simple either-or branching for largely procedural scripts. To control how or if many Ant tasks affect your environment, you must figure out how to define your environment and its contents in terms of resource sets, including the empty set. Second, this mechanism is not applicable universally to all Ant components particularly third-party Ant extensions. So if you want to do two or more things with a set of data you have to hope that all the tasks involved accepted as input the same kind of set descriptors to control its function.
The core Ant developers along with dozens of “Ant: Revealed!” type books detail why sets are and will remain the fundamental organizing and controlling method and this article will leave it as a exercise for you to read their reasons. What we’re here to discuss is how AntXtras picks up where Ant leaves off. Specifically, how AntXtras focuses on ways to make those simple cases simple to implement with familiar patterns, and how AntXtras focuses on ways to extend the core Ant notion of sets and resources to collections of arbitrary tasks themselves aka tasksets. The AntXtras Design Guide describes a taskset as a task container that “overlays some kind of configuration or enforces some kind of control over its nested components.” What I want to emphasize is that as an adhoc container, a taskset let you identify explicitly a sequence of any number of tasks, of any type, in any order, in any location, for special execution control.
The Cast
Along with the concept of a taskset, AntXtras gives you four primary methods to create and control tasksets: <do[inline]>, <doforeach>, <domatch>, and <dowhile>. The differences between these methods are the ways in which they let you control when the contained tasks or type declarations are executed. In the remaining sections of this article we’ll review each of the four methods and the typical conditions under which you might use them. As usual, for a comprehensive description of the discussed Ant components (antlib, parameters, compatibility, restrictions, etc.), you should refer to the AntXtras User Guide.
Just <do> It— Or Not!
Your Requirement: Execute a set of tasks if and only if a condition is met.
The <do> taskset is the simplest way you define a collection of tasks whose execution you want to control; just nest the tasks inside of the <do> taskset and define the criteria for execution. If you don’t explicitly define any controls, you end up with the default control which basically says “reconfigure and execute these tasks always” which can be useful in certain situations. However, the primary reason for using <do>, is so you can prevent automatic execution of its taskset under certain conditions you define.
<do condition(s)> tasks to run if condition(s) met... </do>
Figure 1: General form of the <do> taskset.
Do Easy Things Easily
To define when the taskset runs, you setup one or more of its condition selector attributes. For a simple either-run-or-don’t-run requirement, you can: use one of the standard Ant selectors like “if” or “unless”, use one of the predefined AntXtras selectors like “ifAllTrue” or “ifAntLike”, or use a generic selector that defers to a custom condition you have defined. For a compound test, you can use a combination of these selectors provided you use each kind-of selector once. Table 1 describes a few of the available selectors; read the AntXtras User Guide for the complete list.
<do ifTrue="force-flag"> ...stamp version file </do> <uptodate property="@{module}-jar.uptodate"...> ...sources and version file </uptodate> <do unless="@{module}-jar.uptodate"> ...make, sign, and package jar </do>
Example 1: Using <do> to define simple conditional tasksets.
| Attribute | Implied Execution Criteria |
|---|---|
| if | Run if the named property is defined; the property’s value is unimportant. |
| unless | Run if the named property is not defined; the property’s value is unimportant. |
| ifTrue | Run if the named property is defined as a positive boolean like “yes”. |
| unlessTrue | Run if the named property is not defined as a positive boolean like “yes”. |
| true | Run if the expressed value is a positive boolean like “yes” . |
| false | Run if the expressed value is a negative boolean like “no”. |
| test | Run if the referenced rule or condition evaluates true. |
| ifAll | Run if all of the named properties are defined; property values are unimportant. |
| unlessAll | Run if any of the named properties is not defined; property values are unimportant. |
| ifAntLike | Run if Ant’s version matches the given regular expression pattern. |
| unlessOS | Run if the JRE’s OS platform does not matches the given pattern. |
Table 1: Sampling of the execution controls available for the <do> taskset.
Do Hard Things Easily
Of all the available condition selectors, you will find that “true”, “false”, and “test” are the most flexible and powerful particularly when you combine them with AntXtra’s Function Shortcuts utilities (funcuts). Rather than limit yourself to the few hard-coded attributes that match some predefined tests, you can use funcuts to gain access to all available Ant and AntXtras conditions as well as your own creations. You should also note that with the exception of these three selectors, every other pre-canned selector has a funcut equivalent so you wouldn’t lose any conditions if you used just these three. The snippet below shows how easy it is to setup a set of tasks that is run based on a simple (or not so simple) test.
<do true="${$isset:work-offline}"> ...do something if offline mode </do> <do false="${$newerdate:${requested},,${cached}}"> ...do something if need to refresh </do> <do true="${$noneset:junit.found,testng.found}"> ...do something if those flags off </do> <do test="isWeekendHours" unlessTrue="disabled-flag"> ...do something if weekend hours and enabled </do> <do ifAll="strict,ping" true="${$isref:@{servers}}"> ...do something when need to ping servers </do>
Example 2: Using <do> to define compound conditional tasksets.
| Funcut | Implied Execution Criteria |
|---|---|
| $isset: | Run if the named property is defined; the property’s value is unimportant. |
| $isnotset: | Run if the named property is not defined; the property’s value is unimportant. |
| $allset: | Run if all of the named properties are defined; property values are unimportant. |
| $anyset: | Run if any of the named properties is defined; property values are unimportant. |
| $noneset: | Run if none of the named properties are defined. |
| $isref: | Run if the value is a reference in the active project . |
| $isboolean: | Run if the expressed value is any boolean like “yes” or “no”. |
| $isnumber: | Run if expressed value is a valid integer or float number. |
| $isresource: | Run if the named resource is found in the Ant classpath. |
| $fne: | Run if the named path is to a file that is readable and non-zero bytes in size. |
| $istask: | Run if the value refers to a defined Ant task . |
Table 2: Sampling of the funcuts available for the <do> taskset.
Do Once, Do Twice, <doforeach>
Your Requirement: Execute the same taskset for a limited (but not preset) number of times.
The <doforeach> taskset lets you execute a set of tasks one or more times; just nest the tasks to run inside of the taskset and define the taskset’s loop control attributes. Because it is a simple loop wrapper, <doforeach> is the easiest way for you to bolt on the notion of a set of more than one input to tasks that do not support generic Ant sets natively. The executed components can be a single task (and very often is) or a series of independent steps that collectively make up some higher operation.
<doforeach i=cursor-name loop-control [options]> tasks to run... </doforeach>
Figure 2: General form of the <doforeach> taskset.
Do Easy Things Easily
To define the number of iterations for the taskset, you setup one of its loop definition attributes. AntXtras provides a slew of definitions that cover the most used types of loops; however, you can also define your own custom resource collections and <doforeach> will iterate those too. In addition, AntXtras creates a special cursor property that it updates automatically for each loop iteration. You define the cursor’s name (for access as a standard property within the taskset) but the cursor’s value depends on the type of loop control you’ve defined.
[For new and old files alike...] <doforeach i="file" list="${myfiles}" delim=";"> ...do something for new and existing files </doforeach> [For existing files only...] <fileset id="myfiles" dir="${stat.d}" includes="*.stat"/> <doforeach i="file" files="myfiles"> ...do something for existing status files </doforeach> [For a simple sequence...] <doforeach i="port" in="9030,9037,2"> ...do whatever needed to ping server @ ${port} </doforeach> [For generic Ant resources...] <zipfileset id="injected-jars" src="${my.ear}"> <include name="APP-INF/lib/my-*.jar"/> <date datetime="${LAST.REV}" when="after"/> </zipfileset> <doforeach i="newjar" resources="injected-jars"> ...record changed ${newjar} and get 'about' details </doforeach>
Example 3: Using <doforeach> with a variety of loop definitions.
Many of the builtin loop controls like “list” also support special modifier attributes that you would not get if you used the generic resources control “resources.” For example, you can tell AntXtras whether it should trim whitespace from each line loaded with the “infile” control; or, whether it should use a custom delimiter when it splits the items from a “list” control’s string value (the default behavior assumes a comma-delimited list of values). Table 3 describes a few of the available loop types; read the AntXtras User Guide for the complete list including associated modifier attributes.
| Attribute | Loop Control Description |
|---|---|
| list | Uses a delimited list of literal strings as the loop control. |
| files | Uses a predefined <fileset> as the loop control. |
| in | Uses a simple number range as the loop control. |
| infile | Uses the contents of the named file as the loop control. |
| pathlist | Uses a delimited path list as the loop control. |
| path | Uses a predefined <path> like structure as the loop control. |
| dirs | Uses a predefined <dirset> as the loop control. |
| resources | Uses any general Ant resource collection as the loop control. |
Table 3: Sampling of the loop controls available for the <doforeach> taskset.
Do Hard Things Easily
AntXtras provides a set of options for the <doforeach> taskset that lets you control how it should handle certain exception situations. For instance, you can use a combination of the “tryeach” and “failproperty” parameters to do all iterations, but record whether any iteration failed. You can also put in an explicit maximum loop count to handle cases where the set of values differs significantly from your expectation (possibly signalling an problem with the context or a badly defined loop control).
<doforeach i="module" infile="${mods.f}" maxloops="25" haltifmax="yes"> process each file... </doforeach> <doforeach i="port" list="${ports}" tryeach="yes" failproperty=".stop"> ping each port for each cluster... </doforeach> <do iftrue=".stop"> warn or stop due to issue with ping... </do>
Example 4: Setting the <doforeach> options to control secondary behavior.
So Many Choices, Just One <domatch>
Your Requirement: Execute one of a group of tasksets if a variable is a particular value.
The <domatch> taskset lets you match a value called a switch value to one (and only one) of a set of nested sub-tasksets called choice tasksets. Each choice taskset is identified and potentially matched by its condition selector attributes. The <domatch> parent taskset will pass its switch value to each choice taskset’s condition, and if the condition evaluates true for the value, the choice is selected and its nested tasks executed then the selection process stops there; the parent will not query the remaining choice tasksets. The <domatch> is one way you could identify a set of otherwise unrelated tasksets as somehow connected with the switch value identified as the explicit link.
<domatch switch-value [options]> <{choiceset 1 condition}> tasks to run if choiceset 1 condition met </> … <{choiceset N condition}> tasks to run if choiceset N condition met </> </domatch>
Figure 3: General form of the <domatch> taskset.
Do Easy Things Easily
There is one particular use case for a <domatch> that occurs so often that AntXtras has provided two shortcut choice tasksets to make handling this common pattern easy. Basically, if you have only two choice tasksets and the first is selected if the switch value is “true” and the other is selected otherwise, you can use these shortcut tasksets: define the tasks to be executed when the value is true inside of a <true> taskset, and define the other tasks as part of the <otherwise> taskset. The general form is shown in Figure 4.
<domatch switch-value> <true> ...tasks to run if switch value is 'true' </true> <otherwise> ...tasks to run otherwise </otherwise> </domatch>
Figure 4: Shortcut form of the common ‘if true do {a} else do {b}’ pattern.
As with the standard <do> taskset, you can gain great flexibility if you define the switch value in terms of an AntXtras function shortcut result, but this is optional. Setting the switch value to a simple property or variable value is also supported.
<domatch value="${$truefalse:@{reset}}"> <true> <doforeach i="file" list="@{statfiles}"> <echo file="${file}" message=""/> </doforeach> <delete-quietly dir="${out.d}"/> </true> <otherwise> <assign var="MESG" value="NOTSTARTED;${$iso:}"/> <doforeach i="file" list="@{statfiles}"> <echo file="${file}" message="${$v:MESG}"/> </doforeach> <delete-quietly> <fileset dir="${out.d}"> <include name="classes"/> <include name="classes-emma"/> </fileset> </delete-quietly> </otherwise> </domatch>
Example 5: Using the shortcut <true> and <otherwise> choice tasksets.
Do Hard Things Easily
Unlike the simple <do> taskset, you can only nest choice tasksets inside of a <domatch>. AntXtras provides four general types of choice taskset that differ based on the kind-of condition selector you define to test the switch value. The aforementioned <true> and <otherwise> tasksets are special presetdefs of two of these general types that AntXtras provides. The four actual types of choice tasksets are:
- <equals {test-value}> to match the switch value exactly to a {test-value}
- <like {test-expr}> to match the switch value to a regular expression {test-expr}
- <meets {test-refid}> to match the switch value to an independent test {test-refid}
- <default> to match all switch values unconditionally if no other match found
The <domatch> taskset handles the <default> choice taskset differently from the others— if defined, that taskset is always tested after all other choice tasksets have been tested and have each failed to match the switch value. Note that this test ordering is enforced regardless of the location of the <default> taskset itself within its parent <domatch>.
<equals {test-value}> -or- <like {test-expr}> -or- <meets {test-refid}> tasks to run… tasks to run… tasks to run… </equals> </like> </meets>
Figure 5: General forms of the three customized <domatch> choice tasksets.
There is one major restriction on how you use your choice tasksets: with the exception of the special <default> choice taskset, you can use only a single category of choice taskset for each <domatch> declaration— you cannot mix and match the three different types of choice tasksets in a single <domatch> parent.
<domatch property="distro"> -or- <domatch property="distro"> <equals value="local"> <like value="local|internal"> ...your tasks here ...your tasks here </equals> </like> <equals value="public"> <like value="golden|public"> ...your tasks here ...your tasks here </equals> </like> </domatch> </domatch>
Example 6: Using different choice taskset types based on test requirements.
For more sophisticated condition selectors you need to use the <meets> choice taskset. This taskset lets you define your tests as independent standard Ant conditions or AntXtras criteria (like <tallyset> or <criteria>) and link them back to the condition selectors of the <domatch>.
<criteria id="isBlackoutDay" .../> <criteria id="isOddWeekday" .../> <criteria id="isEvenWeekday" .../> <domatch property="dayofyear"> <meets criteria="isBlackoutDay"> ...notify nothing to be done! </meets> <meets criteria="isOddWeekday"> ...setup for work done on m,w,f </meets> <meets criteria="isEvenWeekday"> ...setup for work done on tu,th </meets> <default> ...setup for work done on sa,su </default> </domatch>
Example 7: Using <meets> choice taskset with custom condition selectors.
There are a few options you can define with the <domatch> taskset to control secondary behavior. For example, if it is illegal for the switch value not to match a specific choice, you can tell the <domatch> to signal an error by setting the “haltifnomatch” attribute to “true”. For the full list of options, read the AntXtras User Guide.
<domatch variable="vcs.system" haltifnomatch="yes"> <equals value="svn"> ...svn setup tasks here <equals value="cvs"> ...cvs setup tasks here <equals value="git"> ...git setup tasks here </domatch>
Example 8: Setting the <domatch> options to control secondary behavior.
End Note 1
In much older versions of Ant– versions that do not support the top-level target and macrodefs, there are situations when you could use even this default behavior as it lets you reuse targets the way you can reuse macros in the more recent Ant versions. You nest the target’s contents inside of a <do> and use the AntXtras <callinline> to re-run the target when appropriate. If you use AntXtras variables instead of Ant’s write-once properties, you get your target to act as a parameterized macro. You can also use <doinline>, an antlib-friendly variant of this default <do> setup, to automatically execute a set of tasks when a particular antlib is loaded for the first time. Essentially the <doinline> taskset’s criteria is hard-coded to this default behavior.