Jetty-maven-plugin: Running a webapp with a DataSource and security
This post describes how to configure the jetty-maven-plugin and the Jetty servlet container to run a web application that uses a data source and requires users to log in, which are the basic requirements of most web applications. I use Jetty in development because it's fast and easy to work with.
and to be able to immediatelly log into and interact with the application.
However it should be noted that Jetty isn't a full-featured JavaEE server and thus may not be always usable.
As you can see, I'm using Jetty 6.1.0.
Next we need to describe the DataSource to Jetty. There are multiple ways to do that, I've chosen to do so in src/main/webapp/WEB-INF/jetty-env.xml:
Notice that the class used is DB2SimpleDataSource and not a JDBC driver. That is, of course, because we need a DataSource, not a Driver. The Jetty wiki pages also contain examples of DataSource configuration for other DBs.
Finally we must make the corresponding JDBC implementation available to Jetty by adding it to the plugin's dependencies in the pom.xml:
Please do not scorn me for using system-scoped dependencies ;-), sometimes that is unfortunatelly the most feasible way.
Beware that Jetty doesn't support HTTPS out of the box and thus if you will add the data constraint CONFIDENTIAL to any resource, you will automatically get HTTP 403 FORBIDDEN no matter what you do. That's why I've commented it out above. It is possible to enable SSL in Jetty but I didn't want to bother with certificate generation etc.
Next we need to tell Jetty how to authenticate users. This is done via realms and we will use the simplest, file-based one. Again there are multiple ways to configure it, for example in the pom.xml:
The name must match exactly the realm-name in web.xml. You then define the users and their passwords and roles in the declared file, in this case in src/test/resources/jetty-users.properties:
The format of the file is username=password[,role1,role2,...].
When you download Jetty, you will find a fine example of using JAAS with a file-based back-end for authentication and authorization under examples/test-jaas-webapp (invoke mvn jetty:run from the folder and go to http://localhost:8080/jetty-test-jaas/). However it seems that JAAS causes an additional overhead visible as a few-seconds delay when starting the server so it might be preferrable not to use it.
When troubleshooting, you may want to tell Jetty to log at the debug level with mvn -DDEBUG .. or to log requests, which can be also configured in the jetty-env.xml.
Beware that this post describes configuration for Jetty 6.1.0. It can be different in other versions and it certainly is different in Jetty 7.
Why Jetty?
Well, because it's much faster then the Websphere AS I normally use and it really well supports fast (or shall I say agile? :-)) development thanks to its fast turnaround. And because it's simply cool to type
bash$ svn checkout http://example.com/repo/trunk/mywebapp
bash$ cd mywebapp
bash$ mvn jetty:run
bash$ firefox http://localhost:8080/mywebapp
and to be able to immediatelly log into and interact with the application.
However it should be noted that Jetty isn't a full-featured JavaEE server and thus may not be always usable.
Project setup
General configuration
You need to add the Jetty plugin to your pom.xml:
<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.example</groupId>
<artifactId>mywebapp</artifactId>
<packaging>war</packaging>
...
<build>
...
<plugins>
<plugin>
<groupId>org.mortbay.jetty</groupId>
<artifactId>maven-jetty-plugin</artifactId>
<version>6.1.0</version>
<configuration>
<scanIntervalSeconds>3</scanIntervalSeconds>
...
</configuration>
...
</plugin>
...
</plugins>
</build>
</project>
As you can see, I'm using Jetty 6.1.0.
Defining a DataSource
Let's assume that the application uses a DataSource configured at the server and accesses it normally via JNDI. Then we must define a reference to the data source in src/main/webapp/WEB-INF/web.xml:
<?xml version="1.0" encoding="UTF-8"?>
<web-app id="WebApp_ID" version="2.4"
xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">
...
<servlet>...</servlet>
...
<resource-ref>
<res-ref-name>jdbc/LMSDB</res-ref-name>
<res-type>javax.sql.DataSource</res-type>
<res-auth>Container</res-auth>
<res-sharing-scope>Shareable</res-sharing-scope>
</resource-ref>
</web-app>
Next we need to describe the DataSource to Jetty. There are multiple ways to do that, I've chosen to do so in src/main/webapp/WEB-INF/jetty-env.xml:
<?xml version="1.0"?>
<!DOCTYPE Configure PUBLIC "-//Mort Bay Consulting//DTD Configure//EN" "http://jetty.mortbay.org/configure.dtd">
<Configure class="org.mortbay.jetty.webapp.WebAppContext">
<New id="LMSDB" class="org.mortbay.jetty.plus.naming.Resource">
<Arg>jdbc/LMSDB</Arg>
<Arg>
<New class="com.ibm.db2.jcc.DB2SimpleDataSource">
<Set name="DatabaseName">LMSDB</Set>
<Set name="User">myUser</Set>
<Set name="Password">secret</Set>
<Set name="ServerName">db.toronto.ca.ibm.com</Set>
<Set name="PortNumber">3711</Set>
</New>
</Arg>
</New>
</Configure>
Notice that the class used is DB2SimpleDataSource and not a JDBC driver. That is, of course, because we need a DataSource, not a Driver. The Jetty wiki pages also contain examples of DataSource configuration for other DBs.
Finally we must make the corresponding JDBC implementation available to Jetty by adding it to the plugin's dependencies in the pom.xml:
<plugin>
<groupId>org.mortbay.jetty</groupId>
<artifactId>maven-jetty-plugin</artifactId>
<version>6.1.0</version>
<configuration>
<...
</configuration>
<dependencies>
<dependency>
<groupId>com.ibm.db2</groupId>
<artifactId>db2jcc</artifactId>
<version>9.7</version>
<type>jar</type>
<scope>system</scope>
<systemPath>${basedir}/../lms.sharedlibraries/db2/db2jcc.jar</systemPath>
</dependency>
<dependency>
<groupId>com.ibm.db2</groupId>
<artifactId>db2jcc_license_cisuz</artifactId>
<version>9.7</version>
<type>jar</type>
<scope>system</scope>
<systemPath>${basedir}/../lms.sharedlibraries/db2/db2jcc_license_cisuz.jar</systemPath>
</dependency>
</dependencies>
</plugin>
Please do not scorn me for using system-scoped dependencies ;-), sometimes that is unfortunatelly the most feasible way.
Enabling security and configuring an authentication mechanism
We would like to limit access to the application only to the authenticated users in the ADMIN role with the exception of pages under public/. Therefore we declare the appropriate security constraints in web.xml:
...
<security-constraint>
<display-name>authorizedUsers</display-name>
<web-resource-collection>
<web-resource-name>ALL URLs</web-resource-name>
<url-pattern>/*</url-pattern>
</web-resource-collection>
<auth-constraint>
<role-name>ADMIN</role-name>
</auth-constraint>
<!--user-data-constraint>
<transport-guarantee>CONFIDENTIAL</transport-guarantee>
</user-data-constraint-->
</security-constraint>
<security-constraint>
<display-name>publicAccess</display-name>
<web-resource-collection>
<web-resource-name>Public pages</web-resource-name>
<url-pattern>/public/*</url-pattern>
</web-resource-collection>
</security-constraint>
<login-config>
<auth-method>BASIC</auth-method>
<realm-name>Learning@IBM Mini Person Feed Management</realm-name>
</login-config>
<security-role>
<description>administrator access</description>
<role-name>ADMIN</role-name>
</security-role>
...
Beware that Jetty doesn't support HTTPS out of the box and thus if you will add the data constraint CONFIDENTIAL to any resource, you will automatically get HTTP 403 FORBIDDEN no matter what you do. That's why I've commented it out above. It is possible to enable SSL in Jetty but I didn't want to bother with certificate generation etc.
Next we need to tell Jetty how to authenticate users. This is done via realms and we will use the simplest, file-based one. Again there are multiple ways to configure it, for example in the pom.xml:
<plugin>
<groupId>org.mortbay.jetty</groupId>
<artifactId>maven-jetty-plugin</artifactId>
<version>6.1.0</version>
<configuration>
<scanIntervalSeconds>3</scanIntervalSeconds>
<userRealms>
<userRealm implementation="org.mortbay.jetty.security.HashUserRealm">
<name>Learning@IBM Mini Person Feed Management</name>
<config>src/test/resources/jetty-users.properties</config>
</userRealm>
</userRealms>
</configuration>
<dependencies>...</dependencies>
</plugin>
The name must match exactly the realm-name in web.xml. You then define the users and their passwords and roles in the declared file, in this case in src/test/resources/jetty-users.properties:
user=psw,ADMIN
The format of the file is username=password[,role1,role2,...].
When you download Jetty, you will find a fine example of using JAAS with a file-based back-end for authentication and authorization under examples/test-jaas-webapp (invoke mvn jetty:run from the folder and go to http://localhost:8080/jetty-test-jaas/). However it seems that JAAS causes an additional overhead visible as a few-seconds delay when starting the server so it might be preferrable not to use it.
Conclusion
With Jetty it's easy to enable security and create a data source, which are the basic requirements of most web applications. Anybody can then very easily run the application to test and develop it. Development is where Jetty really shines provided that you don't need any feature it doesn't have.When troubleshooting, you may want to tell Jetty to log at the debug level with mvn -DDEBUG .. or to log requests, which can be also configured in the jetty-env.xml.
Beware that this post describes configuration for Jetty 6.1.0. It can be different in other versions and it certainly is different in Jetty 7.