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
- Maven Compiler plugin to target Java version (JDK 8 in this article)
- Java Servlet dependency.
- 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
Reference: | How to create a Web Application Project with Java, Maven, Jetty from our JCG partner Yifan Peng at the PGuru blog. |
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 :)