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.
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 jakarta.apache.org, 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 contains one or more 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 purpletech.com. The build.xml file, excerpted throughout this article, is at purpletech.
Properties (that is, variables) are a very important part of Ant. Generally, the important strings — directory names, file names, the classpath — are declared as 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 task, calling one ant file from another one, or (c) .
The 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:
with the following content inside build.properties:
will output the following:
foo=from build.xml bar=from build.properties
Note that you can declare elements directly as children of the (although I prefer to set them inside the “init” task). Also note that using tells ant to expand the file/directory names to absolute paths, which can help resolve some possible confusions. (Be careful to go back to for non-file properties!)
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”.)
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.
I prefer to have a separate target that runs (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.
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 task (the easy way — see the “javatest” target), or through the optional task.
To enable the task, you must do a few steps:
- Download jakarta-ant-[version]-optional.jar from jakarta.apache.org, and put it into ANT_HOME/lib
- Also put junit.jar into ANT_HOME/lib
- 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.
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.
Ant does have a 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.
- Tomcat Application Developer’s Guide by Craig McClanahan – jakarta apache org tomcat tomcat-4.0-doc appdev index
- jGuru Ant FAQ – jguru faq Ant
- jGuru Servlets FAQ – jguru faq Servlets
- Eric Siegel’s Computer Science Songs – cs columbia.edu evs songs
- JUnit test framework – junit.org