16.4 Building Your Struts Applications with Ant

Although there are several different mechanisms to compile and deploy your Struts application, by far the most popular and most flexible is the Ant build tool.[3] This section discusses how to use Ant to perform the necessary tasks for compiling and packaging your application.

[3] Ant is an acronym for "Another Neat Tool."

16.4.1 What Is Ant?

Ant is a platform-independent build tool that can be configured to compile your Java source code files, build your deployment JAR and WAR files, unit-test your code, and create your project's JavaDoc documentation. It also has many other uses and can be expanded to perform new tasks of your own creation.

Ant is similar to the Unix make utility (also known as gmake in Linux and nmake in DOS/Windows). make utilities have been used for generations to manage projects for languages such as C and C++, but these utilities are platform-dependent because the rules they use are typically shell commands executed by the host operating system. Unlike make, Ant's rules (or tasks, in Ant terminology) are Java classes and can be run on any platform with a supported JVM.

A Brief History of Ant

Ant is a Jakarta project created in early 1998 by James Duncan Davidson, who also wrote the original Tomcat servlet container. Initially, Ant was written as a utility to build Tomcat. Others quickly saw the benefits of Ant over make, and it was moved to its own CVS project and officially became a separate Jakarta project in 2000.

16.4.2 Installing and Configuring Ant

You can download Ant from the Jakarta web site at http://jakarta.apache.org/ant/. The examples provided in this chapter were tested with Ant 1.4, but they should be compatible with at least Ant 1.3. Download the binary zip file (for Windows) or the .tar.gz file (for Unix) and uncompress the archive into your desired installation directory. You should also download the 1.4.1 optional.jar file and install it in the ANT_HOME/lib directory. While it's not used in this project, the optional.jar file has many extra tasks that you may find useful in the future.

Ensure that the ANT_HOME/bin directory is added to your system PATH. Your installation may also require you to add the ANT_HOME environment variable, which should be set to the Ant installation directory. The Ant binary typically can determine what ANT_HOME should be, but if you get an error when trying to run Ant, set this environment variable. There is also a caveat when running Ant under Windows 95/98—do not install it in a directory with a long pathname, because the batch file used to run the installation script may not be able to handle the pathname. See the Ant installation documentation for more information.

16.4.3 Getting Started

Ant reads its build commands from an XML file. By default, it looks for a file called build.xml, but you can give the file any name by using the -buildfile <file> option when running Ant. From a command prompt, change directories to the base project directory, which in our example is called storefront. In here, you should see the build.xml file.

The Ant build file consists of a project that has zero or more targets, each of which consists of zero or more tasks. The project element is defined at the top of the build file:

<project name="storefront" default="war" basedir=".">

The project is named storefront and the default target to execute is the war target. The default target is what gets executed if you type ant at the command line without specifying a target. Because the project root directory is the same directory that the build.xml file is located in, "." is used to indicate the base directory property.

The build directory structure for the Storefront application is shown in Figure 16-3.

Figure 16-3. The build structure for the Storefront application

figs/jstr_1603.gif

A target takes the form:

<target name="dostuff">
  <task1 param1="value1" param2="value2"/>
  <task2 param="value"/>
  ...
</target>

A target must have a name and may have several additional attributes that determine when and if the target actually gets executed. The target should contain zero or more tasks.

A task is an atomic unit of work in the Ant world. Each task is bound to a Java class file that Ant executes, passing to it any arguments or subelements defined with that task. The Ant tool is extensible and allows you to create your own tasks. For this book and the Storefront example, the built-in tasks included with the Ant distribution are all you will need. If, however, you needed to create a new task, you could do so by defining the task in the build.xml file, using the taskdef task to bind the task name to a Java class file. The Java class file must extend org.apache.tools.ant.Task and be located in the Ant classpath. There are several other requirements that are beyond the scope of this book, but details can be found in the Ant documentation.

Before you run Ant, you may have to change a few properties in the Storefront build.xml file to suit your development environment:

<property name="webserver.home"
       value="c:/tomcat"/>
<property name="webserver.deploy" 
       value="${webserver.home}/webapps"/>
<property name="servlet.jar"
       value="${webserver.home}/common/lib/servlet.jar"/>

These three properties define where the servlet container is located, where its deployment directory is located, and where it keeps the servlet API classes. First, the webserver.home property is set to the root directory of the servlet container. In this case, the Tomcat 4.0 web server and servlet container are being used. The Storefront build.xml file supports several other containers, but they are commented out; you just need to uncomment the one that you want to use. Tomcat's deployment directory is the webapps directory found just under the Tomcat root directory. Tomcat keeps the servlet API classes in the common/lib/servlet.jar relative to the Tomcat root directory.

Lastly, we need to define the classpath that will be used during compilation of our project. Ant allows us to associate a set of files with a property name. In the following build.xml fragment, the list of all the JAR files necessary to compile the Storefront example is bound to the build.classpath property:

<path id="build.classpath">
  <pathelement location="${servlet.jar}"/>
  <pathelement location="${lib.dir}/commons-beanutils.jar"/>
  <pathelement location="${lib.dir}/commons-collections.jar"/>
  <pathelement location="${lib.dir}/commons-dbcp.jar"/>
  <pathelement location="${lib.dir}/commons-digester.jar"/>
  <pathelement location="${lib.dir}/commons-logging.jar"/>
  <pathelement location="${lib.dir}/commons-pool.jar"/>
  <pathelement location="${lib.dir}/commons-services.jar"/>
  <pathelement location="${lib.dir}/commons-validator.jar"/>
  <pathelement location="${lib.dir}/jdbc2_0-stdext.jar"/>
  <pathelement location="${lib.dir}/log4j.jar"/>
  <pathelement location="${lib.dir}/poolman.jar"/>
  <pathelement location="${lib.dir}/struts.jar"/>
  <pathelement location="${lib.dir}/tiles.jar"/>
  <pathelement path="${build.dir}"/>
</path>

We could have used include elements to define the build.classpath property in far fewer lines; however, it's much clearer to explicitly list each JAR file used during the build process so that nothing that might prevent a successful build is added or omitted.

Dereferencing the property name using the Ant syntax ${property} allows the tasks to use the build.classpath property.

16.4.4 Compiling Java Source Files

The Java source files for the Storefront application are compiled using the Ant javac task. The compiling target, compile, depends on the prepare target:

<target name="compile" depends="prepare">
  <javac destdir="${build.dir}" deprecation="on">
    <classpath refid="build.classpath"/>
      <src path="${src.dir}"/>
  </javac>
</target>

A target may depend on zero or more other targets, using the following syntax:

<target name="final-jar" depends="jars, wars">

Specifying the depends attribute allows you to control the order in which Ant targets are executed. In this case, the compile target is not executed until the prepare target has been executed:

<target name="prepare">
  <tstamp/>
  <mkdir dir="${build.dir}"/>
  <mkdir dir="${dist.dir}/lib"/>
</target>

The prepare target generates timestamp values that can be turned into properties and attached to compilation products such as JAR and WAR files. For this small project, however, timestamps are not used. The prepare target also creates the necessary output subdirectories for our Java classes and WAR file.

The compile target instructs Ant to run the javac compiler on all the files within the source directory and send all the class files to the build directory. The deprecation option is on, so you'll get a detailed message if you accidentally include a deprecated method in one of the source files:

<target name="compile" depends="prepare">
  <javac destdir="${build.dir}" deprecation="on">
    <classpath refid="build.classpath"/>
      <src path="${src.dir}"/>
  </javac>
</target>

The javac task uses the build.classpath property shown in the previous section.

16.4.5 Using Ant to Build the WAR File

The Ant war task builds the web archive. The war target used to build the web archive is shown here:

<target name="war" depends="compile">
  <echo>building war...</echo>
  <war warfile="${dist.dir}/lib/storefront.war" 
       webxml="${web.dir}/WEB-INF/web.xml">
    <fileset dir="${web.dir}"/>
    <classes dir="${build.dir}"/>
    <classes dir="${lib.dir}">
      <include name="*.properties"/>
      <include name="poolman.xml"/>
    </classes>
    <lib dir="${lib.dir}">
      <include name="*.jar"/>
    </lib>
  </war>
</target>

As mentioned previously, the war target is the default target for the project. This means that when Ant is run from the command line without a target argument, the war target is executed. The war target runs only if the compile target has been run first. The war task requires you to define the name of the WAR file and the location of the web.xml file. All the other attributes are optional; if you are interested in seeing them, they are listed in the online Ant documentation (http://jakarta.apache.org/ant/manual/CoreTasks/war.html). Another great reference on Ant is Ant:TheDefinitiveGuide by Jesse Tilly and Eric Burke (O'Reilly).

The nested elements tell the war task where the contents of the WAR file are located. The fileset element defines the base web content of the WAR file. This element is used to declare where the HTML files, JSP pages, images, and so on are located. The classes element points to the Java class files that should be included in the WEB-INF/classes directory in the WAR file, and the lib element declares which files should be included in the WEB-INF/lib folder.

In the Storefront example, everything in the web subdirectory is included. The various subdirectories contain all of the necessary resources (HTML, JSP pages, images, etc.). All of the compiled classes in the build subdirectory are copied into the WAR file's WEB-INF/classes directory along with the properties files. All of the third-party JARs in the lib subdirectory are copied into the WAR's WEB-INF/lib directory. If the lib subdirectory contains any JARs that you don't want to be included, you can use the following snippet:

<lib dir="${lib.dir}">
  <include name="*.jar"/>
  <exclude name="dont_need.jar"/>
</lib>

Here, all the JARs in the lib directory except dont_need.jar will be copied into the WAR file's WEB-INF/lib directory.

The last and often the clearest option is to explicitly include each desired JAR file. While slightly more verbose, this method is immune to changes to the lib folder if other developers in the project start adding JARs indiscriminately. It is also much easier to see exactly what is going to be included in the WAR file.

16.4.6 Cleaning Up

The final two targets are trivial but important. The clean target deletes the build directory, thus removing all of the Java class files:

<target name="clean">
  <delete dir="${build.dir}"/>
</target>
 
<target name="distclean">
  <antcall target="clean"/>
  <delete dir="${dist.dir}"/>
</target>

The distclean target reverts the project back to its pristine "distribution" state. That is, all build products—class files and the WAR file—are removed, so the project directory looks the same as it did when the project directory tree was first installed.

Note that the distclean target calls the clean target. While this isn't really necessary for this small project, it demonstrates more of the power of Ant by invoking another target via the antcall task. The antcall task can even call a target with arguments, but that is beyond the scope of this book.

There are various plug-ins that you can download that allow you to use Ant inside of your specific IDE. For example, the AntRunner plug-in allows Ant to be used within the JBuilder IDE. For this and other plug-ins, see the external tools section of the Ant site at http://jakarta.apache.org/ant/external.html.