MobileJava MobileBuilding with Ant: Introduction

Building with Ant: Introduction

Developing Web Applications using Java Servlets, JavaServer Pages, and other
J2EE technologies is fun! However, the more interesting your application becomes,
the more complicated your build process. In this article, I will sketch out a
framework for how to use simple tools (like Ant and JUnit) and a simple
directory structure to avoid many of the common growing pains of application
development.

Conclusion

I hate articles that make you wade through mountains of verbosity before
telling you what you need to know, so I’ll summarize the main points of this
article right up front:



  • Use Ant (duh).
  • Use a sensible directory.
  • Keep your build directory totally separate from your source directory.
  • Store all JAR files in a common lib directory, and copy or refer to them
    as needed.
  • Plan for multiple web.xml files (they may need to change if you want to
    deploy on more than one server).
  • Make an ant build target for Unit Testing, and use it as (or from) your
    default target.

Ant and Its Targets


Ant is a great open-source
development tool from the Jakarta Project. If you haven’t already, you should go
download it from http://jakarta.apache.org/ant/, follow the install
instructions
for your platform, and look at the manual. I strongly
recommend putting the ant executable on your PATH, so you can just run
“ant” from the command line. Recent versions of ant make this as easy as setting
a few environment variables.


By default, ant looks for a build file named build.xml in the
current directory. This build file follows a very simple structure. Each
<project> contains one or more <target>s; each target can depend on
zero or more other targets. When you run ant, you specify which target you want
ant to build; ant then runs all the targets that one depends on first, then runs
the target you asked for.


In this article, I will discuss a basic structure for a project, which has
evolved over several years of developing web applications. This structure is not
meant to solve everyone’s project management problems, but it is simple enough
to follow that I hope people starting out in web application development can use
it as a starting point, then adapt it to their own needs.


Since I hate canned examples, I will refer to the source for my latest small
open-source project, the Instant Runoff
Poll
application. Full source code, and a running installation, is available
at the Web site http://www.purpletech.com/irv/. The
build.xml file, excerpted throughout this article, is at http://www.purpletech.com/irv/dev/irv/build.xml


Intellectual Properties


Properties (that is, variables) are a very important part of Ant. Generally,
the important strings — directory names, file names, the classpath — are
declared as <property> elements. This allows you to easily change their
value later on, even if you refer to them in several places. It also allows you
to easily override their values, using (a) -Dfoo=bar on the command line,
(b) the <ant> task, calling one ant file from another one, or (c)
<property file=”…”>.


The <property file=”…”> construct is usually used to include a file
called build.properties. This is a convention used so that you can
override default values for your local build environment, without having to
modify build.xml (which may, after all, be a shared file, under version
control). This means that build.properties should probably not
be under version control (add it to .cvsignore under CVS).


Property precedence in Ant is a little upside down. The rule is, the
first line that sets a property wins. That means that a value on the
command line comes first, then a value set in a whatever build.xml file called
this one, then the first value set in this file. (This means that, to be
effective, build.properties must be included very early in the script.)
To illustrate, the following ant script:


  <property name=”foo” value=”from build.xml”/>
<property file=”build.properties”/>
<property name=”bar” value=”from build.xml”/>
<property name=”foo” value=”a different value”/>
<echo message=”foo=${foo} bar=${bar}”/>

with the following content inside build.properties:



foo=from build.properties
bar=from build.properties

will output the following:


foo=from build.xml bar=from build.properties

Note that you can declare <property> elements directly as children of
the <project> (although I prefer to set them inside the “init” task). Also
note that using <property name=”…” location=”…”/> tells ant
to expand the file/directory names to absolute paths, which can help resolve
some possible confusions. (Be careful to go back to
<property name=”…” value=”…”/> for non-file properties!)


init target


This is where it all starts. Here I prefer to set up all the properties used
by other targets, as well as creating the build directory structure, so there
are no surprises later on. All other targets depend on “init,” which means that
“init” will always be run first. (See also the section “Intellectual Properties”.)


clean target


This target removes all compiled files and starts fresh. It is very important
to have a reliable clean target. Often, a dirty build will succeed when
a clean build would fail. Therefore, it is good practice to always run “ant
clean” before committing changes into version control. However, since its
purpose is to delete files, you must be very careful not to make it too
complicated — the more complicated, the greater chance that you will end up
accidentally deleting important source files.


compile target


I prefer to have a separate target that runs <javac> (and sometimes,
ANTLR or other pre-compilation tools). This attitude — keep the
targets as simple as possible — makes your build file more modular, which in
turn makes it easier to create new targets.


Contrast this with a build script in which I had a single target that runs
both javac and javadoc. Then, if I wanted a “test” target that depends on this
javac-and-javadoc target, it would take longer to run, since it would have to
wait for the javadoc to complete.


test target


Unit Testing is the best thing to happen to software development since Emacs. While a full discussion of unit
testing is beyond the scope of this article, I will say this: You should write a
thorough set of unit tests, and you should run them every single time you
compile. This way, if you make a change that causes a test to fail, you find out
immediately, while the change is still fresh in your mind. This
greatly improves your chances of fixing the bug quickly and completely.


My technique for accomplishing this is to simply define a target called
“test,” and either make it the default target, and/or make most other targets
depend on it. In the current example, the default target “webapp” depends on
“test,” and “test” depends on “compile.” That means that when I run “ant” from
the command line, it first compiles the source files, then tests them, then
assembles them into a webapp.


Download JUnit to get started with Unit Testing. Once you include junit.jar in your project, you can either run your
unit tests through the <java> task (the easy way — see the “javatest”
target), or through the optional <junit> task.


To enable the <junit> task, you must do a few steps:


  1. Download jakarta-ant-[version]-optional.jar from
    jakarta.apache.org, and put it into ANT_HOME/lib
  2. Also put junit.jar into ANT_HOME/lib
  3. In your build.xml file, if you want a failed test to stop the build, you
    must do a circuitous runaround involving the failureproperty
    attribute and a separate target whose only point is to fail, which is only
    failureproperty. (This last step should be unnecessary with the next
    release of Ant, which adds an if attribute to the fail
    task.)


javadoc, dist target


Again, following the strategy of keeping targets simple, the javadoc
target simply runs javadoc on the source tree, and the dist
target simply creates the distribution tarball.


webapp target


In the webapp target we bring it all together. The Servlet and EJB
Specs define a Web Application as a collection of related files; JARs, .class,
HTML, JSP, and other file types must be placed together in precise
relationships. In executing this target, the ant tool will gather together all
the various source, object, and documentation files and assemble them into a
working Web application inside the build directory.


One advantage to having an expanded webapp directory on disk is that you can
point to it from your local servlet container, providing an immediate testbed,
especially with containers that support reloading.


This Means WAR


Once the Web Application has been built into the build/webapp, the
war target — which uses the jar tool to zip up the webapp
into a single archive file — is a one-liner. Depending on your target servlet
container, you may not even need to perform this step — some servers allow you
to deploy a webapp directory directly.



 <target name=”war” depends=”webapp”>
<jar jarfile=”${build}/${project}.war” basedir=”${webapp}” />
</target>

Ant does have a <war> task, which assembles the whole WAR file in one
go. However, I prefer the two-step method (build webapp, build war).


Take a breath


We’ve covered a lot of ground here. Make sure to peruse the build.xml file and
play around with Ant properties. In the next section, we will discuss the
directory structure in detail.

References

  • Tomcat Application Developer’s Guide by Craig McClanahan –
    http://jakarta.apache.org/tomcat/tomcat-4.0-doc/appdev/index.html
  • jGuru Ant FAQ –
    http://www.jguru.com/faq/Ant
  • jGuru Servlets FAQ –
    http://www.jguru.com/faq/Servlets
  • Eric Siegel’s Computer Science Songs –
    http://www.cs.columbia.edu/~evs/songs/
  • JUnit test framework –
    http://junit.org



About the Author

Alex Chaffee is a leading consultant and trainer specializing in Java. He has been promoting, teaching, and programming in Java since 1995. As the director of software engineering for EarthWeb, he co-created Gamelan, the official directory for the Java community. He has made presentations at numerous users groups and conferences, written articles for several Java magazines and contributed to the book The Official Gamelan Java Directory. He has also published research papers on evolutionary computation (a.k.a., “genetic algorithms”) and on implicit memory in human cognition. You can contact him at alexc@purpletech.com.

# # #

Get the Free Newsletter!

Subscribe to Developer Insider for top news, trends & analysis

Latest Posts

Related Stories