News
Features
Changes
Todo
Contributors

Downloads

Installation
Configuration
Using
Servlet Sample
FAQ

Architecture

Ant integration
Servlet Engines

API Reference

Support
Statistics


Continuous integration

A strong principle of eXtreme Programming (XP) is the continuous integration aspect (see the Continuous Integration article by Martin Fowler). The traditional approach has been to developing the code first, then test it and then integrate it with other appications. The continuous integration principle is to develop code, tests and integrate at the same time, i.e. at any point in time, you have a functioning code along with tests and integrated.

In order to be able to do continuous integration, you need to be able to automatically run the build for your application, including passing the unit tests (based on JUnit, J2EEUnit, HttpUnit or others). Ant is the perfect tool for this task.


Ant benefits

The benefits of using Ant for running unit tests are as follows :

Benefits  
Ant is written in Java and thus the same scripts can be used on several systems (Windows and Unix for example). Is is therefore very well suited for building java applications.  
Ant provides enough built-in and optional tasks to be able to achieve almost any needed build task without needing to use system dependent scripts.  
By being able to very quickly rerun unit test you can verify that your tests still pass when you modify some part of your code ( regression testing)  
As it is automated, it fits well in the continuous integration principle and your tests can thus be ran automatically and continously  

Writing an Ant build script tutorial

The sample Ant build file described below is taken from the J2EEUnit Sample for Servlet API 2.2 project.

Note This section is both a tutorial for automating builds and unit testing with Ant and J2EEUnit and also a best practice and methodology guide for using Ant in general (independent of J2EEUnit). This is an Ant configuration that has been working for me on several project. Of course, you are free to adapt/modify it to suit your needs.

Project directory structure

Create the following directories :

Directory name   Content of the directory  
build   The Ant build file and some other ancillary files (properties file, ...)  
conf   Configuration files for the project. These are the configuration files that will not be put into the runtime jar. They are configuration file that need to be visible and modifyable by a user of the project. If they were bundled in the jar they would be hard to modify. On the other hand, we don't want a proliferation of configuration files, so all technical configuration files (messages to display for errors, ...) should be put in the src directory in correct java packages.  
conf/test   Configuration files needed for testing the project only.  
docs   Project documentation.  
docs/skins   Documentation skin for Stylebook (if using Stylebook).  
docs/xdocs   Documentation files (XML files) for Stylebook (if using Stylebook).  
misc   Other miscellaneous files (for example a JBuilder project, TogetherJ files, ...)  
src   The project sources : java files + java test files + properties files + test properties files + other files for runtime or test (XML files, ...). Note that all test files should begin by 'Test' or 'test' in order to easily separate them from other files so that in an Ant target we'll be able to include only the runtime files.  
web   The project web files : HTML, JSP, ... (if any)  

Note In the sample application provided with J2EEUnit, we don't generate any web site documentation (only javadoc) so we're not using the stylebook-related directories and Ant targets. However we do so in J2EEUnit itself, so if you need to take a look, download the J2EEUnit sources.

Note An out directory will be created by the Ant build. All build-generated files will be put in that directory (compiled classes, generated javadoc documentation, test configuration files for running an application server, ...).

Note We don't have any lib directory because it is always better not to include dependent jars in your project whenever possible for the following reasons : better continuous integration with other libraries (meaning they also evolve and you should test as much as possible with the latest version to discover potential problems early, more lightweight downloads, less jar proliferation (you'll end up with tens of the same jars otherwise), more version control and integration checks (if your project uses 2 external libraries that need another third library but not in the same version you are in trouble !), ...


Ant Target List

Define the following targets in your build.xml.

Note The Type column specify whether the target is an external target that can be called by the user of the build file or if it is an internal target, intended to be called internally by another target inside the build script.

Target name   Description   Type  
init   Defines token filters, timestamp, display some information on screen, ...   Internal  
usage   Display usage information about the targets.   External  
prepare   Set up the output directory where build generated files will be put and copy the java sources to this output directory, replacing tokens with the values defined in the init target.   Internal  
compile   Compile the java sources and put the result in the output directory. All copies non java source files such as .properties files, XML configuration files, ...   Internal  
source   Generate a zipped file containing the full sources of the project (i.e. the whole directory structure, excluding any build generated files).   External  
javadoc   Generates the javadoc of the project. This javadoc will be part of the project documentation, as generated by the doc target.   Internal  
doc   Generates the full project documentation : javadoc + README files + documentation web site (using Stylebook for example).   External  
clean   Remove any build generated files. In particular, delete the output directory.   External  
jar, war, ear, ...   Generate the project runtime. If the project is a framework, it is usually a jar. If the project is a web application, it is usually a war file. If the project is an EJB application, it is usually either one or several jars or an ear file. Etc ...   External  
tests_XXX where XXX is the name of the servlet engine on which the tests run.   Run the J2EEUnit unit tests.   External  

Step 1 : The Ant project basedir

You build.xml file being in located in build/ you should set the project tag basedir attribute to '..' so that all other paths are relative to your project root directory. Indeed, the batch file (shell script) that was used to bootstrap Ant is located in <root>/build, so '..' is the root directory.

Example :

<?xml version="1.0"?>
[...]
<project name="J2EEUnit Sample" default="war" basedir="..">

Step 2 : initialization of project properties

You should define as properties all values that can be factorized and which are used often in your build file such as source locations, output locations, external jar locations and names, ... You can also define useful file patterns there (see the sample below).

A good principle is to defined any properties that depend on your environment (such as external jar locations) in a file (let's call it build.properties located either where build.xml is located or in your home directory. The properties defined in this file will be loaded by build.xml using the following code :

    <!-- Give user a chance to override without editing this file 
         (and without typing -D each time it compiles it) -->
    <property file="build/build.properties" />
    <property file="${user.home}/build.properties" />

Here are our properties initializations :

<project name="J2EEUnit Sample" default="war" basedir="..">

    <!-- Give user a chance to override without editing this file 
         (and without typing -D each time it compiles it) -->
    <property file="build/build.properties" />
    <property file="${user.home}/build.properties" />

    <!-- Generic project properties -->
    <property name="project.fullname" value="J2EEUnit Sample"/>
    <property name="project.version" value="0.9"/>
    <property name="project.name" value="j2eeunit-sample"/>

    <!-- Miscellaneous settings -->
    <property name="year" value="2000-2001"/>
    <property name="debug" value="on"/>
    <property name="optimize" value="off"/>
    <property name="deprecation" value="off"/>

    <!-- 
       ========================================================================
         Set the properties related to the source tree
       ========================================================================
    -->
    <!-- Source locations for the build -->
    <property name="src.dir" value="src"/>
    <property name="src.java.dir" value="${src.dir}/share"/>
    <property name="src.java.servlet.dir" value="${src.dir}/servlet22"/>
    <property name="build.dir" value="build"/>
    <property name="etc.dir" value="${build.dir}/etc"/>
    <property name="lib.dir" value="lib"/>
    <property name="conf.dir" value="conf"/>
    <property name="conf.test.dir" value="conf/test"/>
    <property name="web.dir" value="web"/>

    <!-- 
       ========================================================================
         Set the properties related to the build area
       ========================================================================
    -->
    <!-- Destination locations for the build (relative to the basedir as -->
    <!-- specified in the basedir attribute of the project tag)          -->
    <property name="out.dir" value="out"/>
    <property name="out.lib.dir" value="${out.dir}/lib"/>
    <property name="out.test.dir" value="${out.dir}/test"/>
    <property name="out.src.dir" value="${out.dir}/src"/>
    <property name="out.classes.dir" value="${out.dir}/classes"/>
    <property name="out.doc.dir" value="${out.dir}/doc"/>
    <property name="out.javadoc.dir" value="${out.doc.dir}/javadoc"/>
    <property name="out.conf.dir" value="${out.dir}/conf"/>

    <!-- Names of deliverables -->

    <!-- The J2EEUnit Sample war file. This is the file that should be
         used at runtime by end users (it excludes the test classes) -->
    <property name="final.war.name" value="${out.dir}/${project.name}-22.war"/>

    <!-- The full sources of J2EEUnit Sample in a zip file -->
    <property name="final.src.name" value="${out.dir}/${project.name}-src-22.zip"/>

    <!-- The J2EEUnit Sample documentation in a zip : javadoc + other
         READMEs. -->
    <property name="final.doc.name" value="${out.dir}/${project.name}-doc-22.zip"/>

    <!-- 
       ========================================================================
         Useful file patterns for targets
       ========================================================================
    -->
    <!-- All source files of the projet. These source files will be copied
         to the destination source directory in the prepare task -->
    <patternset id="all.src.files">

        <!-- All java files -->
        <include name="**/*.java"/>

        <!-- All doc files -->
        <include name="**/package.html"/>
        <include name="**/overview.html"/>

        <!-- All conf files (including test files) -->
        <include name="**/*.txt"/>
        <include name="**/*.xml"/>
        <include name="**/*.properties"/>

        <!-- Other misc files -->
        <include name="**/license.gpl"/>

    </patternset>

    <!-- All non java files in the src directory -->
    <patternset id="all.nonjava.files">

        <!-- All conf files (including test files) -->
        <include name="**/*.txt"/>
        <include name="**/*.xml"/>
        <include name="**/*.properties"/>

        <!-- Other misc files -->
        <include name="**/license.gpl"/>

    </patternset>

Step 3 : 'init' target

Useful for initializing a timestamp (DSTAMP, TODAY, TSTAMP), defining token filters, printing some information messages, registering custom Ant tasks, ...

    <!-- 
       ========================================================================
         Initialize the build. Must be called by all targets
       ========================================================================
    -->
    <target name="init">

        <!-- So that we can use the ${TSTAMP}, ${DSTAMP}, ... time stamps
             in targets, if need be -->
        <tstamp/>

        <echo message="------- ${project.fullname} ${project.version} -------"/>
        <echo message=""/>

        <echo message="java.class.path = ${java.class.path}"/>
        <echo message=""/>
        <echo message="java.home = ${java.home}"/>
        <echo message="user.home = ${user.home}"/>

        <!-- Filters -->
        <filter token="version" value="${project.version}"/>
        <filter token="year" value="${year}"/>

        <!-- Initialize custom Ant task needed for running the server tests -->
        <taskdef name="runservertests" classname="j2eeunit.ant.RunServerTestsTask">
            <classpath>
                <pathelement location="${j2eeunit.ant.jar}"/>
                <pathelement path="${java.class.path}"/>
            </classpath>
        </taskdef>

    </target>

Step 4 : 'usage' targets

Display a usage message.

    <!-- 
       ========================================================================
         Help on usage. List available targets
       ========================================================================
    -->
    <target name="usage" depends="init">

        <echo message=""/>
        <echo message="${project.fullname} build file"/>
        <echo message="------------------------------------------------------"/>
        <echo message=""/>
        <echo message=" Available targets are :"/>
        <echo message=""/>
        <echo message=" war    --> generates the war file (default)"/>
        <echo message=" clean  --> cleans up the build directory"/>
        <echo message=" source --> generates source zip of the project"/>
        <echo message=" doc    --> generates the docs (javadoc, ...)"/>
        <echo message=" all    --> do it all at once"/>
        <echo message="            (clean, war, source, doc)"/>
        <echo message=""/>
        <echo message=" Targets for running the tests for Servlet API 2.2 :"/>
        <echo message=""/>
        <echo message=" tests_resin_12    --> run tests for Resin 1.2"/>
        <echo message=" tests_tomcat_32   --> run tests for Tomcat 3.2"/>
        <echo message=" tests_weblogic_51 --> run tests for WebLogic 5.1"/>
        <echo message=" tests_orion_14    --> run tests for Orion 1.4"/>
        <echo message=""/>

    </target>

Step 5 : 'prepare' target

This target is needed for both compiling and generating the javadoc. It copies all the source files from their src/ directory to the out directory, replacing tokens in the source code (replacing the @version@ by the version number for example).

    <!-- 
       ========================================================================
         Prepare the output directory by copying the source files into it
       ========================================================================
    -->
    <target name="prepare" depends="init">

        <mkdir dir="${out.src.dir}"/>

        <!-- Copy all source files to destination dir. Apply the filters in
             order to replace the tokens for the copyright year and the
             version -->
        <copy todir="${out.src.dir}" filtering="on">
            <fileset dir="${src.java.dir}">
                <patternset refid="all.src.files"/>
            </fileset>
            <fileset dir="${src.java.servlet.dir}">
                <patternset refid="all.src.files"/>
            </fileset>
        </copy>

    </target>

Step 6 : 'compile' target

The compile target simply compiles the java files into .class files and also copies the support files that are in src/ to the directory where the class file have been generated.

    <!-- 
       ========================================================================
         Compiles the source directory
       ========================================================================
    -->
    <!-- Preparation target for the compile target -->
    <target name="prepare-compile" depends="prepare">

        <mkdir dir="${out.classes.dir}"/>

    </target>

    <!-- Run the java compilation -->
    <target name="compile" depends="prepare-compile">

        <javac srcdir="${out.src.dir}"
            destdir="${out.classes.dir}"
            debug="${debug}"
            deprecation="${deprecation}"
            optimize="${optimize}">

            <!-- Exclude all files that are not .java source files -->

            <!-- All doc files -->
            <exclude name="**/package.html"/>
            <exclude name="**/overview.html"/>

            <!-- All conf files (including test files) -->
            <exclude name="**/*.txt"/>
            <exclude name="**/*.xml"/>
            <exclude name="**/*.properties"/>

            <!-- Misc files -->
            <exclude name="**/license.gpl"/>

            <classpath>
                <pathelement path="${java.class.path}"/>
                <pathelement location="${servlet.jar}"/>
                <pathelement location="${j2eeunit.jar}"/>
            </classpath>

        </javac>

        <!-- Copies non java files that need to be in the classes directory -->
        <copy todir="${out.classes.dir}">
            <fileset dir="${src.java.dir}">
                <patternset refid="all.nonjava.files"/>
            </fileset>
            <fileset dir="${conf.test.dir}">
                <include name="j2eeunit.properties"/>
            </fileset>
        </copy>

    </target>

Step 7 : 'source' target

Zip up the sources for distribution.

    <!-- 
       ========================================================================
         Generates source zip of the project
       ========================================================================
    -->
    <target name="source" depends="prepare">

        <zip zipfile="${final.src.name}" basedir=".">

            <exclude name="${out.dir}/**"/>
            <exclude name="**/*.log"/>
            <exclude name="**/*.bak"/>
            <exclude name="**/*.class"/>
            <exclude name="${build.dir}/build.properties"/>

        </zip>

    </target>

Step 8 : 'javadoc' target

Generate the project's javadoc.

    <!-- 
       ========================================================================
         Generate the javadoc
       ========================================================================
    -->
    <!-- Preparation target for the javadoc target -->
    <target name="prepare-javadoc" depends="prepare">

        <mkdir dir="${out.javadoc.dir}"/>

    </target>

    <!-- Generate the javadoc for the current Servlet API -->
    <target name="javadoc" depends="prepare-javadoc">

        <javadoc
            sourcepath="${out.src.dir}"
            packagenames="j2eeunit.sample.*"
            destdir="${out.javadoc.dir}"
            author="true"
            public="true"
            version="true"
            use="true"
            windowtitle="${project.fullname} ${project.version} for Servlet 22 API"
            doctitle="${project.fullname} ${project.version} for Servlet 22 API"
            bottom="Copyright &amp;copy; ${year} Vincent Massol.">

            <classpath>
                <pathelement path="${java.class.path}"/>
                <pathelement location="${servlet.jar}"/>
                <pathelement location="${j2eeunit.jar}"/>
            </classpath>

        </javadoc>

    </target>

Step 9 : 'doc' target

Generate the project's documentation. It includes the javadoc, additional README files (if any) and the documentation web site (built using Stylebook for example, as shown in the example below).

Example not using Stylebook :

    <!-- 
       ========================================================================
         Generate the full documentation
       ========================================================================
    -->
    <!-- Preparation target for the doc target -->
    <target name="prepare-doc" depends="javadoc">

        <mkdir dir="${out.doc.dir}"/>

    </target>

    <!-- Generate the documentation -->
    <target name="doc" depends="prepare-doc">

        <!-- Add the README -->
        <copy file="README" tofile="${out.doc.dir}/README"/>

        <!-- Create the zipped documentation -->
        <zip zipfile="${final.doc.name}" basedir="${out.doc.dir}"/>

    </target>

Example using Stylebook (from the J2EEUnit build file itself) :

    <!-- 
       ========================================================================
         Generate the full documentation for a given Servlet API, i.e.
         web site + javadoc + README
       ========================================================================
    -->
    <!-- Preparation target for the doc target -->
    <target name="prepare-doc" depends="javadoc">

        <mkdir dir="${out.doc.dir}"/>

        <!-- Copy doc-book.xml to book.xml for defining the documentation web
             site and replacing token filters (year) -->
        <delete file="${xdoc.dir}/book.xml"/>
        <copy file="${xdoc.dir}/doc-book.xml" tofile="${xdoc.dir}/book.xml"
            filtering="on"/>

        <!-- Copy non xml files -->
        <copy todir="${out.doc.dir}/files">
            <fileset dir="${xdoc.dir}/files"/>
        </copy>

        <!-- Copy the version.txt file -->
        <copy file="${conf.dir}/version.txt"
            tofile="${out.doc.dir}/version.txt" filtering="on"/>

    </target>

    <!-- Generate the documentation -->
    <target name="doc" depends="prepare-doc">

        <!-- Generate the documentation web site -->
        <stylebook book="${xdoc.dir}/book.xml"
            skinDirectory="${skin.dir}/xml.apache.org"
            targetDirectory="${out.doc.dir}">

            <classpath>
                <pathelement path="${java.class.path}"/>
            </classpath>

        </stylebook>
    

        <!-- Add the README -->
        <copy file="README" tofile="${out.doc.dir}/README"/>

        <!-- Create the zipped documentation -->
        <zip zipfile="${final.doc.name}" basedir="${out.doc.dir}"/>

    </target>

Step 10 : 'clean' target

Removes all build generated files.

    <!-- 
       ========================================================================
         Remove all build generated files
       ========================================================================
    -->
    <target name="clean" depends="init">

        <!-- Deletes all files ending with '~' -->
        <delete>
            <fileset dir="." includes="**/*~" defaultexcludes="no"/>
        </delete>

        <!-- Remove the out directory -->
        <delete dir="${out.dir}"/>

        <!-- Delete log files -->
        <delete>
            <fileset dir=".">
                <include name="**/*.log"/>
            </fileset>
        </delete>

    </target>

Step 11 : 'jar', 'war' targets
'jar' target

This target is useful if your project is a framework for example and you need to deliver a jar file. We also include a manifest file in the jar, with version information. We copy the manifest to the output directory in order to replace the @version@ token with it's value.

Example (from the J2EEUnit build file) :

    <!-- 
       ========================================================================
         Create the runtime jar file
       ========================================================================
    -->
    <!-- Preparation target for the jar target -->
    <target name="prepare-jar" depends="compile">

        <mkdir dir="${out.conf.dir}"/>

        <!-- Copy the manifest in order to replace the version token filter -->
        <copy todir="${out.conf.dir}" filtering="on">
            <fileset dir="${conf.dir}" >
                <include name="manifest"/>
            </fileset>
        </copy>

    </target>

    <!-- Generate the jar file -->
    <target name="jar" depends="prepare-jar">

        <jar jarfile="${final.jar.name}" basedir="${out.classes.dir}"
            manifest="${out.conf.dir}/manifest">

            <!-- Do not include test files in the runtime jar -->
            <exclude name="**/Test*.*"/>
            <exclude name="**/test*.*"/>

        </jar>

    </target>

'war' target

This target is useful if you're building a web application. We also include a manifest file in the war, with version information. We copy the manifest to the output directory in order to replace the @version@ token with it's value.

Example (from the J2EEUnit sample) :

    <!-- 
       ========================================================================
         Create the runtime war file
       ========================================================================
    -->
    <!-- Preparation target for the war target -->
    <target name="prepare-war" depends="compile">

        <mkdir dir="${out.conf.dir}"/>

        <!-- Copy the manifest in order to replace the version token filter  -->
        <copy todir="${out.conf.dir}" filtering="on">
            <fileset dir="${conf.dir}" >
                <include name="manifest"/>
            </fileset>
        </copy>

    </target>

    <!-- Generate the war file -->
    <target name="war" depends="prepare-war">

        <war warfile="${final.war.name}"
             webxml="${conf.dir}/web.xml"
             manifest="${out.conf.dir}/manifest">

            <classes dir="${out.classes.dir}">
                <!-- Do not include test files in the runtime jar -->
                <exclude name="**/Test*.*"/>
                <exclude name="**/test*.*"/>

                <!-- Also exclude the test j2eeunit.properties file -->
                <exclude name="j2eeunit.properties"/>
            </classes>

            <fileset dir="${web.dir}">
                <exclude name="test/**"/>
            </fileset>
        </war>

    </target>


Step 12 : 'tests_XXX' target

The tests_XXX target is in charge of running the J2EEUnit unit tests. It must prepare the test environment for a given servlet engine and package the tests, start that servlet engine, run the tests by starting the JUnit runner and stop the servlet engine.

See the tutorial for running tests in a servlet engine for a detailed tutorial on how to do this.




Copyright © 2000-2001 Vincent Massol. All Rights Reserved.