
How Maven Builds a WAR File
Contents
Introduction
When dealing with a Java Web applications, the completed application is commonly delivered as a WAR file. The Maven build system can easily be set up to create WAR files. In this post we take an in-depth look at how Maven goes from a source project to the final WAR product.
Used software: Apache Maven 3.0.4.
The structure of a WAR file looks like this (source):
Example Project
Let’s look at a very simple Mavenized Web application project in a directory named myprojectname. The contents of this folder are:
The pom.xml file looks like this:
xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">> 4.0.0>> mygroup.com>> myprojectname>> war>> 1.0-SNAPSHOT>> myprojectname Maven Webapp>> http://maven.apache.org>> > > junit>> junit>> 3.8.1>> test> > >> > myprojectname> > >
To package this bare-bones project into a WAR file we use the mvn package
command.
C:\Projects\myprojectname>mvn package [INFO] Scanning for projects... [INFO] [INFO] ------------------------------------------------------------------------ [INFO] Building myprojectname Maven Webapp 1.0-SNAPSHOT [INFO] ------------------------------------------------------------------------ [INFO] ... [INFO] --- maven-war-plugin:2.1.1:war (default-war) @ myprojectname --- [INFO] Packaging webapp [INFO] Assembling webapp [myprojectname] in [C:\Projects\myprojectname\target\myprojectname] [INFO] Processing war project [INFO] Copying webapp resources [C:\Projects\myprojectname\src\main\webapp] [INFO] Webapp assembled in [18 msecs] [INFO] Building war: C:\Projects\target\myprojectname.war ...
This creates a WAR file at /target/myprojectname.war
. The execution of this mvn package
command is the scenario we will take a closer look at. An overview of the operation is displayed in the image below.

Maven is used to build a WAR file from the source project, through the "mvn package" command.
Maven Configuration Is Mostly Implicit
We know that Maven can succesfully create a WAR file from the source project. But our pom.xml file is almost empty, so how does Maven know things like where the .java files for compilation are, or what the exact structure of the WAR file should be? The answer is that these settings are implicit. Maven uses a “convention over configuration” approach, and provides default values for the project’s configuration.
First, some things are implicit because of the default way Maven plugins are bound to the lifecycle phases. For example, the compile phase has the goal compiler:compile assigned to it by default. This means that when executing the compile phase, the compile goal of the compiler plugin is called. When you select WAR packaging in your pom.xml, the war:war goal (which creates the WAR) is bound to the package phase.
Second, plugins define default values for their parameters that are used when no explicit value is passed. For example, the compiler:compile goal has a parameter called compilerId. This parameter has the value javac
by default, meaning the regular JDK compiler will be used. A project’s pom.xml file can override this default to use a different compiler.
Finally, some settings are contained in the Super POM that every pom.xml implicitly inherits. For Maven 3 the Super POM is located in maven_dir/lib/maven-model-builder-3.0.3.jar:org/apache/maven/model/pom-4.0.0.xml
. In this file we find the default directory locations for source files, resources, test source files, etc:
> > ${project.basedir}/target>> ${project.build.directory}/classes>> ${project.artifactId}-${project.version}>> ${project.build.directory}/test-classes>> ${project.basedir}/src/main/java>> src/main/scripts>> ${project.basedir}/src/test/java>> > > ${project.basedir}/src/main/resources> > >> > > ${project.basedir}/src/test/resources> > > ... >
These mappings define Maven’s Standard Directory Layout, and it is encouraged to keep this default structure in all your Maven projects.
Executed Lifecycle Phases
If you are having trouble understanding this section, go read about Maven lifecycles, phases, goals and plugins here.
For our project, when mvn package
is entered this will execute all lifecycle phases that have a goal bound to them in sequence, up to and including the package phase. In our case this means six phases are executed: process-resources
, compile
, process-test-resources
, test-compile
, test
and package
.
Each phase consists of one or more goals. Goals are provided by Maven plugins: a plugin may have one or more goals wherein each goal represents a capability of that plugin. For example, the Compiler plugin has two goals: compiler:compile
and compiler:testCompile
.
We can use the mvn help:describe -Dcmd=phasename
command to list the lifecycle phases along with bound goals for a project. Example:
C:\Project\myprojectname>mvn help:describe -Dcmd=package [INFO] Scanning for projects... [INFO] [INFO] ------------------------------------------------------------------------ [INFO] Building myprojectname Maven Webapp 1.0-SNAPSHOT [INFO] ------------------------------------------------------------------------ [INFO] [INFO] --- maven-help-plugin:2.1.1:describe (default-cli) @ myprojectname --- [INFO] 'package' is a phase corresponding to this plugin: org.apache.maven.plugins:maven-war-plugin:2.1.1:war It is a part of the lifecycle for the POM packaging 'war'. This lifecycle includes the following phases: * validate: Not defined * initialize: Not defined * generate-sources: Not defined * process-sources: Not defined * generate-resources: Not defined * process-resources: org.apache.maven.plugins:maven-resources-plugin:2.5:resources * compile: org.apache.maven.plugins:maven-compiler-plugin:2.3.2:compile * process-classes: Not defined * generate-test-sources: Not defined * process-test-sources: Not defined * generate-test-resources: Not defined * process-test-resources: org.apache.maven.plugins:maven-resources-plugin:2.5:testResources * test-compile: org.apache.maven.plugins:maven-compiler-plugin:2.3.2:testCompile * process-test-classes: Not defined * test: org.apache.maven.plugins:maven-surefire-plugin:2.10:test * prepare-package: Not defined * package: org.apache.maven.plugins:maven-war-plugin:2.1.1:war * pre-integration-test: Not defined * integration-test: Not defined * post-integration-test: Not defined * verify: Not defined * install: org.apache.maven.plugins:maven-install-plugin:2.3.1:install * deploy: org.apache.maven.plugins:maven-deploy-plugin:2.7:deploy [INFO] ------------------------------------------------------------------------ [INFO] BUILD SUCCESS [INFO] ------------------------------------------------------------------------ [INFO] Total time: 2.496s [INFO] Finished at: Sat May 12 04:30:35 CEST 2012 [INFO] Final Memory: 5M/121M [INFO] ------------------------------------------------------------------------
Next, we look at each goal that is goals executed in the WAR build in more detail.
1. resources:resources
The resources:resources goal copies resources for the main source code to the main output directory.
2. compiler:compile
The compiler:compile goal compiles the application source files.
3. resources:testResources
The resources:testResources goal copies resources for the unit test source code to the test output directory.
4. compiler:testCompile
The compiler:compile goal compiles the application unit test sources.
5. surefire:test
The surefire:test goal executes the unit tests of an application, on the classes in the /target/test-classes
directory. The build stops if a unit test fails. When there are no test classes, the goal is always succesful.
7. war:war
The war:war goal builds a WAR file. It gathers all needed files in the /target/myprojectname/
directory, then packages them up into myprojectname.war
. One of these steps is copying the contents of /src/main/webapp/
to /target/myprojectname/
.
Other important tasks performed by the war plugin are copying the compiled classes are copied to WEB-INF/classes/
, and runtime dependencies to WEB-INF/lib/
. By default the WAR builder also includes two Maven descriptor files (details):
- The pom file, located in the archive in
META-INF/maven/${groupId}/${artifactId}/pom.xml
- A pom.properties file, located in the archive in
META-INF/maven/${groupId}/${artifactId}/pom.properties
. During the build this file is generated in the/target/maven-archiver/pom.properties
directory.
The file looks something like this:#Generated by Maven #Sat May 12 00:50:42 CEST 2012 version=1.0-SNAPSHOT groupId=mygroup.com artifactId=myprojectname
Finally, the /target/myprojectname
directory is packaged up into a WAR file. In addition the pom file and the pom.properties file are put in the META-INF/maven
directory of the archive, and a manifest file is generated at META-INF/MANIFEST.MF
.

The final step is packaging everything into a WAR file. The bulk of the files comes from the target/myprojectname directory (red), and some additional files are included (green).
The final WAR is placed in /target/myprojectname.war
. This WAR can now be deployed with a servlet container (e.g., Tomcat) or application server (e.g., Glassfish).
Dependencies
Currently, our pom.xml only has one dependency (JUnit). Let’s add another dependency and see what happens when the WAR is built.
We add this dependency to the pom:
> > log4j>> log4j>> 1.2.16> >
When no scope is specified, a dependency has the compile scope (read more). The compile scope means that the dependency is available during compilation, testing, and at runtime.
Every dependency that is needed at runtime must be distributed with the application. So when we run mvn package
with this pom, Maven downloads the log4j JAR and includes it the /WEB-INF/lib
directory in the WAR file.
Great break down of war packaging, thank you.
Excellent, thank you!
Thanks a lot!!! the images are very detailed.
Excellent. Thanks for the work.