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"