Enterprise Java

How to create a Web Application Project with Java, Maven, Jetty

In this article, 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 simple Servlets, and generate a WAR file. At the end of this article, you will also be able to deploy the service in Tomcat.

System requirements

Creating the Web Service Step by Step

This section explains how to create this simple web project from an EMPTY folder.

Creating the Simple Web Project

To create your web application

$ mvn archetype:generate -DgroupId=com.pengyifan.simpleweb \
      -DartifactId=simple-webapp \
      -Dpackage=com.pengyifan.simpleweb \
      -DarchetypeArtifactId=maven-archetype-webapp \
      -Dversion=1.0-SNAPSHOT \
      -DinteractiveMode=false

...
[INFO] BUILD SUCCESS

Once the Maven Archetype plugin creates the project, change the directory into the simple-webapp directory and take a look at the pom.xml. You should see the

<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>com.pengyifan.simpleweb</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>
  </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. Thus, the default name of this file is ${artifactId}-${version}.war. In this Maven project, the default WAR would be generated in target/simple-webapp-1.0-SNAPSHOT.war. Furthermore, 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.

Next, you will need to configure

  1. Maven Compiler plugin to target Java version (JDK 8 in this article)
  2. Java Servlet dependency.
  3. Maven Jetty Plugin.

The pom.xml should look like this. You can also overwrite pom.xml using the one in the downloaded package.

<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>com.pengyifan.simpleweb</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>
    <dependency>
      <groupId>javax.servlet</groupId>
      <artifactId>servlet-api</artifactId>
      <version>2.4</version>
      <scope>provided</scope>
    </dependency>
  </dependencies>
  <build>
    <finalName>simple-webapp</finalName>
    <plugins>
      <plugin>
        <artifactId>maven-compiler-plugin</artifactId>
        <configuration>
          <source>1.8</source>
          <target>1.8</target>
        </configuration>
      </plugin>
      <plugin>
        <groupId>org.mortbay.jetty</groupId>
        <artifactId>maven-jetty-plugin</artifactId>
        <version>6.1.26</version>
        <configuration>
          <connectors>
            <connector implementation="org.mortbay.jetty.nio.SelectChannelConnector">
              <port>80</port>
            </connector>
          </connectors>
        </configuration>
      </plugin>
    </plugins>
  </build>
</project>

Now, you can invoke the Run goal of the Jetty plugin to start your web application in the Jetty Servlet container. Run:

$ mvn jetty:run

Note that mvn jetty:run will continue to run the Jetty servlet container until you stop the process with CTRL-C.

After Maven starts the Jetty Servlet container, load the URL http://localhost/simple-webapp/index.jsp 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.

<h2>
  Hello World!
</h2>

In src/main/webapp/WEB-INF, we will find the smallest possible web application web.xml, shown in this next example:

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

Adding a Simple Servlet

A web application with a single 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 com.pengyifan.web:

$ mkdir -p src/main/java/com/pengyifan/web
$ cd src/main/java/com/pengyifan/web

Once you’ve created this package, change to the src/main/java/com/pengyifan/web directory and create a class named SimpleServlet:

package com.pengyifan.web;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;

public class SimpleServlet extends HttpServlet {
  @Override
  public void doGet(HttpServletRequest request, 
      HttpServletResponse response)
      throws ServletException, IOException {
    doPost(request, response);
  }

  @Override
  public void doPost(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. Now we need to add this servlet to your web application and map it to a request path. You can change web.xml in src/main/webapp/WEB-INF.

<web-app>
  <display-name>Archetype Created Web Application</display-name>
  <servlet>
    <servlet-name>simple</servlet-name>
    <servlet-class>
      com.pengyifan.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. Run:

$ mvn clean install
...
$ mvn jetty:run
[INFO] [jetty:run]
...
[INFO] Started Jetty Server

At this point, you should be able to retrieve the output of the SimpleServlet. From the web browser, you can load http://localhost:8080/simple-webapp/simple, and get SimpleServlet Executed.

Changing the port

In the source package, we use the port 80. In this task, we recommend you to listen on port 80 for the RESTful API. But we could still listen to other ports. To do so, find the following lines in the pom.xml file, and change the number in <port>.

[...]
<plugin>
  <groupId>org.mortbay.jetty</groupId>
  <artifactId>maven-jetty-plugin</artifactId>
  <version>6.1.26</version>
  <configuration>
    <connectors>
      <connector implementation="org.mortbay.jetty.nio.SelectChannelConnector">
        <port>80</port>
      </connector>
    </connectors>
  </configuration>
</plugin>
[...]

Enabling SSL

For security reason, we might needto enable SSL communication between the server and client. So when we run mvn jetty:run we must be able to use the https protocol. For development we can create our own security certificate and configure the plugin to use it.

To create the development certificate we run the following command:

keytool -genkey -alias jetty6 -keyalg RSA \
  -keystore target/jetty-ssl.keystore \
  -storepass jetty6 -keypass jetty6 \
  -dname "CN=your name or domain"

Fill in your name or domain for the -dname "CN=" option. We need the keystore and key password again when we configure the plugin in the Maven pom.xml. The following code fragment shows how the Jetty plugin supports SSL:

<plugin>
  <groupId>org.mortbay.jetty</groupId>
  <artifactId>maven-jetty-plugin</artifactId>
  <configuration>
    <connectors>
      <connector implementation="org.mortbay.jetty.nio.SelectChannelConnector">
        <port>8080</port>
      </connector>
      <connector implementation="org.mortbay.jetty.security.SslSocketConnector">
        <port>8081</port>
        <maxIdleTime>60000</maxIdleTime>
        <keystore>${project.build.directory}/jetty-ssl.keystore</keystore>
        <password>jetty6</password>
        <keyPassword>jetty6</keyPassword>
      </connector>
    </connectors>
  </configuration>
</plugin>

In the connectors element we have defined connectors for http listening on port 8080, and for https listening on port 8081. Under <keystore>, we reference the keystore file we have created with keytool. <password> and <keyPassword> define the password value.

To test this configuration we can invoke mvn jetty:run and open a web browser with address https://localhost:8081/simple-webapp/. We must not forget to use https for the protocol.

We generated the keystore by using the keytool command from the Java Development Kit. But there is a Maven plugin that does the same thing, but we can define all arguments for keytool in our POM. When we run mvn keytool:generateKeyPair, the keystore is generated and with mvn keytool:clean we can remove the keystore again. If we want to attach the creation of the keystore to the Maven generate-resources phase we must first make sure we invoke keytool:clean otherwise we get an error from keytool that the specified alias already exists. So we can add the following to our POM:

<plugin>
  <groupId>org.codehaus.mojo</groupId>
  <artifactId>keytool-maven-plugin</artifactId>
  <executions>
    <execution>
      <phase>generate-resources</phase>
      <id>clean</id>
      <goals>
        <goal>clean</goal>
      </goals>
    </execution>
    <execution>
      <phase>generate-resources</phase>
      <id>generateKeyPair</id>
      <goals>
        <goal>generateKeyPair</goal>
      </goals>
    </execution>
  </executions>
  <configuration>
    <keystore>${project.build.directory}/jetty-ssl.keystore</keystore>
    <dname>CN=BioCreative, OU=NLM, O=NIH, L=Bethesda, ST=DC</dname>
    <keypass>jetty6</keypass>
    <storepass>jetty6</storepass>
    <alias>jetty6</alias>
    <keyalg>RSA</keyalg>
  </configuration>
</plugin>

Now we can invoke mvn jetty:run and the keystore is automatically generated and used by the Jetty plugin.

Configuring Tomcat

Tomcat Authentication

To deploy the WAR file into Tomcat is a bit difficult then Jetty. First we need to add an user with roles manager-gui and manager-script. In %TOMCAT_PATH%/conf/tomcat-users.xml

<tomcat-users>
  <role rolename="manager-gui"/>
  <role rolename="manager-script"/>
  <user username="admin" password="password" 
    roles="manager-gui,manager-script" />
</tomcat-users>

Maven Authentication

Add above Tomcat’s user in the Maven setting file %MAVEN_PATH%/conf/settings.xml, later Maven will use this user to login Tomcat server.

<settings ...>
  <servers>
    <server>
      <id>TomcatServer</id>
      <username>admin</username>
      <password>password</password>
    </server>
  </servers>
</settings>

Tomcat Maven Plugin

Declares a Maven Tomcat plugin in pom.xml

<plugin>
  <groupId>org.apache.tomcat.maven</groupId>
  <artifactId>tomcat7-maven-plugin</artifactId>
  <version>2.2</version>
  <configuration>
    <url>http://localhost:8080/</url>
    <server>TomcatServer</server>
    <path>/biocreative</path>
  </configuration>
</plugin>

During deployment, it tells Maven to deploy the WAR file to Tomcat server via http://localhost:8080/, on path /biocreative, using “TomcatServer” (in settings.xml) username and password for authentication.

Deploy to Tomcat

Commands to manipulate WAR file on Tomcat.

  • mvn tomcat7:deploy
  • mvn tomcat7:undeploy
  • mvn tomcat7:redeploy

For example,

mvn tomcat7:deploy

Tomcat 6 Example

If you use Tomcat 6, change the plugin artifactId in pom.xml to tomcat6-maven-plugin. The commend line could be

  • mvn tomcat6:deploy
  • mvn tomcat6:undeploy
  • mvn tomcat6:redeploy

Yifan Peng

Yifan is a senior CIS PhD student in University of Delaware. His main researches include relation extraction, sentence simplification, text mining and natural language processing. He does not consider himself of a Java geek, but all his projects are written in Java.
Subscribe
Notify of
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

1 Comment
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
Daniel Escasa
8 years ago

Coupl’a things…

First, looks like you’re using GNU/Linux, and doing work as root. As seasoned Linux users will tell you, that’s a bad idea. Recommended is to run as a user, then sudo — which is what I did. Well, after getting a few access denied errors.

Second, you have to modify the port in pom.xml before you attempt to open the page at port 8080.

Same with the page at port 8081.

Other than that, it works well, and I’ve filed this for future reference :)

Back to top button