Monday, December 13, 2010

A Jersey REST Example with Maven

I finally got around to trying out Jersey for REST services.  Below is an end-to-end example that will build a war file that can be deployed into a Tomcat server.  I'm using Java 1.6 and Tomcat 6.0.20.

Assumption: I will assume you can create and compile a project with Apache Maven.  I used Maven 2.2.1 below.

Installing: Jersey doesn't have to be installed in a conventional sense.  You just need to have the correct jars in your web application's $CLASSPATH.  Maven will automate this for you.  Here is your POM file:

<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>yourGroup</groupId>
  <artifactId>yourExample</artifactId>
  <packaging>war</packaging>
  <version>1.0</version>
  <name>${artifactId}</name>
<repositories>
<!--These are needed for Jersey -->
<repository>
<id>maven2-repository.dev.java.net</id>
<name>Java.net Repository for Maven</name>
<url>http://download.java.net/maven/2/</url>
<layout>default</layout>
</repository> 
<repository>
<id>maven-repository.dev.java.net</id>
<name>Java.net Maven 1 Repository (legacy)</name>
<url>http://download.java.net/maven/1</url>
<layout>legacy</layout>
</repository>
  </repositories>
  <dependencies>
<!-- These are the Jersey dependencies -->
<dependency>
<groupId>com.sun.jersey</groupId>
<artifactId>jersey-server</artifactId>
<version>1.4</version>
</dependency>
<dependency>
<groupId>com.sun.grizzly</groupId>
<artifactId>grizzly-servlet-webserver</artifactId>
<version>1.9.18-i</version>
</dependency>
<dependency>
<groupId>com.sun.jersey.jersey-test-framework</groupId>
<artifactId>jersey-test-framework-grizzly</artifactId>
<version>1.4</version>
<scope>test</scope>
</dependency>
  </dependencies>
</project>


Configuring: There seem to be a few options for your web.xml file.  The following worked for me.  You have to change the line in bold.
<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE web-app
    PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.2//EN"
    "http://java.sun.com/j2ee/dtds/web-app_2.2.dtd">
<web-app>
  <servlet>
<servlet-name>Jersey Web Application</servlet-name>
<servlet-class>com.sun.jersey.spi.container.servlet.ServletContainer</servlet-class>
    <init-param>
      <param-name>com.sun.jersey.config.property.packages</param-name>
      <--You need to change this line to match your package name -->
      <param-value>org.yourproject.restservices</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
  </servlet>
  <servlet-mapping>
<servlet-name>Jersey Web Application</servlet-name>
<url-pattern>/*</url-pattern>
  </servlet-mapping>
</web-app>


The Code: The following is a simple Java code that takes supports both GET and POST operations with arbitrary key-value pairs.  It doesn't however support parameters with multiple values.  I show a sample code snippet below.  From other examples that I've seen, the UriInfo class seems to be the easiest way to get a Map of the request parameters with GET methods.  With POST, you have more direct access to the map.
//This package declaration should match what you used in the web.xml above.
package org.yourproject.restservices;
//These are Jersey jars
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.Produces;
import javax.ws.rs.Consumes;
import javax.ws.rs.Path;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.MultivaluedMap;
import javax.ws.rs.core.UriInfo;

import java.net.URLEncoder;
import java.util.Iterator;
@Path("/echo") public class MyService{
   @GET 
   @Produces("text/html") 
   public String get(@Context UriInfo ui) {
      MultivaluedMap queryParams=ui.getQueryParameters();
      return showQueryParams(queryParams);   
   }
 
   @POST
   @Consumes("application/x-www-form-urlencoded")
   @Produces("text/html") 
   public String post(MultivaluedMap queryParams) {
return showQueryParams(queryParams);  
   }  
   
   //Extract the parameters.  
   protected String showQueryParams(MultivaluedMap queryParams) {
Iterator it=queryParams.keySet().iterator();
String theKey=null;
String returnString="";
try {
      while(it.hasNext()) {
      theKey=(String)it.next();
      returnString += URLEncoder.encode(theKey,"UTF8")
                 +"  "
                 +URLEncoder.encode((String)queryParams.getFirst(theKey),"UTF-8");
   }
}
catch (Exception ex) { 
   //This will probably be an UnsupportedEncodingException.
           //Handle more approrpriately. 
   ex.printStackTrace();
           returnString=ex.getMessage();
}
return returnString;
   }


}

Compile and Invoke: Use "mvn clean install" to generate a war file, then copy this from your project's target directory to a waiting Tomcat server.  Better yet, add the clean up and installation code to your POM using Ant tasks.  

You can invoke this service any number of ways: directly call it in a web browser, write an HTML page that uses it as a FORM action, or invoke on the command line with curl or wget.  Here is a curl example that uses POST:

curl http://your.server.org:8080/yourExample/echo -d "hollow=world&true=false"


5 comments:

Clint Wilde said...

Great tutorial. Thanks alot.

Pradeep Kumar TR said...

This was really helpful. Thanks.

Rodrigo HJORT said...

Excellent tutorial, Marlon! Really helped me out.

Andrea said...

Great! thanks for the help!

Unknown said...

no real reason to use "mvn clean install" ... install puts the artifact in your repository. Unless you really want it there just use "mvn clean package"