Chapter 6. Executing programs

6.1 Running programs under Ant—an introduction
6.2 Running native programs
6.3 Advanced <java> and <exec>
6.4 Bulk operations with <apply>
6.5 How it all works
6.6 Best practices
6.7 Summary

So far, our code compiles and the tests pass. Ant is packaging it into JAR files and then into Zip and tar packages for distribution.

This means it’s time to explore the capabilities of Ant to execute programs, both Java and native. Ant is one of the best tools for starting programs in Java; it wraps all the platform-specific details into a few tasks. It’s so useful that it’s often used behind the scenes in many applications.

Ant’s execution services come in three tasks, <java>, <exec>, and <apply>, that together meet most projects’ needs for running Java and native code. Let’s start with an overview of the problem, get into <java>, and then look at native code.

6.1. Running programs under Ant—an introduction

Ant does a lot of work in its own libraries, in the Java code behind tasks. You don’t need to explicitly run external programs on every line of a build file just to get the application compiled, packaged, and tested the way you’d have to do with the Make tool. Yet eventually, projects need to use external programs.

The most common program to run from inside Ant is the one you’re actually building, such as our diary application. You may also need a native compiler, a Perl script, or just some local utility program. There are also other Java programs to which Ant can delegate work, such as tools like javac in the JDK.

When you need to run programs from inside Ant, there are two solutions. One option is to wrap it in a custom Ant task. We’ll cover this subject in chapter 18. It’s no harder than writing any other Java class, but it does involve programming, testing, and documentation. It’s the most powerful and flexible means of integrating with Ant, and the effort is sometimes worthwhile. It’s a great way of making software easy for other developers to use.

The other option—the easy option—is using Ant’s <java> or <exec> task to run the program straight from a build file. Ant can run Java and native programs with ease. Java programs can even run inside Ant’s own JVM for higher performance. Figure 6.1 illustrates the basic conceptual model of this execution. Many Ant tasks work by calling native programs or Java programs. As a case in point, the <javac> task can run Sun’s javac compiler in Ant’s JVM, or IBM’s Jikes compiler as a native application.

Figure 6.1. Ant can spawn native applications, while Java programs can run inside or outside Ant’s JVM.

When running a program, Ant normally halts the build until it finishes. Console output from the program goes to the Ant logger, which forwards it to the screen, while input data goes the other way. Users can specify input sources and output destinations, whether they’re files, Ant properties, or filter chains, which are Ant components to process incoming data.

Before we can run our diary, we need an entry point—a method that the Java runtime can invoke to start the program running. Listing 6.1 shows this entry point in its class DiaryMain.

Listing 6.1. An entry point to add events to our diary
public static void main(String args[])
        throws Exception {
    DiaryMain diaryMain = new DiaryMain("calendar.d1");
    diaryMain.load(false);
    if (args.length > 0) {
        String time = args[0];
        String name = null;
        name = args.length > 1 ? args[1] : "event";
        String text = null;
        text = args.length > 2 ? args[2] : null;
        diaryMain.add(time, name, text);
    }
    diaryMain.save();
    System.out.println(diaryMain.toString());
}

This method will create or update the calendar file calendar.d1 with the appointment passed in as arguments. If no arguments are passed down, it lists the current diary; otherwise it tries to create one from the time, name, and text arguments passed in.

This entry point turns the JAR file into a program, one that can be run from the command line. It also can be run from Ant, which lets us integrate it with the build and which allows us to have Ant provide arguments from Ant properties and datatypes.

6.1.1. Introducing the <java> task

The central task for running Java applications is called, not surprisingly, <java>. It has many options and is well worth studying. We used it in our introductory build file in chapter 2. In this chapter, we’re going to run our diary entry point with it, an entry point that gets compiled into the JAR file created in chapter 5:

<target name="java-example-1" depends="sign-jar">
  <echo>running the diary program</echo>
  <java classname="d1.core.DiaryMain" />
</target>

The task takes the name of the entry point class via its classname attribute. What happens when we run this target? First Ant runs the targets that build the JAR file. Then Ant reaches the java-example-1 target itself:

java-example-1:
     [echo] running the diary program
     [java] Could not find d1.core.DiaryMain.
            Make sure you have it in your classpath

Oops! We left out the classpath and get to see Ant’s error message when this happens. Welcome to classpath-related pain. If you’re new to Java, this will be a new pain, but to experienced Java developers, it should be a very familiar sensation. To run our program, we have to set up the classpath for <java>.

6.1.2. Setting the classpath

Why did the <java> task fail? Why couldn’t it find our entry point? For almost any use of the <java> task, you’ll need a custom classpath. The basic <java> task runs only with Ant’s classpath, which is as follows:

  • Ant’s startup JAR file, ant-launcher.jar.
  • Everything in the ANT_HOME/lib directory (or more accurately, the directory containing the version of ant-launcher.jar that’s running).

  • All JAR files in ${user.home}/.ant/lib. On Unix, that’s "~/.ant/lib", while on Windows it’s something like "c:Documents and SettingsUSER.antlib"; the exact location varies with the user name and locale.
  • All directories or JAR files named with -lib options on the command line.
  • Everything in the CLASSPATH environment variable, unless -noclasspath is set on the command line.
  • Any libraries an IDE hosting Ant saw fit to include on the classpath.
  • If Ant can find it, JAVA_HOME/lib/tools.jar.

Adding classpaths is easy: you just fill out the <java> task’s <classpath> element, or set its classpath attribute to a path as a string. If you’re going to use the same classpath in more than one place, declare the path and then refer to it using the classpathref attribute. This is simple and convenient to do.

One common practice is to extend the compile-time classpath with a second classpath that includes the newly built classes, either in archive form or as a directory tree of .class files. We do this by declaring two classpaths, one for compilation and the other for execution:

<path id="compile.classpath">
  <fileset dir="lib" includes="*.jar" />
</path>

<path id="run.classpath">
  <path refid="compile.classpath"/>
  <pathelement location="${target.jar}"/>
</path>

The compile classpath will include any libraries we depend upon to build; then the run classpath extends this with the JAR just built. This approach simplifies maintenance; any library used at compile time automatically propagates to the runtime classpath.

With the new classpath defined, we can modify the <java> task and run our program:

<target name="java-example-2" depends="sign-jar">
  <echo>running with classpath ${toString:run.classpath}</echo>
  <java classname="d1.core.DiaryMain"
    classpathref="run.classpath" />/>
</target>

The successful output of this task delivers the results we want:

java-example-2:
     [echo] running with classpath
           /home/ant/diary/core/dist/diary-0.1alpha.jar
     [java] File :calendar.d1

BUILD SUCCESSFUL
Total time: 1 second

Our program worked: it listed the empty diary. We also printed out the classpath by using the ${toString:run.classpath} operation. When Ant expands this, it resolves the reference to run.classpath, calls the datatype’s toString() method, and passes on the result. For Ant paths, that value is the path in a platform-specific path form. The result is we can see what the classpath will be.

 

Note

When running <java> with a custom classpath, nearly everything on Ant’s classpath other than the java and javax packages are excluded. Compare this to <javac>, which includes Ant’s classpath unless it’s told not to with includeAntRuntime="false".

 

Setting up the classpath and the program entry point are the two activities that must be done for every <java> run. To pass data to a running program, the task needs to be set up with the arguments for the main() method and any JVM system properties that the program needs.

6.1.3. Arguments

The argument list is the main way of passing data to a running application. You can name arguments by a single value, a line of text, a filename, or a path. These arguments are passed down in one or more <arg> elements, an element that supports the four attributes listed in table 6.1. Ant resolves the arguments and passes them on in the order they’re declared.

Table 6.1. The attributes of the <arg> element of <java>

<arg> attribute

Meaning

value String value
file File or directory to resolve to an absolute location before invocation
path A string containing files or directories separated by colons or semicolons
pathref A reference to a predefined path
line Complete line to pass to the program

Let’s feed some of them to our program.

<java classname="d1.core.DiaryMain"
  classpathref="run.classpath" >
  <arg value="2006-07-31-08:30" />
  <arg file="." />
  <arg path="${user.home};/" />
</java>

This uses three of the options.

<arg value="2006-07-31-08:30" />

String arguments are the simplest. Any string can be passed to the invoked program. The only tricky spot is handling those symbols that XML does not like. Angle brackets need to be escaped, substituting &lt; for < and &gt; for >.

The second argument used the file attribute to pass down a filename—the current directory:

<arg file="." />

As with <property location> assignments, this attribute can take an absolute or relative path. Ant will resolve it to an absolute location before passing it to the program.

The path attribute passes an entire path down. It generates a single argument from the comma- or colon-separated file path elements passed in

<arg path="${user.home};/" />

As with other paths in Ant, relative locations are resolved, and both Unix or MS-DOS directory and path separators can be used. The invoked program receives a path as a single argument containing resolved filenames with the directory and path separators appropriate to the platform. If we had wanted to pass a predefined path down, we could have used the pathref attribute instead:

<arg pathref="run.classpath" />

Ant will retrieve the path and add it as another argument.

Returning to our Ant target, and running it on Windows, this is what we see:

[java] Event #3ca33f56-21a1-41f8-bf1a-7e3c2c3dc207
       - Mon Jul 31 09:30:00 BST 2006
       - C:app2diarycore
       - C:Documents and Settingsant;C:

What do we see on a Unix box?

[java] Event #0d1aec19-0c51-46b4-a3d3-c48e49a2319f
       - Mon Jul 31 09:30:00 BST 2006
       - /home/ant/diary/core
       - /home/ant/:/

All the files and paths are resolved relative to the project directory and converted to the platform-specific representation before any invoked program sees them. We also have encountered one little quirk of Ant path mapping: what does "/" resolve to? In Unix, this is the root directory; every file in a mounted drive is underneath this directory. But in Windows, it resolves to the root directory of the current disk. There’s no single root directory for Windows systems. This can cause a problem if you want to create a single fileset spanning content in two Windows disks, such as C: and D: because it cannot be done.

There is one other way to pass arguments down, using the line attribute. This takes a single line, which is split into separate arguments wherever there’s a space between values:

<java classname="${main}" classpathref="run.classpath" >
  <arg line ="2006-07-31-08:30 . ${user.home};/" />
</java>

Is this equivalent? Not where the home directory has a space in it:

[java] Event #2c557ace-8aa1-4501-a99c-2e7449761442
       - Mon Jul 31 09:30:00 BST 2006
       - .
       - C:Documents

The line was split by spaces after property expansion and the trailing "and Settingsant" portion of the path was turned into argument number four on the command line, where it got lost. What’s insidious here is that the target worked properly on Unix; we’ve written a brittle build file, which works only on systems where the current directory and user.home directory are without spaces. Note also that our local directory has not been expanded, and if we had escaped that last argument with single quotes ("2006-07-31-08:30 . '${user.home};/'"), the result would still not be a valid path for the local platform.

 

Issue

<arg value> is not <arg line>—People are often disappointed that <java> or <exec> doesn’t correctly pass the contents of an argument like <arg value="some commands here"> down to the program. They were expecting Ant to pass a series of all arguments down to the program, but Ant passed them down as a single argument. Learn the differences between the different argument options, and use the right one for each problem.

 

Avoid using the <arg line=""> option. The only reason for using it is to support an unlimited number of arguments—perhaps from a property file or prompted input. Anyone who does this had better hope that spaces aren’t expected in individual arguments.

The other way to pass data down is through JVM system properties.

6.1.4. Defining system properties

Java system properties are set on the Java command line as -Dproperty=value arguments. The <java> task lets you use these properties via the <sysproperty> element. This is useful in configuring the JVM itself, such as controlling how long network addresses are cached:

<sysproperty key="networkaddress.cache.ttl" value="300"/>

There are two alternate options instead of the value parameter: file and path. Just as with <arg> elements, the file attribute lets you name a file. Ant will pass down an absolute filename in the platform-specific format. The path attribute is similar, except that you can list multiple files, and it will convert the path separator to whatever is appropriate for the operating system:

<sysproperty key="configuration.file" file="./config.properties"/>
<sysproperty key="searchpath"
    path="build/classes:lib/javaee.jar" />

We used these elements in chapter 4, passing information down to test cases running under <junit>. It shouldn’t be a surprise to readers to discover that <junit> makes use of the <java> task internally.

We can also pass Ant properties down in bulk.

Propertysets

A propertyset is an Ant datatype that represents a set of properties. You can use it inside the <java> task to pass down a set of Ant properties

<syspropertyset >
  <propertyref builtin="commandline" />
  <propertyref prefix="proxy.*" />
</syspropertyset>

This declaration passes down to the program all Ant properties beginning with "proxy" and all properties on Ant’s own command line. The <propertyset> datatype has a few more features that make it useful. Being a datatype, you can declare it in one location with an id attribute and reuse it via idref attributes. You can also use a mapper to rename the properties in the set.

Some Java properties, such as java.endorsed.dirs, are interpreted by the JVM itself, not the application. For these to be picked up reliably, we need to run our code in a new Java process, a new JVM instance. In Ant, this is called a forked JVM.

6.1.5. Running the program in a new JVM

The <java> task runs inside Ant’s JVM unless the fork attribute is set to true. Non-forked code can be faster and shares more of the JVM’s state. Forking offers the following advantages:

  • You get perfect isolation from Ant’s own classloader and code.
  • Forked Java programs can run in a new working directory, with different JVM options and environment.
  • Forked Java programs can run in a different version of Java than Ant itself.
  • You cannot run a JAR ("-jar something.jar") in the Ant’s JVM.
  • Memory-hungry programs can run in their own JVM and not use Ant’s memory.
  • Forked programs can be killed if they don’t finish in a specified time period.

Let’s run the diary in a new JVM:

<target name="java-example-fork" depends="build">
  <property name="classpath-as-property" refid="run.classpath" />
  <java classname="d1.core.DiaryMain"
    classpathref="run.classpath"
    fork="true">
    <arg value="2005-06-31-08:30" />
    <arg file="." />
    <arg path="${user.home};/" />
  </java>
</target>

In informal experiments on a single machine, the time to build and run the program usually appears to be two seconds, rather than the one second that the original version took. That could be a doubling of execution time, but it’s only an extra second, as measured with Ant’s single-second clock. Does a second or so of delay matter that much?

We prefer to always fork our Java programs, preferring isolation over possible performance gains. This reduces the time spent debugging obscure problems related to classloaders and JVM isolation. It also lets us tune JVM options.

6.1.6. JVM tuning

Once we fork <java>, we can change the JVM under which it runs and the options in the JVM. This gives us absolute control over what’s going on.

You can actually choose a Java runtime that’s different from the one hosting Ant by setting the jvm attribute to the command that starts the JVM. This lets you run a program under a different JVM.

In addition to choosing the JVM, you can configure it. The most commonly used option is the amount of memory to use, which is so common that it has its own attribute, the maxmemory attribute. The memory option, as per the java command, takes a string that lists the number of bytes, kilobytes (k), or megabytes (m) to use. Usually, the megabyte option is the one to supply, with maxmemory="64m" setting a limit of 64MB on the process.

Other JVM options are specific to individual JVM implementations. A call to java -X will list the ones on your local machine. Because these options can vary from system to system, they should be set via properties so that different developers can override them as needed. Here, for example, we set the default arguments for memory and the server JVM with incremental garbage collection, using properties to provide an override point for different users:

<target name="java-example-jvmargs" depends="build">
  <property name="jvm" value="java" />
  <property name="jvm.gc.args" value="-Xincgc"/>
  <property name="jvm.memory" value="64m"/>
  <property name="jvm.type" value="-server"/>
  <java
    classname="d1.core.DiaryMain"
    classpathref="run.classpath"
    fork="true"
    jvm="${jvm}"
    dir="${build.dir}"
    maxmemory="${jvm.memory}">
    <jvmarg value="${jvm.type}" />
    <jvmarg line="${jvm.gc.args}"/>
    <arg value="2005-06-31-08:30" />
    <arg file="."/>
  </java>
</target>

You supply generic JVM arguments using <jvmarg> elements nested inside the <java> task. The exact syntax of these arguments is the same as for the <arg> elements. We use the line attribute, despite the negative things we said about it earlier, as it permits a single property to contain a list of arguments. Developers can override the jvm.gc.args property in their build.properties file to something like

-XX:+UseConcMarkSweepGC -XX:NewSize=48m -XX:SurvivorRatio=16

This would transform their garbage collection policy, without affecting anyone else.

We’ve also changed the starting directory of the program with the dir attribute. This lets you use relative file references in your code and have them resolved correctly when running. If you run this example, you may be surprised to see from the output that the file argument <arg file="."/> still resolves to the directory diary/core, not the build directory in which the JVM is running. Only the new JVM picks up the directory change, not Ant itself.

None of these JVM options have any effect when fork="false"; only a warning message is printed. If they don’t seem to work, look closely at the task declaration and see if forking needs to be turned on. Ant’s -verbose flag will provide even more details.

Regardless of whether the program runs in Ant’s own JVM or a new process, we can still capture the return code of the program, which can be used to pass information back to the caller or signal an error.

6.1.7. Handling errors

What’s going to happen if we pass a bad parameter to our program? Will the build break? First, we try to create an event on a day that doesn’t exist:

<java classname="d1.core.DiaryMain"
  classpathref="run.classpath" >
  <arg value="2007-02-31-08:30" />
  <arg file="Event on February 31" />
</java>

Running this, we get a stack trace, but the build still thinks it succeeded.

[java] Exception in thread "main"
        java.lang.IllegalArgumentException:
       "2007-02-31-08:30" is not a valid representation of
       an XML Gregorian Calendar value.
[java]  at com.sun.org.apache.xerces.internal.jaxp.datatype.
         XMLGregorianCalendarImpl.<init>
[java]  at com.sun.org.apache.xerces.internal.jaxp.datatype.
         DatatypeFactoryImpl.newXMLGregorianCalendar
[java]  at d1.core.Event.parseIsoDate(Event.java:187)
[java]  at d1.core.Event.setDate(Event.java:175)
[java]  at d1.core.DiaryMain.add(DiaryMain.java:61)
[java]  at d1.core.DiaryMain.main(DiaryMain.java:97)
[java] Java Result: 1

BUILD SUCCESSFUL

The program failed, but Ant was successful. What happened?

Many Ant tasks have an attribute, failonerror, which controls whether the failure of the task breaks the build. Most such tasks have a default of failonerror="true", meaning any failure of the task breaks the build, resulting in the familiar BUILD FAILED message.

The <java> task is one such task, halting the build if failonerror="true" and the return value of the Java program is non-zero. That happens if System.exit() is called with a non-zero argument or if the JVM failed, as it does when the entry point throws an exception, or if the command line arguments to the JVM are invalid. To get this behavior, developers must explicitly set the attribute, as shown here:

<target name="java-example-failonerror" depends="build">
  <java classname="d1.core.DiaryMain"
    classpathref="run.classpath"
    fork="true"
    failonerror="true">
    <arg value="2007-02-31-08:30" />
    <arg file="Event on February 31" />
  </java>
</target>

Calling this target, we get an error message and a halted build:

[java] Exception in thread "main"
        java.lang.IllegalArgumentException:
       "2007-02-31-08:30" is not a valid representation of
       an XML Gregorian Calendar value.
[java]  at com.sun.org.apache.xerces.internal.jaxp.datatype.
         XMLGregorianCalendarImpl.<init>
[java]  at com.sun.org.apache.xerces.internal.jaxp.datatype.
         DatatypeFactoryImpl.newXMLGregorianCalendar
[java]  at d1.core.Event.parseIsoDate(Event.java:187)
[java]  at d1.core.Event.setDate(Event.java:175)
[java]  at d1.core.DiaryMain.add(DiaryMain.java:61)
[java]  at d1.core.DiaryMain.main(DiaryMain.java:97)
[java] Java Result: 1

BUILD FAILED
/home/ant/diary/core/core-chapter6.xml:128: Java returned: 1

This is normally what you want, so set failonerror="true" on all uses of <java>. Alternatively, we could capture the return value and act on it in follow-up tasks.

Capturing the return code

Sometimes, you want to know if the program returned a status code or what the value was without halting the build. Knowing this lets you do conditional actions on the return code or run programs that pass information back to Ant. To get the return code, set the resultproperty attribute to the name of a property and leave failonerror="false":

<target name="java-resultproperty" depends="build">
  <java classname="d1.core.DiaryMain"
    classpathref="run.classpath"
    fork="true"
    resultproperty="result" >
    <arg value="2007-02-31-08:30" />
    <arg file="Event on February 31" />
  </java>
  <echo>result=${result}</echo>
</target>

This will print out the result. We could use a <condition> to test the return value and act on it, or pass the property to another task.

6.1.8. Executing JAR files

So far, we’ve been executing Java programs by declaring the entry point in the classname attribute of the <java> task. There’s another way: running a JAR file with an entry point declared in its manifest. This is equivalent to running java -jar on the command line. Ant can run JAR files similarly, but only in a forked JVM. To tell it to run a JAR file, set the jar attribute to the location of the file:

<target name="java-jar" depends="build">
  <java
    jar="${target.jar}"
    fork="true"
    failonerror="true">
    <arg value="2007-06-21-05:00" />
    <arg file="Summer Solstice" />
  </java>
</target>

For this to work, the manifest must be set up correctly. We declared a Main class when we created our JAR files in section 5.5, but now we’ve changed the manifest to point to d1.core.DiaryMain. When we run this new target, we get the same output as before.

If our JAR file depended upon other libraries, we must declare these dependencies in the manifest by providing a list of relative URLs in the Class-Path attribute of the Main section in the JAR manifest. There’s no other way to set the classpath for JAR files, as java itself ignores any command-line classpath when running JAR files via -jar. Running Java programs this way is simple but inflexible. We normally use the classname attribute and set up the classpath in Ant.

That’s the core of <java> execution covered. Now it’s time to introduce <exec>, which can run native executables. Both tasks have a lot in common, so many of the concepts will be familiar. We’ll return to <java> when we look at features common to both tasks.

6.2. Running native programs

A native program is any program compiled for the local system, or, in Unix, any shell script marked as executable. Many Ant tasks call them behind the scenes, including <java> itself when it starts a new JVM for a forked <java> call. There are many other programs that can be useful, from a command to mount a shared drive, to a native installer program. Ant can call these programs with the parameters you desire.

To run an external program in Ant, use the <exec> task. It lets you declare the following options:

  • The name of the program and arguments to pass in
  • The directory in which it runs
  • Whether the application failure halts the build
  • The maximum duration of a program
  • A file or a property to store the output
  • Other Ant components to act as input sources or destinations
  • Environment variables
  • A string that should be in the name of the OS

The syntax of <exec> is similar to <java>, except that you name an executable rather than a Java class or JAR. One use of the task is to create a symbolic link to a file for which there is no intrinsic Java command:

<exec executable="ln">
  <arg value="-s"/>
  <arg location="execution.xml"/>
  <arg location="symlink.xml"/>
</exec>

You don’t need to supply the full path to the executable if it’s on the current path. You can use all the options for the <arg> nested element as with the <java> task, as covered in section 6.1.3.

We can even use the task to start Java programs.

6.2.1. Running our diary as a native program

If a Java program has a shell script to launch the program, <exec> can start it. We can use it to start our diary application via a Python script. This script will launch the diary JAR via a java -jar command, passing down all arguments.

To run this script from Ant (Unix only), we invoke it after it’s been prepared by the "scripts" target, which uses <chmod> to make the file executable:

<target name="exec-python" depends="scripts,sign-jar">
  <exec executable="${dist.dir}/diary.py"
    dir="${build.dir}"
    failonerror="true">
    <arg value="2007-12-21-12:00" />
    <arg value="Midwinter" />
  </exec>
</target>

We need to give the full path to the script or program to run. If we didn’t, <exec> would fail with some OS-specific error code if the executable couldn’t be found. The executable attribute doesn’t convert its value to a file or location but executes it as is. This is inconvenient at times, but it does let us run anything on the path.

As with <java>, the <exec> task has failonerror="false" by default. Again, we normally set failonerror="true".

There’s a second problem with execution. What happens if a program cannot be run, because it’s not there? This is a separate failure mode from the return code of a program that actually starts, which is what failonerror cares about. There’s a different attribute, failIfExecuteFails, which controls whether the actual execution failures are ignored. It’s true by default, which is where most people should leave it.

One common use of <exec> is issuing shell commands, which is unexpectedly tricky.

6.2.2. Executing shell commands

We’ve reached the point where we can use <exec> to start a program. Can we also use it to run shell commands? Yes, with caveats. Many shell commands aren’t native programs; rather, they’re instructions to the shell itself. They can contain shell-specific wildcards or a sequence of one or more shell or native commands glued together using shell parameters, such as the pipe (|), the angle bracket (>), double angle brackets (>>), and the ampersand (&).

For example, one might naively try to list the running Java processes and save them to a file by building a shell command, and use this shell command in <exec> as a single command, via the (deprecated) command attribute:

<exec command="jps > processes.txt"
  failonerror="false"/>

This won’t work. In addition to getting a warning for using the command attribute, the whole line needs to be interpreted by a shell. You’ll probably see a usage error from the first program on the line—in this case the jps command:

exec-ps:
     [exec] The command attribute is deprecated.
     [exec] Please use the executable attribute and nested arg
            elements.
     [exec] Malformed Host Identifier: >processes.txt
     [exec] usage: jps [-help]
     [exec]        jps [-q] [-mlvV] [<hostid>]
     [exec] Definitions:
     [exec]     <hostid>:      <hostname>[:<port>]

The trick is to make the shell the command and to pass in a string containing the command you want the shell to execute. The Unix sh program lets you do this with its -c command, but it wants the commands to follow in a quoted string. XML doesn’t permit double quotes inside a double quote–delimited literal, so you must use single quotes, or delimit the whole string in the XML file with single quotes:

<exec executable="sh"
  failonerror="true"/>
  <arg line="-c 'jps &gt; processes.txt'" />
</exec>

A command that uses both single and double quotes needs to use the &quot; notation instead of the double quote. Our simple example doesn’t have this problem.

The Windows NT command shell CMD is similar to the Unix one:

<exec executable="cmd"
  failonerror="true"/>
  <arg line="/c jps &gt; processes.txt" />
</exec>

A lot of people encounter problems trying to run shell commands on Ant; it keeps the user mailing list busy on a regular basis. All you have to do is remember that shell commands aren’t native programs. It’s the shell itself that Ant must start with the <exec> task.

Running shell scripts shows another problem: the program we want to run varies with the underlying platform. Can you run such programs and keep your build file portable? The answer is “Maybe, if you try hard enough.”

6.2.3. Running under different Operating Systems

The <exec> task can keep build files portable by letting you list which operating systems a program runs on. If Ant isn’t running on a supported OS, the program isn’t started. There are two attributes for this situation. The first attribute, os, takes a list of operating systems. Before the task is executed, Ant examines the value of the os.name system property and executes the program only if that property is found in the os string. A valid os attribute would have to be something such as "Linux AIX Unix", based on your expectations of which OS versions would be used. This method is very brittle and usually breaks when a new OS ships.

The newer osfamily attribute lets you declare which particular family of operating systems to use, according to the families listed in table 6.2. This method is much more robust, as an osfamily="winnt" test will work for all current and future NT-based Windows operating systems.

Table 6.2. Operating system families recognized by Ant. The <os> condition accepts these in its family attribute, as does the osfamily attribute of <exec> and its descendants.

Attribute value

Platform

windows Windows 9x or NT-based systems
win9x Windows 95-98, Windows ME, and WinCE
winnt Windows NT-based systems (Including Win2K, XP, Vista, and successors)
mac Apple Macintosh (all versions)
unix All Unix systems, including Linux and Macintosh
os/2 OS/2
netware Novell Netware
openvms HP (originally DEC) OpenVMS
os/400 IBM OS/400 minicomputer OS
z/os IBM z/OS and OS/390
tandem HP (formerly Tandem) nonstop OS

We can use this attribute to restrict a program to a specific OS family. Doing so allows us to have different versions for different platforms side by side. Here, for example, is a target that runs the Windows or Unix time commands:

<target name="time">
  <exec executable="cmd"
    failonerror="true"
    osfamily="winnt">
    <arg value="/c"/>
    <arg value="time"/>
    <arg value="/t" />
  </exec>
  <exec executable="date"
    failonerror="true"
    osfamily="unix">
    <arg value="+%H:%M" />
  </exec>
</target>

The Windows command is actually implemented by the shell, so we have to execute the cmd program with the appropriate arguments. The Unix program is a self-contained executable. By having the two tasks in the same target, each guarded by its own osfamily attribute, the relevant target will be run. The result will be the time according to the OS, something like

date:
     [exec] 15:27

If you have a lot of <exec> code in your build and you want to make it multiplat-form, use this osfamily attribute to make it somewhat portable. Ant tasks such as <chmod> use it internally to avoid breaking under Windows.

Incidentally, the same operation system test can be used in an Ant condition.

The <os> condition

The Ant condition <os> can examine the operating system. This can be used to skip targets, depending on the underlying operating system. Here’s a simple target that you can use to see what Ant thinks a system is:

<target name="os" >
  <condition property="is.unix">
    <os family="unix" />
  </condition>
  <condition property="is.nt">
    <os family="winnt" />
  </condition>
  <condition property="is.mac">
    <os family="mac" />
  </condition>
  <echo>
  os.name=${os.name}
  os.arch=${os.arch}
  os.version=${os.version}

  is.unix=${is.unix}
  is.winnt=${is.winnt}
  is.mac=${is.mac}
  </echo>
</target>

This target will produce different output on the various platforms, such as here, the Mac OS/X listing:

[echo]
[echo]   os.name=Mac OS X
[echo]   os.arch=ppc
[echo]   os.version=10.3.8
[echo]
[echo]   is.unix=true
[echo]   is.winnt=${is.winnt}
[echo]   is.mac=true
[echo]

The combination of a new Java version and a new OS can sometimes break all this logic. There’s no specific test for a platform being Windows- or Unix-based in Java, so Ant makes guesses based on OS names and path and directory separators. When a new version of Java gives an OS a new name, Ant can guess wrong. That’s always a good time to upgrade Ant.

We can also use conditions to set up <exec> itself, either by checking for the OS or by looking for the program.

6.2.4. Probing for a program

Sometimes, you want to run a program, but you are prepared to continue the build if it isn’t there. If you know where the program must be, then an <available> test can look for it. But what if the only requirement is that it must be on the path? The <available> task or condition can search a whole file path for a named file, so probing for a program’s existence is a simple matter of searching for its name in the environment variable PATH. Of course, in a cross-platform manner, nothing is ever simple; Windows and Unix name executables and even the PATH variable differently. Taking this into account, looking for a program becomes a multiple-condition test. The test needs to look for the executable with and without the .exe extension, across two different environment variables converted to Ant properties:

<target name="probe_for_python" >
  <property environment="env" />
  <condition property="python.found" >
    <or>
      <available file="python"     filepath="${env.PATH}" />
      <available file="python.exe" filepath="${env.Path}" />
    </or>
  </condition>
  <echo>python.found = ${python.found}</echo>
</target>

If we run this, it tells us if the python program is ready to run:

probe_for_python:
     [echo] python.found = true

You can then write dependent targets that skip some work if the program is absent:

<target name="python" depends="probe_for_python" if="python.found">
  <exec executable="python" ... />
</target>

We do this in our build files to probe for programs, if they really are optional. If we have to have them present, we just let <exec> fail with an error message, as it saves work.

We’ve covered the basics of <exec>. We’ve also looked at how to use <exec> to run a program, to run a shell script, and to skip the execution if the operating system is unsupported or the program is missing. Along with the <java> task, readers now have enough information to use the tasks to run their own and other people’s programs, both Java and native.

The tasks can do a lot more, though. You can tell Ant to kill the programs if they take too long, change the environment in which they execute, or pipe data between the program and Ant. These are advanced features offered by both tasks.

6.3. Advanced <java> and <exec>

Running programs is one of the most complex things that Ant can do. Programs don’t run in isolation: they operate in an environment that can change their behavior. They have a stream giving them text, and two output streams—one for normal text, one for errors. Developers may need to link those inputs and outputs to files or other things that can produce or consume the data. Sometimes, programs hang, so the build needs to kill the programs if they take too long. Sometimes, the program may be expected to outlast the build itself. These are needs developers have, so they are problems that Ant has to address.

The <java> and <exec> tasks round off their ability to run programs with the attributes and elements needed for total control of how they run programs. Let’s run through them, starting with environment variables.

6.3.1. Setting environment variables

All programs run in an environment, one in which the PATH variable controls the search path for executables, and JAVA_HOME defines the location of the JDK. Ant normally passes its own environment down to new processes that it starts. We can change that, passing a new environment down to child processes, which is useful when running some programs. What we cannot do is change Ant’s own environment—this is fixed when Ant is run.

You can set environment variables inside <java>, <junit> and <exec> by using the element <env>. The syntax of this element is identical to that of the <sysproperty> element:

<java classname="d1.core.DiaryMain"
  classpathref="run.classpath"
  fork="true">
  <env key="JAVA_HOME" value="${user.home}/JDK" />
  <env key="PATH" path="${dist.dir};${env.PATH}"/>
  <env key="Path" path="${dist.dir};${env.Path}"/>
  <arg value="2006-06-31-08:30" />
  <arg file="." />
  <arg path="${user.home};/" />
</java>

In <java> and <junit>, the PATH environment variable controls where native libraries are loaded by the JNI facility. Setting the path on a forked program—and it must be forked—lets us load new JNI libraries in the program that’s run by <java>.

The feature is invaluable in <exec>, as many native programs read the environment variables. You also can choose whether the program inherits Ant’s current environment through the newenvironment attribute. Usually, you pass down all current environment settings, such as PATH and TEMP, but sometimes you may want absolute control over the parameters:

<exec executable="preprocess"
  newenvironment="true" >
  <env key="PATH" path="${dist.dir};${env.PATH}"/>
  <env key="Path" path="${dist.dir};${env.Path}"/>
  <env key="USER" value="self"/>
</exec>

Setting environment variables lets us control how some programs run. We also like to control how programs stop, killing them if they take too long.

6.3.2. Handling timeouts

How do you deal with programs that hang? You can stop a running build by hand, but unattended builds need to take care of themselves. To address this issue, the <exec> and <java> tasks support timeout attributes, which accept a number in milliseconds. The <java> task also needs fork="true" for timeout protection; <exec> always forks, so this is a non-issue.

When a program is executed with the timeout attribute set, Ant starts a watchdog thread that sleeps for the duration of the timeout. When it wakes up, this thread will kill the program if it has not yet finished. When this happens, the return code of the <exec> or <java> call is set to the value “1”. If failonerror is set to true, this will break the build. Here’s a somewhat contrived example, using the Unix sleep command to sleep for fifteen seconds, with a timeout of two seconds[1]:

1 If you really need to insert a pause into a build, the <sleep> task works across all platforms.

<target name="sleep-fifteen-seconds" >
  <echo message="sleeping for 15 seconds" />
  <exec executable="sleep"
    failonerror="true"
    timeout="2000">
    <arg value="15" />
  </exec>
</target>

Running this target produces an error when the timeout engages:

sleep-fifteen-seconds:
     [echo] sleeping for 15 seconds

BUILD FAILED
core-chapter6.xml:195: Timeout: killed the sub-process
Total time: 3 seconds
Process ant exited with code 1

It’s useful to have timeouts for all programs that run in automated builds, especially those programs you write yourself—since they may be less stable. We often do timeouts for <junit> runs too, for a similar reason. Nobody likes to find the background build has hung for the entire weekend because a test went into an infinite loop.

Long-lived programs don’t have to block Ant if we run them in the background.

6.3.3. Running a program in the background

During testing or if you want to use Ant as a launcher of Java programs, you may want Ant to run the program in the background, perhaps even for it to outlive the Ant run itself. In Ant terminology, this is called spawning. Spawning can be accomplished by setting the spawn attribute to true. After starting the program, Ant detaches the program from its own process completely so that when Ant exits, the spawned program will keep going.

Spawning has a price, which is complete isolation from all further interaction between Ant and the spawned program. Here are the consequences:

  • There’s no way to kill a spawned process from Ant.
  • There’s no notification when the process terminates.
  • Result codes aren’t accessible.
  • You cannot capture any output from the program.
  • You cannot set the timeout attribute.

Both <exec> and <java> support the spawn attribute, though with <java> you must also set fork="true":

<java
  jar="${target.jar}"
  fork="true"
  spawn="true">
  <arg value="2038-01-19-03:14" />
  <arg value="32-bit signed time_t wraps around" />
</java>

Here we’re lucky: our program terminates of its own accord. If it did not, we would have to find the process using the appropriate OS tools and kill it. GUI applications are much easier to deal with, as you can just close their windows. As an example, the jconsole program that comes with Java is something that developers may want to run against the JVM of a process under development:

<target name="jconsole" >
  <exec executable="jconsole"
    spawn="true" />
</target>

The way that Ant completely forgets about the program is a bit brutal, as it’s hard to stop the program afterwards. We do sometimes use spawned programs in functional testing. You start the program/application server, then run the unit tests, then run a special program to shut down the application. If you’re not careful, your builds can leak spawned applications, so use the spawn option with care.

The other advanced features of the tasks are all related to integrating the program with Ant. Ant can pass data to the program and capture the results, letting us pull those results into files and Ant properties.

6.3.4. Input and output

Running <java> and <exec> programs can interact with the user, getting input from the console and feeding their output through Ant’s logging system. What if we want Ant itself to capture the output, perhaps to feed it into another task? That and generating input for the program are both possible under Ant. You can feed the program input data from a file or property, and then save the output to different files or properties. The attributes to configure all this are listed in table 6.3.

Table 6.3. Attributes of <java> and <exec> for sharing data with Ant
input A file to use as a source of input
inputstring A string to use as a source of input
output File to save the output
outputproperty Name of a property in which to store output
error File to save error (stderr/System.err) output
errorproperty Name of a property in which to store error output
append Set to true, will append the output to the files, rather than overwrite existing files
logerror Force error output to the log while capturing output

To provide input to a program, we can identify a file with the input attribute, or a string with inputstring. Setting inputstring="" is a way to tell Ant not to pass any data down.

The core attributes for capturing output are output and outputProperty. If they alone are set, then all output from an application, both the “normal” and “error” streams (System.out and System.err), are redirected. If you still want to see error output on the log, set the logerror attribute to true and Ant will still log it.

We can also remove the error log from the normal output by setting either the error filename attribute or the errorproperty property name. The named destination will get the error stream, and the output destination will get the System.out messages.

Let’s add some of these attributes to the <exec> of the Python script that runs our diary program:

<exec executable="${dist.dir}/diary.py"
  dir="${build.dir}"
  inputstring=""
  outputproperty="output"
  logerror="true"
  failonerror="false"
  resultproperty="result">
  <arg value="2007-12-21-12:00" />
  <arg value="Midwinter" />
</exec>
<echo>
output=${output}
result=${result}
</echo>

This target provides the empty string as input, and then logs output to a property that’s echoed afterwards. Setting logerror="true" ensures that any error text still goes to Ant’s output stream. We’ve set failonerror="false" and declared a property for the result with resultproperty. This property will pick up the exit value of the application, which we could feed into another task.

These properties are fairly simple to learn and use. If you want to chain programs and tasks together, save the output to a file or property then pass it into another. Normally, the files and properties are sufficient for managing program I/O. Sometimes, they’re not. In those situations, reach for an I/O Redirector.

6.3.5. Piped I/O with an I/O redirector

The I/O Redirector is the power tool of I/O for those special emergencies. We’re not going to cover it in detail; instead we’ll refer the reader to Ant’s own documentation.

An I/O redirector is a <redirector> element that can be nested inside the <java> and <exec> tasks to set up their entire I/O system. It supports all the I/O-related attributes of the tasks listed in table 6.3 and adds an extra input-related attribute, loginputstring, which stops the input string from being logged by Ant on its way to the program, for extra security. There are also three extra attributes, inputencoding, outputencoding and errorencoding which manage the encoding of the source and destination files.

The real power of the task comes in its nested mapper and filter elements. The <inputmapper>, <outputmapper> and <errormapper> elements let you control the names of the source and destination files. Alongside these are three optional FilterChain elements, elements that let you attach Ant filters to the input and output streams.

This is effectively the same as Unix pipe operations; anyone who is used to typing in commands like "tail -500 log | less" will know the power of piping. Ant’s FilterChain mechanism has the same underlying ability; yet, as a relatively recent addition, it isn’t broadly used. Accordingly, it doesn’t have a broad set of filters to apply. This isn’t too problematic, as one of the filters executes scripts. This lets us post-process the output from a program.

Here, for example, we can use JavaScript to convert the program’s output to uppercase:

<java
   jar="${target.jar}"
   fork="true"
   failonerror="true">
   <arg value="2006-06-21-05:00" />
   <arg value="Summer Solstice" />
   <redirector>
     <outputfilterchain>
       <tokenfilter>
         <scriptfilter language="javascript">
             self.setToken(self.getToken().toUpperCase());
         </scriptfilter>
       </tokenfilter>
     </outputfilterchain>
   </redirector>
 </java>

The result is returned in uppercase:

[java] FILE :CALENDAR.D1
[java] EVENT #E18CA8B7-A68C-45A6-889B-3A881F9EF38F
       - WED JUN 21 06:00:00 BST 2006 - SUMMER SOLSTICE -

This is very powerful. Run Ant with any needed script libraries, and suddenly you can add inline pre- or post-processing to Java or native code. Developers could have some fun there.

You don’t need to write JavaScript to post-process the output if Ant has built-in support for the transformation. There are nearly twenty operations that you can use in a FilterChain—that is, twenty FilterReaders—that can take the output of a program and transform it. These operations can be used for changing the output of an execution or the contents of a file into the form that a follow-on task can use.

6.3.6. FilterChains and FilterReaders

Many common operations perform against files, such as stripping out all comments, searching for lines with a specific value, or expanding Ant properties. These are operations that Ant can perform against files while they’re being moved or loaded, and against the input and output streams of the executed program. We used one in the previous section to run some JavaScript code against the output of our application, but there are other actions that are available in a FilterChain.

A FilterChain is an Ant datatype that contains a list of FilterReaders. Each FilterReader is something that can add, remove, or modify text as it’s fed through the chain. The result is that the chain can transform all text that’s passed through it.

Table 6.4 shows the built-in FilterReaders. They can be used in any task that takes nested FilterChain elements, currently <concat>, <copy>, <move>, <loadfile>, <loadproperties>, <exec>, and <java>.

Table 6.4. Ant’s built-in FilterReaders

FilterReader

Description

<classconstants> Generates "name=value" lines for basic and String datatype constants found in a class file.
<concatfilter> Saves a stream to the beginning or end of a named file.
<deletecharacters> Deletes named characters from the stream.
<escapeunicode> Converts non-ASCII character to a u1234-style declaration.
<expandproperties> Replaces Ant property values.
<filterreader> Provides a custom FilterReader by declaring its classname.
<fixcrlf> Converts the carriage return, line feed and tab content of the data, just as the <fixcrlf> task can do for individual files.
<headfilter> Extracts the first specified number of lines.
<linecontains> Passes through only those lines containing the specified string.
<linecontainsregexp> Passes through only those lines matching specified regular expression(s).
<prefixlines> Prepends a prefix to all lines.
<replacetokens> Replaces tokens on the stream.
<scriptfilter> Passes the text though inline script in a supported language.
<stripjavacomments> Removes Java-style comments.
<striplinebreaks> Removes line breaks, that is and , but different characters can be stripped if desired.
<striplinecomments> Removes lines beginning with a specified set of characters.
<tabstospaces> Replaces tabs with a specified number of spaces.
<tailfilter> Extracts the last specified number of lines.
<tokenfilter> Splits the input stream into tokens (by default, separate lines), and passes the tokens to nested string filters.

One use is stripping all comments from Java source files, which can be done by using the <stripjavacomments> FilterReader within a <copy>:

<copy dir="src" todir="build/src" includes="**/*.java" >
  <filterchain>
    <stripjavacomments />
  </filterchain>
</copy>

After this operation, the copied Java source files will not contain any comments.

The <redirector> element of <java> and <exec> supports three FilterChains:

  • <inputfilterchain> A FilterChain to create data for input to the program
  • <outputfilterchain> A FilterChain to handle the standard output stream
  • <errorfilterchain> A FilterChain to handle the error output stream

These FilterChains can be used to set up data for a program or to clean up the output. Compared to Unix pipes, Ant’s filtering is limited. In particular, there’s currently no way to directly link two or more <exec> or <java> processes.

That’s the end of our coverage of the <java> and <exec> tasks. We’ve looked at running them, passing arguments, handling failure, and now handling I/O. There’s one more execution task of interest, and that’s <apply>, which is a version of <exec> that operates on many files at once.

6.4. Bulk operations with <apply>

What do we do if we want to execute the same program against a number of files in a bulk operation? In a command shell, we’d use a wildcard, such as *.xml. In Ant, we’d want to use its built-in methods to represent groups of files or other resources, such as filesets and other resource collections.

For this special problem of passing a list of files to an external executable, there’s an extension of <exec> called <apply>. This task takes a resource collection and either invokes the application once for every entry in the collection, or passes the entire collection to the application as its arguments.

All the attributes of <exec> can be used with <apply>, with the additional feature of bulk execution. We could use this task to create a .tar file by using the Unix tar program instead of the <tar> task, and so—on Unix only—pick up file permissions from the file system:

<target name="native-tar" depends="build" >
  <property name="native.tar" location="${dist.dir}/native.tar"/>
  <apply executable="tar"
    relative="true"
    parallel="true"
    dir="${build.classes.dir}"
     >
    <arg value="-cf" />
    <arg value="${native.tar}"/>
    <srcfile/>
    <fileset dir="${build.classes.dir}"
      includes="**/*.class"/>
  </apply>
</target>

This <apply> task looks almost like a normal <exec> operation. Apart from the nested fileset listing the parameters, there’s a <srcfile/> element in the argument list to show the task where to put the expanded files in the list of arguments. The parallel attribute declares that we want all files supplied at once, instead of executing the tar program once for every file. Similarly, we set the directory of the task as the base directory of the source code and then the relative attribute to true so that only the relative path to the files is passed down, not the absolute path. This would package up the files with the wrong paths. How did we know which attributes to apply? Trial and error, in this case.

To see the task at work, we run Ant with the -verbose option:

native-tar:
    [apply] Current OS is Linux
    [apply] Executing 'tar' with arguments:
    [apply] '-cf'
    [apply] '/tmp/diary/core/dist/native.tar'
    [apply] 'd1/core/DateOrder.class'
    [apply] 'd1/core/Diagnostics.class'
    [apply] 'd1/core/DiaryMain.class'
    [apply] 'd1/core/Event.class'
    [apply] 'd1/core/Event2.class'
    [apply] 'd1/core/EventList.class'
    [apply] 'd1/core/Events.class'
    [apply] 'd1/core/Validatable.class'
    [apply] 'd1/core/ValidationException.class'
    [apply]
    [apply] The ' characters around the executable and
            arguments are
    [apply] not part of the command.[2]
    [apply] Applied tar to 9 files and 0 directories.

2 Ant says this because too many people submitted bug reports about the quotes. The quotes are printed to stop confusion about its mishandling of spaces/arguments.

There are some other extra options in the task. You can specify a mapper to provide a map from source files to target files. These files will be inserted into the command’s argument list at the position indicated with the <targetfile/> attribute. The presence of a mapper element also turns on the task’s limited dependency logic: a file is included in the list if the destination file is missing or out of date.

You can have some fun with this task. Here’s the declaration to run the javap bytecode disassembler over our source:

<apply executable="javap"
  parallel="true"
  failonerror="true" relative="true" addsourcefile="false">
  <arg value="-classpath" />
  <arg pathref="run.classpath" />
  <arg value="-verbose"/>
  <targetfile/>
  <fileset dir="${build.classes.dir}"
    includes="**/*.class"/>
  <globmapper from="*.class" to="*"/>
</apply>

The javap program needs the name of the class, without any .java or .class suffix. By declaring a mapper from the suffixed source to some imaginary non-suffixed target files, we can use the <targetfile/> attribute to feed the command with the mapped classname. To drop the source file from the argument list, we said addsourcefile="false". We also declared that the program should be run once per file, with parallel="false". The result is that every class file will be disassembled to Java bytecodes, with the output printed to Ant’s log.

There’s no equivalent of <apply> for Java programs, although it’s always been discussed. Developers have to write custom Ant tasks instead. Many of these tasks have made their way back into Ant or became third-party tasks for Ant and, together, have given Ant its power.

And that’s it. That’s how build files can execute programs. It may seem complex, but it’s probably Java’s most widely used and most debugged library at executing programs. We can run native or Java code; redirect input to and from properties, files, and filters; and apply commands in bulk. Now, for the curious, we’re going to look under the hood at the implementation classes.

6.5. How it all works

Let’s take a quick diversion to explore how Ant actually executes programs using <java>, <exec>, and <apply>. This information is useful when trying to understand why things aren’t working as expected—especially when debugging <java> runs—or why setting fork="true" has so many effects.

The <java> task and <exec> tasks are the underpinnings of many Ant tasks, which is why so many Ant tasks take a subset of the two tasks’ options. Indeed, <java> even delegates to <exec> when it sees fit. Having a good idea of how they work is very important when troubleshooting Ant. First, let’s look at the <java> task.

6.5.1. <java>

In-process <java> loads the application in a classloader that exposes the JVM’s own javax. and java. packages but none of Ant’s own classes. A security manager is set up to intercept System.exit() calls; you can use the <permission> element to change the policies of this SecurityManager. Ant then loads the entry point class and calls its static main(String args[]) method with the arguments from the build file. If a timeout was specified, a watchdog thread is started first to terminate the entry point thread if it times out. This is why terminating a non-forked <java> is dangerous: it can leave Ant’s own JVM in a bad state.

When <java> is run with fork="true", Ant sets up the command line for the java command, a command that’s executed by <exec>. This is why <exec> and <java> share so many features. They share the same code underneath to do the heavy lifting. It’s the <exec> task that owns the problem of running applications.

6.5.2. <exec> and <apply>

The <exec> task is the main way that programs are executed in Ant; all tasks that run external programs will use its classes. The task delegates execution to a CommandLauncher. There are subclasses of CommandLauncher for different Java versions and various platforms, including Windows NT, Mac OS, OS/2, and others. They use one of Java’s execution commands, usually Runtime.exec(), and sometimes start a shell script or batch file to handle all the complexity of execution. Separate threads will pump data to and from the running process, while a static ProcessDestroyer instance tracks which processes are currently running. When Ant’s JVM terminates, the ProcessDestroyer tries to shut down all the processes for a clean exit, though it can not be guaranteed.

When a program is spawned, Ant forgets about it completely. This stops Ant’s termination affecting the spawned process, but it also prevents Ant from halting the process or capturing any output.

The <apply> task extends ExecTask (the class behind <exec>) so that it inherits all features of the parent class. The big difference is the extra logic that’s required for setting up the command line—potentially breaking the operation into multiple executions—and handling the output.

Other Ant tasks extend <apply>. An example is the <chmod> task, which applies the native chmod program to all the file resources supplied to it. This demonstrates a core aspect of Ant’s design: the <java>, <exec>, and <apply> tasks are the ways to run Java programs, native programs, and native programs in bulk, be it from a build file or another task.

6.6. Best practices

While it’s simple to call other programs from Ant, it soon gets complicated as you try to produce a robust, portable means of executing external applications as part of the build process.

Java programs are easy to work with, as the classpath specification and JVM options make controlling the execution straightforward. In-JVM execution has a faster startup, but external execution is more trouble-free, which makes it a good choice.

For Java programs to be callable from Ant, they should be well documented. Ideally, they should have a library API as well as a public entry point. The API enables Java programs to use the external program as a set of classes, rather than just as something to run once. This makes migration to a custom task much easier. The programs should let you set the base directory for reading in relative information, or have parameters setting the full paths of any input and output files used.

When calling a Java program, we recommend that you

  • Set the arguments using one <arg> entry per parameter.
  • Use <arg file="filename"> to pass in file parameters.
  • Explicitly state the classpath, rather than rely on Ant’s own classpath.
  • Set failonerror="true" unless you want to ignore failures or capture the result.

Using <exec> to call external applications or glue together commands in the local shell is a more complex undertaking, as you’re vulnerable to all the behavior of the underlying operating system. It’s very hard to write a portable build file that uses native programs. Our recommendations for native programs are very similar to those of the Java recommendations:

  • Set the arguments using one <arg> entry per parameter.
  • Use <arg file="filename"> to pass in file parameters.
  • Set failonerror="true" unless you really want to ignore failures.
  • Test on more than one platform to see what breaks.

Ant isn’t a scripting language. Calling external programs and processing the results through chained input and output files is not its strength. Ant expects tasks to do their own dependency checking and to hide all the low-level details of program invocation from the user. If you find yourself using many <exec> and <java> calls, then maybe you’re working against Ant, rather than with it.

6.7. Summary

The <java> and <exec> tasks let you invoke external Java and native programs from an Ant build; both have many similarities in function and parameters.

The <java> task lets you start any Java program by using the current classpath or, through the <classpath> element, any new classpath. This task is an essential tool in executing newly written software and in integrating existing code with Ant. By default, Java programs run inside the current JVM, which is faster, but the forked version is more controllable and robust. If ever anything doesn’t work under Ant, set fork="true" to see if this fixes the problem.

The <exec> task is the native program equivalent. It gives Ant the ability to integrate with existing code and with existing development tools, though the moment you do so, you sacrifice a lot of portability.

Finally <apply> invokes a native program over supplied filesets. It can be useful for bulk operations, despite its limitations.

All of these programs have common features, such as the ability for a failing program to halt the build. They also share attributes and elements to pass files and properties, or to dynamically generate data to a program and collect the results the same way. This means you can integrate Ant with external Java and native programs to make them part of the build, giving the build file access to everything the Java tools and native commands offer, as well as providing a way to run the programs you build yourself.

Having completed packaging, testing, and running our diary program, the library—limited as it is—is ready for use. What are we going to do? Ship it!

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset