AntAnt

Creates a standard java project structure with accompanying buildfiles.

Version: 1.0.0.rc5


  1. Purpose
  2. Source and Distribution
  3. Creating a Project
    1. Generated Structure
    2. Generated Files
    3. Webapp setup
  4. Using an AntAnt buildfile
    1. Set up your environment
    2. Build Targets
    3. Creating jarfiles
    4. Deployment metadata
    5. Creating Webapps and warfiles
    6. Dependency management and classpaths
    7. JDepend
  5. Roadmap

Purpose

The goals of AntAnt are to:

It's impossible to standardize every aspect of a build without inconveniencing some people or getting into endless arguments about best practices. However 80% of Ant files are the same for every projects (there's a lot of cut and paste in the Ant world) and duplicating the effort for every project gets old fast, as does understanding each and every project's quirks. The build and the project layout used by AntAnt is derived from what seems to work well enough, and from idioms shared by other Java shops, open source projects and outlined in some books.

Most of the tasks in AntAnt can be customized without too much effort. The 20% you need to customize most of the time can be altered via build.xml. Where Ant task tend to become custom is at the deployment end, so typically the things you will need to set up in Ant are:

Source and Distribution

Source code is available from a subversion repository at http://dehora.net/svn/antant/ - releases are in the tags directory. Releases can be also be found at http://dehora.net/code/antant/.

Installation

To run AntAnt you will need Ant 1.6 or greater and have declared an ANT_HOME envar.

Cd to where you want AntAnt installed and check it out from Subversion.


  E:\home>cd e:\java

  E:\java>svn co http://dehora.net/svn/antant/tags/antant/1.0.0 antant-1.0.0
  A  antant-1.0.0\antant.xml
  A  antant-1.0.0\antant
  A  antant-1.0.0\doc
  A  antant-1.0.0\doc\layout.png
  A  antant-1.0.0\doc\readme.html
  A  antant-1.0.0\lib
  A  antant-1.0.0\lib\antant-ext.zip
  A  antant-1.0.0\antant.bat
  A  antant-1.0.0\README.txt
  A  antant-1.0.0\template
  A  antant-1.0.0\template\ant
  A  antant-1.0.0\template\ant\import-war.xml
  A  antant-1.0.0\template\ant\import-base.xml
  A  antant-1.0.0\template\ant\tomcat.properties
  A  antant-1.0.0\template\ant\jdepend.xml
  A  antant-1.0.0\template\ant\get-ant-ext.xml
  A  antant-1.0.0\template\ant\import-tomcat.xml
  A  antant-1.0.0\template\run.xml
  A  antant-1.0.0\template\ivy.xml
  A  antant-1.0.0\template\src
  A  antant-1.0.0\template\src\java
  A  antant-1.0.0\template\src\java\META-INF
  A  antant-1.0.0\template\src\java\META-INF\ejb-jar.xml
  A  antant-1.0.0\template\src\java\WEB-INF
  A  antant-1.0.0\template\src\java\WEB-INF\web.xml
  A  antant-1.0.0\template\build.properties
  A  antant-1.0.0\template\ivyconf.xml
  A  antant-1.0.0\template\build.xml
  Checked out revision 6.

  E:\java>

AntAnt also requires some Ant extensions to be available on the classpath. The handiest place to put these is in Ant's lib folder. Run the setup target to perform the install:


  E:\java>cd antant-1.0.0

  E:\java\antant-1.0.0>antant setup
  Buildfile: E:\java\antant-1.0.0\antant.xml

  setup:
        [get] Getting: http://dehora.net/code/antant-ext.zip
        [get] To: E:\java\apache-ant-1.6.5\antant-ext.zip
        [get] ...................
        [get] last modified = Thu Sep 08 01:37:25 BST 2005
      [unzip] Expanding: E:\java\apache-ant-1.6.5\antant-ext.zip into E:\java\apache-ant-1.6.5\lib

  BUILD SUCCESSFUL
  Total time: 14 seconds
  E:\java\antant-1.0.0>

Now add the antant folder to your Path. To tests antant is available, run the version target:


  E:\home>antant version
  Buildfile: E:\java\antant\antant.xml

  version:

       [echo] AntAnt: version 1.0.0


  BUILD SUCCESSFUL
  Total time: 0 seconds
  E:\home>

That's it. Let's create a project.

Creating a Project

To create a project call the 'create' target. You'll be asked the following questions about your project:


    E:\home>antant
    Buildfile: E:\java\antant\antant.xml

    create:

         [echo] AntAnt: version 1.0.0.rc4

        [input] Please enter the project location (an absolute path):

        [input] Please enter project name (this will be the project directory):
    Terminate batch job (Y/N)? y

    E:\home>antant create
    Buildfile: E:\java\antant\antant.xml

    create:

         [echo] AntAnt: version 1.0.0.rc3

        [input] Please enter the project location (an absolute path):
    e:/home

        [input] Please enter project name (this will be the project directory):
    world-domination

        [input] Please enter project/package org name (this will be used in jar and dist names):
    evilinc

        [input] Do you want a web.xml file? (y,n)
    y

        [input] Do you want an SQL DDL area? (y,n)
    y

        [mkdir] Created dir: E:\home\world-domination
        [mkdir] Created dir: E:\home\world-domination\ant
        [mkdir] Created dir: E:\home\world-domination\bin
        [mkdir] Created dir: E:\home\world-domination\build
        [mkdir] Created dir: E:\home\world-domination\conf
        [mkdir] Created dir: E:\home\world-domination\docs
        [mkdir] Created dir: E:\home\world-domination\src
        [mkdir] Created dir: E:\home\world-domination\src\main
        [mkdir] Created dir: E:\home\world-domination\src\main\java
        [mkdir] Created dir: E:\home\world-domination\src\test\java
        [mkdir] Created dir: E:\home\world-domination\lib
         [copy] Copying 1 file to E:\home\world-domination
         [copy] Copying 1 file to E:\home\world-domination
         [copy] Copying 1 file to E:\home\world-domination
         [copy] Copying 1 file to E:\home\world-domination\ant
         [copy] Copying 1 file to E:\home\world-domination
         [copy] Copying 4 files to E:\home\world-domination

    make.web-inf:
         [echo] making WEB-INF in e:/home/world-domination/src/main/webapp/WEB-INF

         [echo] ** WARNING: to use tomcat tasks, add the line  to build.xml's imports
         [echo] ** WARNING: to use war tasks, add the line  to build.xml's imports

        [mkdir] Created dir: E:\home\world-domination\src\main\webapp
        [mkdir] Created dir: E:\home\world-domination\src\main\webapp\images
        [mkdir] Created dir: E:\home\world-domination\src\main\webapp\style
        [mkdir] Created dir: E:\home\world-domination\src\main\webapp\script
        [mkdir] Created dir: E:\home\world-domination\src\main\webapp\WEB-INF
        [mkdir] Created dir: E:\home\world-domination\src\main\webapp\WEB-INF\layout
        [mkdir] Created dir: E:\home\world-domination\src\main\webapp\WEB-INF\pages
         [copy] Copying 1 file to E:\home\world-domination\ant
         [copy] Copying 1 file to E:\home\world-domination\ant
         [copy] Copying 1 file to E:\home\world-domination\src\main\webapp\WEB-INF

    make.ddl.dir:
         [echo] making src/main/ddl
        [mkdir] Created dir: E:\home\world-domination\src\main\ddl

         [echo] Project 'world-domination' created in e:/home


    BUILD SUCCESSFUL
    Total time: 22 seconds
    E:\home>

Produces the following layout:


Generated Structure

The AntAnt project structure:

Generated Files

AntAnt generates a number of files:

Webapp setup

If your project has a web application and you want to use Tomcat and War tasks, you need to have build.xml import the tomcat and war files. Change the generated build.xml to look as follows


  <!-- ===============================================================
  Imports
  ==================================================================== -->
  <import file="ant/import-base.xml"/>

  <import file="ant/import-tomcat.xml"/>
  <import file="ant/import-war.xml"/>


Using an AntAnt buildfile

Set up your environment

To run the created buildfile, you will need:

Build Targets

Most of the time you'll only need to use these targets:

    clean            clean compile targets
    compile          build the code and copy over resources
    doc              generate the javadoc
    jar              Generate the jar file(s)
    test             run the junit tests
    war              build the war file
To see all the tasks run 'ant -projecthelp'

    E:\temp\world-domination>ant -projecthelp
    Buildfile: build.xml

    Main targets:

     antant.version      print the AntAnt version used to create this build
     clean               clean compile targets
     clean-dist          blow away the last dist creation
     compile             build the code and copy over resources
     cp.build            print the build classpath
     cp.test             print the test classpath
     dist                create a binary distribution
     dist-src            prepare a source distribution
     doc                 generate the javadoc
     global-publish-jar  build and install the jars to the global lib dir
     ivy.configure       setup Ivy with ivyconf.xml
     ivy.report          generates a report of dependencies with Ivy
     ivy.resolve         retrieve dependencies with Ivy
     jar                 build all the jar packages
     setup               Download global jar extensions for Ant builds
     test                run the junit tests
     test-report         Executes the unit tests and produce the report
    Default target: test

build.properties

The build.properties file can be used to overrride any of the properties, and has the following fields declared by default:


    # ------------------------------------------------------------------------------
    # Project info
    # ------------------------------------------------------------------------------
    release.version=0.0.0
    project.name=world-domination
    project.org=evilinc

    # ------------------------------------------------------------------------------
    # Packaging,:
    # ------------------------------------------------------------------------------
    jar.name=${project.org}-${project.name}

    # ------------------------------------------------------------------------------
    # If you want to override a property, do it here:
    # ------------------------------------------------------------------------------

Creating jarfiles

AntAnt uses the macrodef feature of Ant to create jarfiles. Here's the jar task unchanged from a generated build file:


  <!-- ====================================================================
    Custom packaging: add macro-jar calls into the jar target
  ==================================================================== -->
  <patternset id="jar.exclusion.set">
  <!-- example: <exclude name="org/example/stuff/service/beans/**"/> -->
  </patternset>
  <patternset id="jar.inclusion.set">
    <include name="**/**"/>
  </patternset>

  <target name="jar" depends="clean, compile" description="build all the jar packages">
    <macro-jar
      jarname="${jar.name}"
      incl="jar.inclusion.set"
      excl="jar.exclusion.set" />
  </target>


The inclusion and exclusion patternsets are used to define what classes are included or excluded from the jar. To produce multiple jars from a single project you will need to add multiple macro-jar calls inside the jar target. For example the deps/service project creates a service and beans jar from a single project as follows:


  <patternset id="jar.exclusion.set">
    <exclude name="org/example/stuff/service/beans/**"/>
  </patternset>
  <patternset id="jar.inclusion.set">
    <include name="**/**"/>
  </patternset>

  <patternset id="jar.beans.exclusion.set">
  </patternset>
  <patternset id="jar.beans.inclusion.set">
    <include name="org/example/stuff/service/beans/**"/>
  </patternset>

  <target name="jar" depends="clean, compile" description="build all the jar packages">
    <macro-jar
      jarname="${jar.beans.name}"
      incl="jar.beans.inclusion.set"
      excl="jar.beans.exclusion.set" />
    <macro-jar
      jarname="${jar.name}"
      incl="jar.inclusion.set"
      excl="jar.exclusion.set" />
  </target>


Deployment metadata

The jar/war tasks insert versioning data into the Manifest.mf file. This allows an association between version control tags and deployed code. The version number is taken from 'release.version' property in build.properties. Here's an example:


  Manifest-Version: 1.0
  Ant-Version: Apache Ant 1.6.1
  Created-By: 1.4.2_03-b02 (Sun Microsystems Inc.)
  Built-By: dehora
  Sealed: false
  Specification-Title: WorldDomination
  Specification-Version: 1.0.0
  Specification-Vendor: evilinc
  Implementation-Title: WorldDomination
  Implementation-Version: 1.0.0
  Implementation-Vendor: evilinc


Creating Webapps and warfiles

Web applications tend to need custom setups, so all the war tasks are in a separate import file, ant/import-war.xml. The war specific targets can be used to create, deploy wars or update static files; they assume a certain layout based on the folder layout created during setup:


  ./src
    /webapp
      /images  (gifs, pngs)
      /script  (javascript, xslt
      /style   (css sheets)
      /WEB-INF
           /web.xml
           /layout  (templates etc)
           /pages   (jsps etc)


I've used this layout on a few projects and it works fine. If you don't want this layout
you will have to edit the tasks and properties in ant/import-war.xml. 

The following web related tasks are available:


  deploy-unwar          deploy webapp as unpacked to the servlet container
  deploy-war            deploy the war to the servlet container
  rebuild-webapp        create an unpacked webapp, deploy it and restart tomcat
  war                   build the war file
  deploy-webapp-static  fast deploy, no jars or classes, good for jsp work



Some people will find setting up their IDE for servlet projects will result in
faster copy/reload cycles for webapp development. For that advantage you'll  trade
some dissonance with the production application structure and the fact that
your IDE setup, classpath, etc is unique to you. The advantage of these tasks is that they'll
mirror the actual deployment process and classpath in a production environment.

When you ask for a webapp structure, AntAnt will copy over Tomcat Ant tasks to your project's ant folder. The access control details can be specified in ant/tomcat.properties; see the Tomcat documentation for general info on how to use these tasks. The project.servletcontainer.deploy property can be overridden to specify a local Tomcat area; its default value is ${third.party.location}/jakarta-tomcat/webapps.

Dependency management and classpaths

There seems to be two schools of thought on managing third party jar dependencies in Java projects:

  • keeping jar files in source control is evil
  • keeping jar files in source control is good
AntAnt manages jars using Ivy. Ivy provides transitive dependency management and the ability to obtain jar files from the filesystem or from a web repository (or both). By default Ivy will copy jars into the lib folder, where the classpath declarations set up by AntAnt are programmed to look. Ivy is configurable through the ivyconf.xml.

Once Ivy is configured, the Ant build can find its jars in two places:

  • ${depend.location} - This is /lib by default.
  • ${global.depend.location} . This is ../lib by default (ie where WorldDomination is a sub-project of GalacticDomination) and can be added to the classpath with a pathelement addition to the classpath in build.xml

AntAnt has 2 classpath declarations in build.xml

  • ${classpath} - this inherits from base.classpath, which is in import-base.xml. It points to ant extensions and jars in the lib folder.
  • ${test.classpath} this inherits from classpath. It adds the build/classes folder and test/docs. The latter is handy if you want to load files in a path-neutral way.

Setting up a filesystem repository

Sometimes you won't be able to use an online repositories to grab a jar file. Either it's not there or the url is misconfigured so that Ivy can't locate the file. Or perhaps you just want to keep jars locally. Then you'll need a file system based repository. This can be located anywhere, but by default AntAnt has a filesystem repository location preconfigured. To understand this we'll need to look at the ivyconf.xml file:


    <ivyconf>
      <conf defaultResolver="default" checkUpToDate="true"/>
      <resolvers>
        <chain name="default">
          <filesystem name="global.depend">
            <artifact pattern="${global.depend.location}/[organisation]/[artifact]-[revision].[ext]" />
          </filesystem>
          <ivyrep name="ivyrep"/>
          <ibiblio name="ibiblio" />
        </chain>
      </resolvers>
    </ivyconf>
The main bit is the 'filesystem' element. Here it says there is a filesystem repository called global.depend, which has a path pattern (layout) as follows:

    ${global.depend.location}/[organisation]/[artifact]-[revision].[ext]
Ivy (and Maven) repositories can be organised according to a standard layout. ${global.depend.location} is an ant expression and by default expands to "../lib" in AntAnt. You can change this in build.xml, but this is a useful default location if your AntAnt project is one of many subprojects - they can all depend on a relative lib folder at the superprojects' top level. The rest of the path /[organisation]/[artifact]-[revision].[ext] is saying the lib folders are organised by organisation folders (ie 'mysql') and the jarfile names will be project-revision.ext, (ie "mysql-connector-java-3.1.11.jar"). New jars can now be declared in ivy.xml inside the 'dependencies' element something like this:

  <dependency org="mysql" name="mysql-connector-java" rev="3.1.11" />
Note how the 'org', 'name' and 'rev' attributes map onto the path structure declared in ivyconf.xml. If you had a project layout like this

   ./super-project
       /test-project
          build.xml
          ivy.xml
          ivyconf.xml
          /ant
          /bin
          /conf
          /bin        
          /docs 
          /lib     
You could add a top level lib folder as follows:

   ./super-project
       /test-project
          build.xml
          ivy.xml
          ivyconf.xml     
          /ant
          /bin
          /conf
          /bin        
          /docs 
          /lib     
   ./lib
      /mysql
        /mysql-connector-java-3.1.11.jar
and if your ivy.xml and ivyconf.xml files are setup as above, Ivy will locate the mysql jar from the filesystem.

JDepend

JDepend traverses Java class file directories and generates design quality metrics for each Java package. JDepend allows you to automatically measure extensibility, reusability, and maintainability aspacts of an object design and to manage package dependencies. The JDepend task in ant/jdepend.xml produces a html report for the project. Full documentation: http://www.clarkware.com/software/JDepend.html

To use JDepend, you need to import the jdepend tasks. Change build.xml to look as follows


    <!-- ===============================================================
    Imports
    ==================================================================== -->
    <import file="ant/import-base.xml"/>

    <import file="ant/import-jdepend.xml"/>


Roadmap

I've been using AntAnt in one form or another since 2001. AntAnt is not meant to be a framework and there are no plans to make it so. It's a stunt work - something that should be small enough to explained in an hour and after that hour something you should feel comfortable changing for your own needs.

Therefore I don't have new features planned, but if you find bugs mail me.