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/ that 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 JavaServer Pages (JSP) page. We’re not going to tell you how to develop your Struts 2, Tapestry, Wicket, Java Server Faces (JSF), or Waffle application in the next 20-odd pages, and we’re not going to get into integrating an Inversion of Control (IoC) container such as 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 that uses Hibernate, Velocity, and the Spring Framework; and the other that 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 following 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 XML document shown in Example 5-1.
<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 that 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. Although you can still do such a thing, there
is no need. 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 plugin
element shown in Example 5-2 to your project’s build
configuration.
<project> [...] <build> <finalName>simple-webapp</finalName> <plugins> <plugin> <groupId>org.mortbay.jetty</groupId> <artifactId>maven-jetty-plugin</artifactId> </plugins> </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 = /Users/tobrien/svnw/sonatype/examples/simple-webapp/src/ main/webapp [INFO] web.xml file = /Users/tobrien/svnw/sonatype/examples/simple-webapp/src/ main/webapp/WEB-INF/web.xml [INFO] Classes = /Users/tobrien/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 = /Users/tobrien/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 - if your webapp requires one, please configure one. 2007-11-17 22:11:51.057::INFO: Started [email protected]: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. This is the directory where you will find the index.jsp file shown in Example 5-3.
<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, shown in Example 5-4.
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 code
shown in Example 5-5.
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
servlet
and servlet-mapping
elements shown in Example 5-6 to your
project’s web.xml file. The
web.xml file can be found in
src/main/webapp/WEB-INF.
<!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 your local Maven repository located at ~/.m2/repository. The same process will have to be repeated for all of the Java Platform Enterprise Edition (J2EE) APIs maintained by Sun Microsystems—Java Naming and Directory Interface (JNDI), Java Database Connectivity (JDBC), Servlet, JSP, Java Transaction API (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 that 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 releasing these specification JARs under the Apache Software License version 2.0, a license that 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 it 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 dependency element
shown in Example 5-7 to
pom.xml.
<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 were
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.
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 configuration shown in Example 5-8 to add this 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 add 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 - if your webapp requires one, please configure one. 2007-12-14 16:18:32.745::INFO: Started [email protected]: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.
3.144.119.170