Apache Maven 1.x has reached its end of life, and is no longer supported. For more information, see the announcement. Users are encouraged to migrate to the current version of Apache Maven.

Web Applications

Building a web application is not too much different to working with other types such as JARs in Maven. You still create a project descriptor, add your source code and resources, and build an archive to finish.

However, a few extra steps must be taken to create the appropriate web application structure.

Adding Webapp Contents

The first step is to add the web application contents. Unlike a JAR, resources are not included in the root of the WAR - they will be located in /WEB-INF/classes, as the purpose of the resources is to make them available in the classloader inside the application.

To include the contents of your web application (JSPs, images, etc., as well as some of the contents of WEB-INF), create a directory called src/main/webapp. These will all reside in the base of the web application when it is packaged up.

To configure the web application to use these resources, set the property:

maven.war.src=${basedir}/src/main/webapp

You can include whatever you like in this structure, including a WEB-INF directory and anything underneath it. However, you should not have a /WEB-INF/classes or /WEB-INF/lib directory as Maven will populate this from your project.

As mentioned, resources and classes will be compiled and copied into the /WEB-INF/classes directory. The lib directory is populated from the project's dependency list, as shown in the next section.

Adding Dependencies to the lib Directory

Since Maven requires that you declare all of the dependencies of your project, it is quite easy to also set up the lib directory in your web application.

Note however, libraries are not bundled into the web application by default - you must list those that you want included. This is quite simple to do, by adding a property to those dependencies that are required in the WAR:

<dependency>
  <groupId>mycompany</groupId>
  <artifactId>mycompany-utils</artifactId>
  <version>1.0.0</version>
  <properties>
    <war.bundle>true</war.bundle>
  </properties>
</dependency>

Building and Deploying

There are two particular goals that are used to build a web application: war:webapp and war:war.

The first builds an unpacked directory structure in the target directory that is the same as it would be exploded into an application server. This can be most effective for deploying directly into an application server with reloading enabled in the development environment so that reconstructing the WAR in this directory after making changes will be loaded.

The second will build a .war archive which is later deployed into the servlet container as is.

To automatically deploy into a servlet container will rely on scripting up the appropriate goals or locating an existing plugin for the container. There is currently one available for JBoss - see the JBoss Plugin. Tomcat also has several deployment Ant tasks which can easily be used from Maven.

Further Information

If you would like an example, please refer to The simple-webapp example project.

For more information on goals and configuration, see the WAR plugin reference.

Precompiling JSPs for Tomcat

How to precompile JSPs is a common question when developing Web Applications, as doing so can bring a performance boost to the first time deployment of your application in a production environment.

The following code snippet can be used in maven.xml if you are using a Tomcat 4.1 based servlet container. It should also serve as a starting point for implementing a similar task for other containers.

<preGoal name="war:webapp">
  <j:set var="precompileJsp" value="${precompile.jsp}"/>
  <j:if test="${precompileJsp == 'true'}">
    <attainGoal name="precompile-jsp"/>
  </j:if>
</preGoal>

<postGoal name="war:webapp">
  <j:set var="precompileJsp" value="${precompile.jsp}"/>
  <j:if test="${precompileJsp == 'true'}">
    <maven:set var="target" property="maven.war.webapp.dir" plugin="maven-war-plugin" />
    <util:available file="${maven.build.dir}/web-fragment.xml">
      <util:loadText var="fragment" file="${maven.build.dir}/web-fragment.xml"/>
      <ant:replace file="${target}/WEB-INF/web.xml" token="&lt;!-- [INSERT FRAGMENT HERE] --&gt;" value="${fragment}"/>
    </util:available>
  </j:if>
</postGoal>

<goal name="precompile-jsp" description="Precompile all JSPs into java classes, and then into classes" prereqs="war:load,java:compile">
  <maven:set var="warSource" property="maven.war.src" plugin="maven-war-plugin" />
  <ant:mkdir dir="${maven.build.dir}/jspc"/>
  <ant:mkdir dir="${maven.build.dir}/jspc-processed"/>
  <ant:mkdir dir="${maven.build.dir}/jspc-classes"/>

  <j:set var="jspOutDir" value="${maven.build.dir}/jspc"/>
  <j:set var="jspClassesOutDir" value="${maven.build.dest}"/>
  <ant:path id="jspc.classpath">
    <ant:pathelement location="${tomcat.home}/common/lib/jasper-runtime.jar"/>
    <ant:pathelement location="${tomcat.home}/common/lib/jasper-compiler.jar"/>
    <ant:pathelement location="${tomcat.home}/common/lib/servlet.jar"/>
    <ant:path refid="maven.dependency.classpath"/>
    <ant:pathelement path="${maven.build.dest}"/>
  </ant:path>
  <ant:taskdef name="jasper2" classname="org.apache.jasper.JspC" classpathref="jspc.classpath"/>
  <ant:jasper2
    webXmlFragment="${maven.build.dir}/web-fragment.xml"
    package="${pom.package}.jsp.${pom.artifactId}"
    outputDir="${jspOutDir}"
    srcdir="${warSource}"
    uriroot="${warSource}"
    uribase="/${pom.artifactId}"
    verbose="2"/>
  <ant:javac
    srcdir="${jspOutDir}"
    destdir="${jspClassesOutDir}"
    debug="${maven.compile.debug}"
    deprecation="${maven.compile.deprecation}"
    optimize="${maven.compile.optimize}"
    classpathref="jspc.classpath"/>
</goal>

To execute this code, set the precompile.jsp property to true. When building the web application, the JSPs will be compiled and added to the generated WAR file.

These servlets will not be utilised by Tomcat by default. To do so, you must include them in the web.xml file. The script above does this, as long as you include the following comment in your web.xml file in between the last servlet and first servlet-mapping declaration:

<!-- [INSERT FRAGMENT HERE] -->