Originally published on 2010-07-22; fully revised and updated.
If you maintain or modernize Java web applications that still use Apache Ant, this walkthrough shows how a WAR-oriented build.xml works end to end: from cleanup and compilation to packaging and deployment into a Tomcat webapps directory.
This revision is based on the original sample post: A sample Ant build script that builds a WAR file.

WAR build workflow in this Ant script
The script defines two deployment paths: a full WAR build/install path and a faster JSP/static-only refresh path.
flowchart TD
A[Run Ant target] --> B{Target selected}
B -->|install| C[clean and prepare]
C --> D[compile sources]
D --> E[copy descriptors and resources]
E --> F[build WAR archive]
F --> G[copy WAR to app server webapps]
B -->|jsps| H[copy JSP and static assets only]
Expected properties and project layout
The build relies on conventional directories for source, web pages, resources, and libraries. During packaging, it stages content into a temporary WAR-like folder structure under temp/WEB-INF.
src: Java source filessrc-tests: test or auxiliary Java sources (compiled by this sample)pages: JSP and static web contentresources:web.xml, manifest, and app XML fileslib: JAR dependencies for compile and WAR inclusiontarget: final WAR output directory
project-root/
├── build.xml
├── lib/
├── pages/
├── resources/
│ ├── MANIFEST.MF
│ ├── web.xml
│ ├── managed-beans.xml
│ └── navigation-rules.xml
├── src/
├── src-tests/
└── target/
Reference build.xml (based on the original sample)
The following is a cleaned, readable version of the sample logic from the original post.
<project name='MyWebApplication' basedir='..' default='install'>
<!-- project-specific variables -->
<property name='jsp.dir.name' value='myapp' />
<property name='package.name' value='${jsp.dir.name}.war' />
<property name='webapp.dir' value='/Users/al/tomcat-6.0.16/webapps' />
<property environment='env' />
<property name='build.dir' value='build' />
<property file='${build.dir}/build.${env.HOSTNAME}' />
<property name='lib.dir' value='lib' />
<property name='pages.dir' value='pages' />
<property name='src.dir' value='src' />
<property name='src.tests.dir' value='src-tests' />
<property name='resources.dir' value='resources' />
<property name='dest.dir' value='target' />
<!-- temporary staging structure -->
<property name='temp.dir' value='temp' />
<property name='temp.dir.web-inf' value='${temp.dir}/WEB-INF' />
<property name='temp.dir.lib' value='${temp.dir.web-inf}/lib' />
<property name='temp.dir.classes' value='${temp.dir.web-inf}/classes' />
<property name='temp.dir.meta-inf' value='${temp.dir}/META-INF' />
<property name='package.file' value='${dest.dir}/${package.name}' />
<path id='build.class.path'>
<fileset dir='${lib.dir}'>
<include name='**/*.jar' />
</fileset>
</path>
<target name='clean'>
<delete>
<fileset dir='${dest.dir}' includes='**/*' />
</delete>
<delete dir='${temp.dir}' />
<delete dir='${temp.dir.classes}' />
<delete dir='${temp.dir.meta-inf}' />
<delete dir='${temp.dir.web-inf}' />
</target>
<target name='prepare' depends='clean'>
<mkdir dir='${dest.dir}' />
<mkdir dir='${temp.dir}' />
<mkdir dir='${temp.dir.lib}' />
<mkdir dir='${temp.dir.meta-inf}' />
<mkdir dir='${temp.dir.web-inf}' />
<mkdir dir='${temp.dir.classes}' />
</target>
<target name='compile' depends='prepare'>
<echo>=== COMPILE ===</echo>
<javac debug='on' srcdir='${src.dir}' destdir='${temp.dir.classes}' includes='**/*'>
<classpath refid='build.class.path' />
</javac>
<javac debug='on' srcdir='${src.tests.dir}' destdir='${temp.dir.classes}' includes='com/**'>
<classpath refid='build.class.path' />
</javac>
</target>
<target name='package' depends='compile'>
<echo>=== PACKAGE ===</echo>
<copy file='${resources.dir}/MANIFEST.MF' tofile='${temp.dir.meta-inf}/MANIFEST.MF' overwrite='true' />
<copy file='${resources.dir}/web.xml' tofile='${temp.dir.web-inf}/web.xml' overwrite='true' />
<copy file='${resources.dir}/managed-beans.xml' tofile='${temp.dir.web-inf}/managed-beans.xml' overwrite='true' />
<copy file='${resources.dir}/navigation-rules.xml' tofile='${temp.dir.web-inf}/navigation-rules.xml' overwrite='true' />
<copy todir='${temp.dir.classes}'>
<fileset dir='${src.dir}'>
<include name='**/*.xml' />
<include name='**/*.xsl' />
</fileset>
</copy>
<war destfile='${package.file}' webxml='${temp.dir.web-inf}/web.xml' basedir='${temp.dir}'>
<fileset dir='${pages.dir}' />
<lib dir='${lib.dir}' />
<classes dir='${temp.dir.classes}' />
</war>
</target>
<target name='jsps'>
<echo>=== DEPLOY JSPS ===</echo>
<copy todir='${webapp.dir}/${jsp.dir.name}'>
<fileset dir='${pages.dir}'>
<include name='**/*.jsp' />
<include name='**/*.html' />
<include name='**/*.css' />
<include name='**/*.gif' />
<include name='**/*.jpg' />
<include name='**/*.png' />
<include name='**/*.js' />
</fileset>
</copy>
</target>
<target name='install' depends='package'>
<echo>=== INSTALL ===</echo>
<copy file='${package.file}' tofile='${webapp.dir}/${package.name}' overwrite='true' />
</target>
</project>Code language: HTML, XML (xml)
Run the build targets
Use Ant target names directly. Override environment-specific paths with -D properties to avoid editing build.xml per machine.
# Full build and WAR creation
ant package
# Build and copy WAR into Tomcat webapps
ant install -Dwebapp.dir=/opt/tomcat/webapps
# Fast JSP/static-only refresh
ant jsps -Dwebapp.dir=/opt/tomcat/webapps -Djsp.dir.name=myapp
# Clean output directories
ant cleanCode language: PHP (php)
Target comparison
| Target | Depends on | Primary output | Best for |
|---|---|---|---|
| clean | None | Removes temp and target artifacts | Resetting state before a new build |
| prepare | clean | Creates staging/output directories | Pre-compilation setup |
| compile | prepare | Compiled classes in temp/WEB-INF/classes | Verifying code compiles |
| package | compile | WAR file in target/ | Artifact creation for CI/CD |
| install | package | WAR copied to server webapps | Local or direct server deployment |
| jsps | None | JSP/static files copied to deployed app dir | Fast UI/content-only updates |
Common pitfalls and best practices
- Avoid hard-coded server paths. Externalize
webapp.dirusing-Dproperties or environment-specific property files. - Keep test outputs separate from production classes. In this sample,
src-testscompiles into the same classes directory used in the WAR. - Be deliberate with bundled libraries.
lib/**/*.jaris broad; verify you are not packaging container-provided JARs. - Prefer CI artifact publishing over direct server copy. Use
packageto generate the WAR, then deploy through your pipeline. - Fail fast on missing descriptors. Ensure required files like
web.xmlexist before packaging.
For legacy Ant projects, this pattern is still workable: keep targets small, parameterize environment values, and treat the generated WAR as the primary deployable artifact.
I’m Rajesh Kumar, a DevOps, SRE, DevSecOps, Cloud, and Platform Engineering expert passionate about sharing practical knowledge, real-world experiences, and industry best practices. I have worked at Cotocus and regularly write about technology, travel, investing, health, product reviews, and digital marketing through my various platforms.
I publish technical articles at DevOps School, travel stories at Holiday Landmark, stock market insights at Stocks Mantra, health and fitness guidance at My Medic Plus, product reviews at TrueReviewNow, and SEO and digital marketing strategies at Wizbrand.
Find Trusted Cardiac Hospitals
Compare heart hospitals by city and services — all in one place.
Explore Hospitals