O'Reilly logo

Java Web Services: Up and Running by Martin Kalin

Stay ahead with the world's most comprehensive technology and business learning platform.

With Safari, you learn the way you learn best. Get unlimited access to videos, live online training, learning paths, books, tutorials, and more.

Start Free Trial

No credit card required

JAX-RS: WADLing Through Jersey

Jersey is the centerpiece project for the recent JAX-RS (Java API for XML-RESTful Web Services). Jersey applications can be deployed through familiar commercial-grade containers such as standalone Tomcat and GlassFish, but Jersey also provides the lightweight Grizzly container that is well suited for learning the framework. Jersey works well with Maven. A deployed Jersey service automatically generates a WADL, which is then available through a standard GET request. A good place to start is https://jersey.dev.java.net.

A Jersey service adheres to REST principles. A service accepts the usual RESTful requests for CRUD operations specified with the standard HTTP verbs GET, POST, DELETE, and PUT. A request is targeted at a Jersey resource, which is a POJO. Here is the MsgResource class to illustrate:

package msg.resources; 

import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.FormParam;
import javax.ws.rs.Produces;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.DELETE;
import java.beans.XMLEncoder;
import java.io.ByteArrayOutputStream;

// This is the base path, which can be extended at the method level.
@Path("/")
public class MsgResource {
    private static String msg = "Hello, world!";

    @GET
    @Produces("text/plain")
    public String read() {
        return msg + "\n";
    }

    @GET
    @Produces("text/plain")
    @Path("{extra}")
    public String personalized_read(@PathParam("extra") String cus) {
        return this.msg + ": " + cus + "\n";
    }

    @POST
    @Produces("text/xml")
    public String create(@FormParam("msg") String new_msg ) {
        this.msg = new_msg;

        ByteArrayOutputStream stream = new ByteArrayOutputStream();
        XMLEncoder enc = new XMLEncoder(stream);
        enc.writeObject(new_msg);
        enc.close();
        return new String(stream.toByteArray()) + "\n";
    }

    @DELETE
    @Produces("text/plain")
    public String delete() {
        this.msg = null;
        return "Message deleted.\n";
    }
}

The class has intuitive annotations, including the ones for the HTTP verbs and the response MIME types. The @Path annotation right above the MsgResource class declaration is used to decouple the resource from any particular base URL. For example, the MsgResource might be available at the base URL http://foo.bar.org:1234, at the base URL http://localhost:9876, and so on. The @GET, @POST, and @DELETE annotations specify the appropriate HTTP verb for a particular service operation. The @Produces annotation specifies the MIME type of the response, in this case either text/plain for the GET and DELETE operations or text/xml for the POST operation. Each method annotated as a MsgResource is responsible for generating the declared response type.

The MsgResource class could be put in a WAR file along with the supporting Jersey JAR files and then deployed in a servlet container such as Tomcat. There is, however, a quick way to publish a Jersey service during development. Here is the publisher class to illustrate:

import com.sun.jersey.api.container.grizzly.GrizzlyWebContainerFactory;
import java.util.Map;
import java.util.HashMap;

class JerseyPublisher {
    public static void main(String[ ] args) {
        final String base_url = "http://localhost:9876/";
        final Map<String, String> config = new HashMap<String, String>();

        config.put("com.sun.jersey.config.property.packages",
                   "msg.resources"); // package with resource classes

        System.out.println("Grizzly starting on port 9876.\n" +
                           "Kill with Control-C.\n");
        try {
            GrizzlyWebContainerFactory.create(base_url, config);
        }
        catch(Exception e) { System.err.println(e); }
    }
}

Grizzly requires configuration information about the package, in this case named msg.resources, that contains the resources available to clients. In this example, the package houses only the single class MsgResource but could house several resources. On each incoming request, the Grizzly container surveys the available resources to determine which method should handle the request. RESTful routing is thus in effect. For example, a POSTed request is delegated only to a method annotated with @POST.

Compiling and executing the resource and the publisher requires that several Jersey JAR files be on the classpath. Here is the list of five under the current release:

asm-3.1.jar  
grizzly-servlet-webserver-1.8.3.jar  
jersey-core-0.9-ea.jar  
jersey-server-0.9-ea.jar  
jsr311-api-0.9.jar

All of these JAR files, together with others for a Maven-centric version of Jersey, are available at the Jersey home page cited earlier.

Once the JerseyPublisher has been started, a browser or a utility such as curl can be used to access the resource. For example, the curl command:

% curl http://localhost:9876/

issues a GET request against the service, which causes the @GET-annotated read method to execute. The response is the default message:

Hello, world! 

By contrast, the curl command:

% curl -d msg='Goodbye, cruel world!' http://localhost:9876/echo/fred

issues a POST request against the service, which in turn causes the @POST-annotated create method to execute. (A REST purist might argue that a PUT operation would be more appropriate here, as the create method arguably updates an existing message rather than creates a message.) The create method uses the @FormParam annotation so that the POSTed data are available as the method’s argument. The @FormParam parameter, in this case the string msg, need not be the same as the method parameter, in this case new_msg. The output is:

<?xml version="1.0" encoding="UTF-8"?>
<java version="1.6.0_06" class="java.beans.XMLDecoder">
 <string>Goodbye, cruel world!</string>
</java>      

because the @Produces annotation on the create method specifies text/xml as the response type. The method generates this response type with the XMLEncoder.

In addition to the read method, there is a second method, personalized_read, annotated with @GET. The method also has the annotation @Path("{extra}"). For example, the request:

% curl http://localhost:9876/bye

causes this method to be invoked with bye as the argument. The braces surrounding extra signal that extra is simply a placeholder rather than a literal. A method-level @Path is appended to the class-level @Path. In this example, the class-level @Path is simply /.

The Grizzly publisher automatically generates a WADL document, which is available at:

http://localhost:9876/application.wadl

Here is the automatically generated WADL for the MsgResource:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<application xmlns="http://research.sun.com/wadl/2006/10">
    <doc xmlns:jersey="http://jersey.dev.java.net/" 
         jersey:generatedBy="Jersey: 0.9-ea 08/22/2008 04:48 PM"/>
    <resources base="http://localhost:9876/">
        <resource path="/">
            <method name="DELETE" id="delete">
                <response>
                    <representation mediaType="text/plain"/>
                </response>
            </method>
            <method name="GET" id="read">
                <response>
                    <representation mediaType="text/plain"/>
                </response>
            </method>
            <method name="POST" id="create">
                <request>
                    <param xmlns:xs="http://www.w3.org/2001/XMLSchema" 
                           type="xs:string" name="msg"/>
                </request>
                <response>
                    <representation mediaType="text/xml"/>
                </response>
            </method>
            <resource path="{extra}">
                <param xmlns:xs="http://www.w3.org/2001/XMLSchema" 
                       type="xs:string" style="template" name="extra"/>
                <method name="GET" id="personalized_read">
                    <response>
                        <representation mediaType="text/plain"/>
                    </response>
                </method>
            </resource>
        </resource>
    </resources>
</application>

The WADL captures that the MsgResource supports two GET operations, a POST operation, and a DELETE operation. The WADL also describes the MIME type of the response representation for each operation. Of course, this WADL document could be used as input to the wadl2java utility.

Jersey is an appropriately lightweight framework that honors the spirit of RESTful services. The Grizzly publisher is attractive for development, automatically generating a WADL document to describe the published services. For production, the move to a web container, standalone or embedded in an application server, is straightforward because Jersey resources are simply annotated POJOs. The entire JSR-311 API, the Jersey core, comprises only 3 packages and roughly 50 interfaces and classes.

For now, JAX-WS and JAX-RS are separate frameworks. It would not be surprising if, in the future, the two frameworks merged.

With Safari, you learn the way you learn best. Get unlimited access to videos, live online training, learning paths, books, interactive tutorials, and more.

Start Free Trial

No credit card required