
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.
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.
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.
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.
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>
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.
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
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.

