Chapter 5. A Simple Web Application

5.1. Introduction

Favicon

In this chapter, we create a simple web application with the Maven Archetype plugin. We'll run this web application in a Servlet container named Jetty, add some dependencies, write a simple Servlet, and generate a WAR file. At the end of this chapter, you will be able to start using Maven to accelerate the development of web applications.

5.1.1. Downloading this Chapter's Example

Favicon

The example in this chapter is generated with the Maven Archetype plugin. While you should be able to follow the development of this chapter without the example source code, we recommend downloading a copy of the example code to use as a reference. This chapter's example project may be downloaded with the book's example code at http://www.sonatype.com/book/mvn-examples-1.0.zip or http://www.sonatype.com/book/mvn-examples-1.0.tar.gz. Unzip this archive in any directory, and then go to the ch05/ directory. In the ch05/ directory you will see a directory named simple-webapp/ which contains the Maven project developed in this chapter. If you wish to follow along with the example code in a web browser, go to http://www.sonatype.com/book/examples-1.0 and click on the ch05/ directory.

5.2. Defining the Simple Web Application

Favicon

We've purposefully kept this chapter focused on Plain-Old Web Applications (POWA)—a servlet and a JSP page. We're not going to tell you how to develop your Struts 2, Tapestry, Wicket, JSF, or Waffle application in the next twenty odd pages, and we're not going to get into integrating an IoC container like Plexus, Guice, or the Spring Framework. The goal of this chapter is to show you the basic facilities that Maven provides for developing web applications—no more, no less. Later in this book, we're going to take a look at developing two web applications, one which uses Hibernate, Velocity, and the Spring Framework, and the other which uses Plexus.

5.3. Creating the Simple Web Project

Favicon

To create your web application project, run mvn archetype:create with an artifactId and a groupId. Specify the archetypeArtifactId as maven-archetype-webapp. Running this will create the appropriate directory structure and Maven POM.

~/examples$ mvn archetype:create -DgroupId=org.sonatype.mavenbook.ch05 \
                            -DartifactId=simple-webapp \
                            -DpackageName=org.sonatype.mavenbook \
                            -DarchetypeArtifactId=maven-archetype-webapp
[INFO] [archetype:create]
[INFO] ----------------------------------------------------------------------
[INFO] Using parameters for creating Archetype: maven-archetype-webapp:RELEASE
[INFO] ----------------------------------------------------------------------
[INFO] Parameter: groupId, Value: org.sonatype.mavenbook.ch05
[INFO] Parameter: packageName, Value: org.sonatype.mavenbook
[INFO] Parameter: basedir, Value: ~/examples
[INFO] Parameter: package, Value: org.sonatype.mavenbook
[INFO] Parameter: version, Value: 1.0-SNAPSHOT
[INFO] Parameter: artifactId, Value: simple-webapp
[INFO] *************** End of debug info from resources from generated POM **
[INFO] Archetype created in dir: ~/examples/simple-webapp 

Once the Maven Archetype plugin creates the project, change directories into the simple-web directory and take a look at the pom.xml. You should see the following XML document:

Example 5.1. Initial POM for the simple-web project

<project 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">
  <modelVersion>4.0.0</modelVersion>
  <groupId>org.sonatype.mavenbook.ch05</groupId>
  <artifactId>simple-webapp</artifactId>
  <packaging>war</packaging>
  <version>1.0-SNAPSHOT</version>
  <name>simple-webapp Maven Webapp</name>
  <url>http://maven.apache.org</url>
  <dependencies>
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>3.8.1</version>
      <scope>test</scope>
    </dependency>
  </dependencies>
  <build>
    <finalName>simple-webapp</finalName>
    <plugins>
      <plugin>
        <artifactId>maven-compiler-plugin</artifactId>
        <configuration>
          <source>1.5</source>
          <target>1.5</target>
        </configuration>
      </plugin>
    </plugins>
  </build>
</project>

Notice the packaging element contains the value war. This packaging type is what configures Maven to produce a web application archive in a WAR file. A project with war packaging, is going to create a WAR file in the target/ directory, the default name of this file is ${artifactId}-${version}.war. In this project, the default WAR would be generated in target/simple-webapp-1.0-SNAPSHOT.war. In the simple-webapp project, we've customized the name of the generated WAR file by adding a finalName element inside of this project's build configuration. With a finalName of simple-webapp, the package phase produces a WAR file in target/simple-webapp.war.

5.4. Configuring the Jetty Plugin

Favicon

Once you've compiled, tested, and packaged your web application, you'll likely want to deploy it to a servlet container and test the index.jsp which was created by the Maven Archetype plugin. Normally, this would involve downloading something like Jetty or Apache Tomcat, unpacking a distribution, copying your application's WAR file to a webapps/ directory, and then starting your container. And, while you could still do such a thing, there is no need to do this. Instead, you can use the Maven Jetty plugin to run your web application within Maven. To do this, we'll need to configure the Maven Jetty Plugin in our project's pom.xml. Add the following plugin element to your project's build configuration.

Example 5.2. Configuring the Jetty Plugin

<project>
  [...]
  <build>
    <finalName>simple-webapp</finalName>
    <plugins>
      <plugin>
        <groupId>org.mortbay.jetty</groupId>
        <artifactId>maven-jetty-plugin</artifactId>
      </plugin>
    </plugins>
  </build>
  [...]
</project>

Once you've configured the Maven Jetty Plugin in your project's pom.xml, you can then invoke the Run goal of the Jetty plugin to start your web application in the Jetty Servlet container. Run mvn jetty:run as follows:

~/examples$ mvn jetty:run
...
[INFO] [jetty:run]
[INFO] Configuring Jetty for project: simple-webapp Maven Webapp
[INFO] Webapp source directory = \
       ~/svnw/sonatype/examples/simple-webapp/src/main/webapp
[INFO] web.xml file = \
       ~/svnw/sonatype/examples/simple-webapp/src/main/webapp/WEB-INF/web.xml
[INFO] Classes = ~/svnw/sonatype/examples/simple-webapp/target/classes
2007-11-17 22:11:50.532::INFO:  Logging to STDERR via org.mortbay.log.StdErrLog
[INFO] Context path = /simple-webapp
[INFO] Tmp directory =  determined at runtime
[INFO] Web defaults = org/mortbay/jetty/webapp/webdefault.xml
[INFO] Web overrides =  none
[INFO] Webapp directory = \
       ~/svnw/sonatype/examples/simple-webapp/src/main/webapp
[INFO] Starting jetty 6.1.6rc1 ...
2007-11-17 22:11:50.673::INFO:  jetty-6.1.6rc1
2007-11-17 22:11:50.846::INFO:  No Transaction manager found 
2007-11-17 22:11:51.057::INFO:  Started SelectChannelConnector@0.0.0.0:8080
[INFO] Started Jetty Server

After Maven starts the Jetty Servlet container, load the URL http://localhost:8080/simple-webapp/ in a web browser. The simple index.jsp generated by the Archetype is trivial; it contains a second-level heading with the text "Hello World!". Maven expects the document root of the web application to be stored in src/main/webapp. It is in this directory where you will find the index.jsp file shown in Example 5.3, “Contents of src/main/webapp/index.jsp”.

Example 5.3. Contents of src/main/webapp/index.jsp

<html>
  <body>
    <h2>Hello World!</h2>
  </body>
</html>

In src/main/webapp/WEB-INF we will find the smallest possible web application descriptor in web.xml.

Example 5.4. Contents of src/main/webapp/WEB-INF/web.xml

<!DOCTYPE web-app PUBLIC
 "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
 "http://java.sun.com/dtd/web-app_2_3.dtd" >

<web-app>
  <display-name>Archetype Created Web Application</display-name>
</web-app>

5.5. Adding a Simple Servlet

Favicon

A web application with a single JSP page and no configured servlets is next to useless. Let's add a simple servlet to this application and make some changes to the pom.xml and web.xml to support this change. First, we'll need to create a new package under src/main/java named org.sonatype.mavenbook.web.

$ mkdir -p src/main/java/org/sonatype/mavenbook/web
$ cd src/main/java/org/sonatype/mavenbook/web

Once you've created this package, change to the src/main/java/org/sonatype/mavenbook/web directory and create a class named SimpleServlet in SimpleServlet.java which contains the following code.

Example 5.5. SimpleServlet Class

package org.sonatype.mavenbook.web;

import java.io.*;
import javax.servlet.*;                                                         
import javax.servlet.http.*;

public class SimpleServlet extends HttpServlet {
    public void doGet(HttpServletRequest request,
                      HttpServletResponse response)
        throws ServletException, IOException {
    
        PrintWriter out = response.getWriter();
        out.println( "SimpleServlet Executed" );
        out.flush();
        out.close();
    }
}

Our SimpleServlet class is just that, a servlet that prints a simple message to the response's Writer. To add this servlet to your web application, and map it to a request path, add the following servlet and servlet-mapping elements to your project's web.xml file. The web.xml file can be found in src/main/webapp/WEB-INF.

Example 5.6. Mapping the Simple Servlet

<!DOCTYPE web-app PUBLIC
 "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
 "http://java.sun.com/dtd/web-app_2_3.dtd" >

<web-app>
  <display-name>Archetype Created Web Application</display-name>
  <servlet>
    <servlet-name>simple</servlet-name>
    <servlet-class>org.sonatype.mavenbook.web.SimpleServlet</servlet-class>
  </servlet>
  <servlet-mapping>
    <servlet-name>simple</servlet-name>
    <url-pattern>/simple</url-pattern>
  </servlet-mapping>
</web-app>

Everything is in place to test this servlet, the class is in src/main/java and the web.xml has been updated. Before we launch the Jetty plugin, compile your project by running mvn compile:

~/examples$ mvn compile
...
[INFO] [compiler:compile]
[INFO] Compiling 1 source file to ~/examples/ch05/simple-webapp/target/classes
[INFO] ------------------------------------------------------------------------
[ERROR] BUILD FAILURE
[INFO] ------------------------------------------------------------------------
[INFO] Compilation failure

/src/main/java/org/sonatype/mavenbook/web/SimpleServlet.java:[4,0] \
  package javax.servlet does not exist

/src/main/java/org/sonatype/mavenbook/web/SimpleServlet.java:[5,0] \
  package javax.servlet.http does not exist

/src/main/java/org/sonatype/mavenbook/web/SimpleServlet.java:[7,35] \
  cannot find symbol
  symbol: class HttpServlet
  public class SimpleServlet extends HttpServlet {

/src/main/java/org/sonatype/mavenbook/web/SimpleServlet.java:[8,22] \
  cannot find symbol
  symbol  : class HttpServletRequest
  location: class org.sonatype.mavenbook.web.SimpleServlet

/src/main/java/org/sonatype/mavenbook/web/SimpleServlet.java:[9,22] \
  cannot find symbol
  symbol  : class HttpServletResponse
  location: class org.sonatype.mavenbook.web.SimpleServlet

/src/main/java/org/sonatype/mavenbook/web/SimpleServlet.java:[10,15] \
  cannot find symbol
  symbol  : class ServletException
  location: class org.sonatype.mavenbook.web.SimpleServlet

The compilation fails because your Maven project doesn't have a dependency on the Servlet API. In the next section, we'll add the Servlet API to this project's POM.

5.6. Adding J2EE Dependencies

Favicon

To write a servlet, we'll need to add the Servlet API as a project dependency. The Servlet specification is a JAR file that can be downloaded from Sun Microsystems at http://java.sun.com/products/servlet/download.html. Once the JAR file is downloaded you'll need to install the resulting JAR in you local Maven repository located at ~/.m2/repository. The same process will have to be repeated for all of the J2EE APIs maintained by Sun Microsystems—JNDI, JDBC, Servlet, JSP, JTA, and others. If this strikes you as somewhat tedious, you are not alone. Lucky for you, there is a simpler alternative to downloading all of these libraries and installing them manually—Apache Geronimo's independent open-source implementations.

For years, the only way to get the Servlet specification JAR was to download it directly from Sun Microsystems. You had to go to the Sun web site, agree to a click-through licensing agreement and only then could you access the Servlet JAR. This was all necessary because the Sun specification JARs were not made available under a license which allowed for redistribution. Manually downloading Sun artifacts was something you just had to do to write a Servlet or to use JDBC from a Maven project for a few years. It was tedious and annoying until the Apache Geronimo project was able to create a Sun certified implementation of a number of enterprise specifications. These specification JARs are released under the Apache Software License Version 2.0, a license which allows for free redistribution of source and binary. Now, for the purposes of your programming, there is little to no difference between the Servlet API JAR downloaded from Sun Microsystems and the Servlet API JAR implemented by the Apache Geronimo project, both have passed a rigorous Test Compatibility Kit (TCK) from Sun Microsystems.

Adding a dependency on something like the JSP API or the Servlet API is now very straightforward, and does not require you to manually download a JAR file from a web site and install it in your local repository. The catch is that you have to know where to look; what groupId, artifactId, and version to use to reference the appropriate Apache Geronimo implementation. To add the Servlet specification API as a dependency to your project's POM, add the following dependency element to pom.xml.

Example 5.7. Add the Servlet 2.4 Specification as a Dependency

<project>
  [...]
  <dependencies>
    [...]
    <dependency>
      <groupId>org.apache.geronimo.specs</groupId>
      <artifactId>geronimo-servlet_2.4_spec</artifactId>
      <version>1.1.1</version>
      <scope>provided</scope>
    </dependency>
  </dependencies>
  [...]
</project>

The groupId for all of the Apache Geronimo specification implementations is org.apache.geronimo.specs. The artifactId contains the version of the specification that you are most familiar with; for example, if you were going to include the Servlet 2.3 specification you would have an artifactId of geronimo-servlet_2.3_spec, and if you are targeting the Servlet 2.4 specification, your artifactId would be geronimo-servlet_2.4_spec. As for the version, you'll have to take a look at the public Maven repository to figure out which version you should use. For versions, your best bet is going to be the latest version for a particular specification implementation. If you are looking for a specific alternative to a Sun Specification from the Apache Geronimo project, we've assembled a list of available specifications in Appendix B, Appendix: Sun Specification Alternatives.

It is also worth pointing out that we have used the provided scope for this dependency. This tells Maven that the jar is "provided" by the container and thus should not be included in the war.

If you were interested in writing a custom JSP tag for this simple web application, you would need to add a dependency on the JSP 2.0 spec. Use the following configuration to add this dependency.

Example 5.8. Adding the JSP 2.0 Specification as a Dependency

<project>
  [...]
  <dependencies>
    [...]
    <dependency>
      <groupId>org.apache.geronimo.specs</groupId>
      <artifactId>geronimo-jsp_2.0_spec</artifactId>
      <version>1.1</version>
      <scope>provided</scope>
    </dependency>
  </dependencies>
  [...]
</project>

Once you've added the Servlet specification as a dependency, run mvn clean install followed by mvn jetty:run.

[tobrien@t1 simple-webapp]$ mvn clean install
...
[tobrien@t1 simple-webapp]$ mvn jetty:run
[INFO] [jetty:run]
...
2007-12-14 16:18:31.305::INFO:  jetty-6.1.6rc1
2007-12-14 16:18:31.453::INFO:  No Transaction manager found 
2007-12-14 16:18:32.745::INFO:  Started SelectChannelConnector@0.0.0.0:8080
[INFO] Started Jetty Server

At this point, you should be able to retrieve the output of the SimpleServlet. From the command line, you can use curl to print the output of this servlet to standard output:

~/examples$ curl http://localhost:8080/simple-webapp/simple
SimpleServlet Executed

5.7. Conclusion

Favicon

After reading this chapter, you should be able to bootstrap a simple web application. This chapter didn't dwell on the million different ways to create a complete web application, other chapters provide a more comprehensive overview of projects that involve some of the more popular web frameworks and technologies.


Creative Commons License
Maven: The Definitive Guide by Sonatype, Inc. is licensed under a Creative Commons Attribution-Noncommercial-No Derivative Works 3.0 United States License.
Based on a work at www.sonatype.com.

Favicon Report Typos, Errors, Ask Questions, Discuss, Share Your Ideas with us at Get Satisfaction