RESTful Web Services Developer's Guide

Chapter 5 Jersey Sample Applications

This chapter discusses some sample applications that demonstrate how to create and use the Jersey annotations in your application. When you install the Jersey add-on to GlassFish, these samples are installed into the as-install/jersey/samples directory. If you have installed Jersey in another way, read the section Installing and Running the Jersey Sample Applications for information on how to download the sample applications.

The Jersey Sample Applications

All of the Jersey sample applications can be executed from the command line or from NetBeans IDE 6.5 using at least Maven version 2.0.9. Maven makes it easy to distribute the samples without redistributing third-party dependencies, which can be very large.

There are three samples included in the tutorial that demonstrate how to create and use resources. They are:

Configuring Your Environment

To run the sample applications, you must install Jersey onto either GlassFish v3 Prelude or NetBeans IDE 6.5, and you need to install and configure Maven. Here are the links to more information on these topics.

The HelloWorld-WebApp Application

This section discusses the HelloWorld-WebApp application that ships with Jersey. The HelloWorld-WebApp application is a “Hello, world” application that demonstrates the basics of developing a resource. There is a single class, HelloWorldResource that contains one method, getClichedMessage that produces a textual response to an HTTP GET request with a greeting that is sent back as plain text.

Annotating the Resource Class

The following code is the contents of the com.sun.jersey.samples.helloworld.resources.HelloWorldResource class:

import javax.ws.rs.GET;
import javax.ws.rs.Produces;
import javax.ws.rs.Path;

@Path("/helloworld")
public class HelloWorldResource {

		@GET
		@Produces("text/plain")
		public String getClichedMessage() {
			return "Hello World";
	}
}

In this example, the following annotations are processed at runtime:

Configuring the Resource with the Runtime

The helloworld-webapp/src/main/webapp/WEB-INF/web.xml deployment descriptor for HelloWorld-Webappcontains the settings for configuring your resource with the JAX-RS API runtime:

<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>
					<param-value>
							com.sun.jersey.samples.helloworld.resources
					</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>

The com.sun.jersey.spi.container.servlet.ServletContainer servlet is part of the JAX-RS API runtime, and works with the generated HelloWorldResource class to get the resources provided by your application. The <servlet-mapping> elements specify which URLs your application responds to relative to the context root of your WAR. Both the context root and the specified URL pattern prefix the URI Template specified in the @Path annotation in the resource class file. In the case of the HelloWorld-WebApp sample application, @Path is set to /helloworld, the URL pattern is set to the wild card character, and the context root specified in sun-web.xml is /helloworld-webapp, so the resource will respond to requests of the form:


http://<server>:<server port>/helloworld-webapp/helloworld

ProcedureBuilding and Running the HelloWorld-WebApp Application in NetBeans

  1. Make sure that Maven is installed and configured, as described in Running the Jersey Examples.

  2. Select File->Open Project in NetBeans IDE 6.5.

  3. Navigate to jersey/samples, select HelloWorld-WebApp, and click OK.

  4. Right click the HelloWorld-WebApp project in the Projects pane and select Run.

    This will generate the helper classes and artifacts for your resource, compile the classes, package the files into a WAR file, and deploy the WAR to your GlassFish instance.

  5. If a web browser doesn't open automatically to display the output, you may need to open a web browser and enter the URL for the application.


    http://localhost:8080/helloworld-webapp/helloworld

    You will see the following output in your web browser:


    Hello World

ProcedureBuilding and Running the HelloWorld-WebApp Application with Maven

  1. Make sure that Maven is installed and configured, as described in Running the Jersey Examples.

  2. Open a terminal prompt and navigate to jersey.home/samples/HelloWorld-WebApp.

  3. Enter mvn glassfish:run and press Enter.

    This will build, package, deploy, and run the web application. It will also start GlassFish if it is not running.

  4. In a web browser navigate to:


    http://localhost:8080/helloworld-webapp/helloworld

    You will see the following output in your web browser:


    Hello World

The Storage-Service Application

The Storage-Service sample application demonstrates a simple, in-memory, web storage service and test code using the Jersey client API. The web storage service enables clients to create and delete containers. Containers are used to create, read, update, and delete items of arbitrary content, and to search for items containing certain content. A container can be thought of as a hash map of items. The key for the item is specified in the request URI. There are three web resources that are shown below.

The web storage service ensures that content can be cached (by browsers and proxies) by supporting the HTTP features Last-Modified and ETag. The sample consists of three web resources implemented by the classes described in the following paragraphs.


Note –

Two of the resource classes have similar names. ContainersResource (plural) and ContainerResource (singular).


Understanding the Web Resource Classes for the Storage-Service Example

The first resource class, com.sun.jersey.samples.storageservice.resources.ContainersResource, provides metadata information on the containers. This resource references the ContainerResource resource using the @Path annotation declared on the ContainersResource.getContainerResource method. The following code is extracted from ContainersResource.java:

@Path("/containers")
@Produces("application/xml")
public class ContainersResource {
    @Context UriInfo uriInfo;
    @Context Request request;
    
    @Path("{container}")
    public ContainerResource getContainerResource(@PathParam("container")
        String container) {
        return new ContainerResource(uriInfo, request, container);
    }
    
    @GET
    public Containers getContainers() {
        System.out.println("GET CONTAINERS");
        
        return MemoryStore.MS.getContainers();
    }    
}

Another resource class , com.sun.jersey.samples.storageservice.resources.ContainerResource, provides for reading, creating, and deleting of containers. You can search for items in the container using a URI query parameter. The resource dynamically references the ItemResource resource class using the getItemResource method that is annotated with @Path. The following code is extracted from ContainerResource.java:

@Produces("application/xml")
public class ContainerResource {
    @Context UriInfo uriInfo;
    @Context Request request;
    String container;
    
    ContainerResource(UriInfo uriInfo, Request request, String container) {
        this.uriInfo = uriInfo;
        this.request = request;
        this.container = container;
    }
    
    @GET
    public Container getContainer(@QueryParam("search") String search) {
        System.out.println("GET CONTAINER " + container + ", search = " + search);

        Container c = MemoryStore.MS.getContainer(container);
        if (c == null)
            throw new NotFoundException("Container not found");
        
        
        if (search != null) {
            c = c.clone();
            Iterator<Item> i = c.getItem().iterator();
            byte[] searchBytes = search.getBytes();
            while (i.hasNext()) {
                if (!match(searchBytes, container, i.next().getName()))
                    i.remove();
            }
        }
        
        return c;
    }    

    @PUT
    public Response putContainer() {
        System.out.println("PUT CONTAINER " + container);
        
        URI uri =  uriInfo.getAbsolutePath();
        Container c = new Container(container, uri.toString());
        
        Response r;
        if (!MemoryStore.MS.hasContainer(c)) {
            r = Response.created(uri).build();
        } else {
            r = Response.noContent().build();
        }
        
        MemoryStore.MS.createContainer(c);
        return r;
    }
    
    @DELETE
    public void deleteContainer() {
        System.out.println("DELETE CONTAINER " + container);
        
        Container c = MemoryStore.MS.deleteContainer(container);
        if (c == null)
            throw new NotFoundException("Container not found");
    } 
    
    
    @Path("{item: .+}")
    public ItemResource getItemResource(@PathParam("item") String item) {
        return new ItemResource(uriInfo, request, container, item);
    }
    
    private boolean match(byte[] search, String container, String item) {
        byte[] b = MemoryStore.MS.getItemData(container, item);
        
        OUTER: for (int i = 0; i < b.length - search.length; i++) {
             for (int j = 0; j < search.length; j++) {
                if (b[i + j] != search[j])
                    continue OUTER;
            }
            
            return true;
        }
        
        return false;
    }
}

The last resource class discussed in this sample, com.sun.jersey.samples.storageservice.resources.ItemResource, provides for reading, creating, updating, and deleting of an item. The last modified time and entity tag are supported in that it conditionally returns content only if the browser or proxy has an older version of the content. The following code is extracted from ItemResource.java:

public class ItemResource {
    UriInfo uriInfo;
    Request request;
    String container;
    String item;
    
    public ItemResource(UriInfo uriInfo, Request request,
            String container, String item) {
        this.uriInfo = uriInfo;
        this.request = request;
        this.container = container;
        this.item = item;
    }
    
    @GET
    public Response getItem() {
        System.out.println("GET ITEM " + container + " " + item);
        
        Item i = MemoryStore.MS.getItem(container, item);
        if (i == null)
            throw new NotFoundException("Item not found");
        Date lastModified = i.getLastModified().getTime();
        EntityTag et = new EntityTag(i.getDigest());
        ResponseBuilder rb = request.evaluatePreconditions(lastModified, et);
        if (rb != null)
            return rb.build();
            
        byte[] b = MemoryStore.MS.getItemData(container, item);
        return Response.ok(b, i.getMimeType()).
                lastModified(lastModified).tag(et).build();
    }    
    
    @PUT
    public Response putItem(
            @Context HttpHeaders headers,
            byte[] data) {
        System.out.println("PUT ITEM " + container + " " + item);
        
        URI uri = uriInfo.getAbsolutePath();
        MediaType mimeType = headers.getMediaType();
        GregorianCalendar gc = new GregorianCalendar();
        gc.set(GregorianCalendar.MILLISECOND, 0);
        Item i = new Item(item, uri.toString(), mimeType.toString(), gc);
        String digest = computeDigest(data);
        i.setDigest(digest);
        
        Response r;
        if (!MemoryStore.MS.hasItem(container, item)) {
            r = Response.created(uri).build();
        } else {
            r = Response.noContent().build();
        }
        
        Item ii = MemoryStore.MS.createOrUpdateItem(container, i, data);
        if (ii == null) {
            // Create the container if one has not been created
            URI containerUri = uriInfo.getAbsolutePathBuilder().path("..").
                    build().normalize();
            Container c = new Container(container, containerUri.toString());
            MemoryStore.MS.createContainer(c);
            i = MemoryStore.MS.createOrUpdateItem(container, i, data);
            if (i == null)
                throw new NotFoundException("Container not found");
        }
        
        return r;
    }    
    
    @DELETE
    public void deleteItem() {
        System.out.println("DELETE ITEM " + container + " " + item);
        
        Item i = MemoryStore.MS.deleteItem(container, item);
        if (i == null) {
            throw new NotFoundException("Item not found");
        }
    }
    
    
    private String computeDigest(byte[] content) {
        try {
            MessageDigest md = MessageDigest.getInstance("SHA");
            byte[] digest = md.digest(content);
            BigInteger bi = new BigInteger(digest);
            return bi.toString(16);
        } catch (Exception e) {
            return "";
        }
    }
}

The mapping of the URI path space is shown in the following table.

Table 5–1 URI Path Space for Storage-Service Example

URI Path 

Resource Class 

HTTP Methods 

/containers 

ContainersResource

GET 

/containers/{container} 

ContainerResource

GET, PUT, DELETE 

/containers/{container}/{item} 

ItemResource

GET, PUT, DELETE 

ProcedureBuilding and Running the Storage-Service Application from the Command Line

  1. Make sure that Maven is installed and configured, as described in Running the Jersey Examples.

  2. Open a terminal prompt and navigate to jersey.home/samples/storage-service.

  3. To run the sample, enter mvn clean compile exec:java and press Enter.

    This will build, package, and deploy the web storage service using Grizzly, an HTTP web server.


    Tip –

    To run the application on GlassFish, copy the classes from the example into sources of the web application. Then, create a web.xml file that uses the Jersey servlet. The Java classes are not dependent on a particular container.


  4. To view the Web Application Description Language file (the WADL), open a web browser and navigate to:


    http://localhost:9998/storage/application.wadl

    The WADL description in also shown in this document at Example: The Storage-Service WADL

  5. Leave the terminal window open, and open a new terminal window to continue with exploring this sample.

ProcedureExploring the Storage-Service Example

Once the service is deployed, it is a good idea to see how it works by looking at the contents of the containers, creating a container, adding content to the container, looking at the date and time stamp for the items in the container, searching for content in the containers, modifying content of an item in the containers, deleting an item from the container, and deleting the container. To accomplish these tasks, use the cURL command line tool, as shown in the following steps. cURL is a command line tool for transferring files with URL syntax.

  1. To show which containers currently exist, open a new terminal prompt and enter the following command. This command will henceforth be referred to as the GET command.

    curl http://127.0.0.1:9998/storage/containers

    The response is in XML format. Because there are no containers present, the response is an empty containers element.

  2. To create a container, enter the following at the terminal prompt:

    curl -X PUT http://127.0.0.1:9998/storage/containers/quotes

    This step creates a container called quotes. If you run the GET command from the previous step again, it will return information in XML format showing that a container named quotes exists at the URI http://127.0.0.1:9998/storage/containers/quotes.

  3. Create some content in the quotes container. The following example shows how to do this from the terminal prompt:

    curl -X PUT -HContent-type:text/plain --data 
    			"Something is rotten in the state of Denmark"  
    			http://127.0.0.1:9998/storage/containers/quotes/1
    curl -X PUT -HContent-type:text/plain --data 
    			"I could be bounded in a nutshell" 
    			http://127.0.0.1:9998/storage/containers/quotes/2
    curl -X PUT -HContent-type:text/plain --data 
    			"catch the conscience of the king" 
    			http://127.0.0.1:9998/storage/containers/quotes/3 
    curl -X PUT -HContent-type:text/plain --data 
    			"Get thee to a nunnery" 
    			http://127.0.0.1:9998/storage/containers/quotes/4 

    If you run the GET command again with /quotes at the end (curl http://127.0.0.1:9998/storage/containers/quotes), it will return information in XML format that show that the quotes container contains 4 items. For each item, the following information is displayed: digest, date last modified, MIME type, item name (also referred to as its key), and URI address. For example, the listing for the first item looks like this:

        <item>
            <digest>7a54c57975de11bffcda5bc6bd92a0460d17ad03</digest>
            <lastModified>2008-10-10T15:011:48+01:00</lastModified>
            <mimeType>text/plain</mimeType>
            <name>1</name>
            <uri>http://127.0.0.1:9998/storage/containers/quotes/1</uri>
        </item>
  4. You can search for information within the contents of the quotes container. For example, the following command would search for the String king.

    curl "http://127.0.0.1:9998/storage/containers/quotes?search=king"

    This returns the information for the item that contains the text, similar to that shown in the previous step, but not the text itself. The next step shows how to see the contents of the item containing the text king.

  5. To get the contents of the item containing the text you found using the search, use the following command:

    curl  http://127.0.0.1:9998/storage/containers/quotes/3

    This step returns the contents of item 3, which is the quote catch the conscience of the king.

  6. To delete an item from the container, use the following command:

    curl -X DELETE http://127.0.0.1:9998/storage/containers/quotes/3
  7. To delete an item from the container, use the following command:

    curl -X DELETE http://127.0.0.1:9998/storage/containers/quotes/3

    You can use the GET command to verify that item 3 has been removed.

  8. To delete the entire quotes container, use the following command:

    curl -X DELETE http://127.0.0.1:9998/storage/containers/quotes

    You can use the GET command to verify that the container has been removed.

  9. For a discussion of the caching of HTTP requests and responses, look at the as-install/jersey/samples/Storage-Service/README.html file.

    If you go back to the terminal window that is running the web storage service, you will see the history of HTTP requests and responses, which will look something like this:

    GET CONTAINERS
    PUT CONTAINER quotes
    GET CONTAINERS
    PUT ITEM quotes 1
    PUT ITEM quotes 2
    PUT ITEM quotes 3
    PUT ITEM quotes 4
    GET CONTAINER quotes, search = null
    PUT ITEM quotes 4
    PUT ITEM quotes 4
    GET CONTAINER quotes, search = king
    DELETE ITEM quotes 3
    GET CONTAINER quotes, search = null
    DELETE CONTAINER quotes
    GET CONTAINER quotes, search = null
    GET CONTAINERS 

Extending the Storage-Service Example

This example demonstrates storing and returning plain text strings. This example can easily be modified to store and return arbitrary binary data, for example, images. You can easily store any piece of data with any media type. All you have to do is, in the examples in the previous section, change the text/plain parameter to image/jpeg, or some other value, and point to a JPEG file. For example, to create some content that is a JPEG file, you could use the following curl command.

curl -X PUT -HContent-type:image/jpeg --data 
			/home/jersey_logo.jpg  
			http://127.0.0.1:9998/storage/containers/images/1

To retrieve the contents of the item containing the image, use the following command:

curl  http://127.0.0.1:9998/storage/containers/images/1

Example: The Storage-Service WADL

This is the Web Application Description Language file (the WADL) that is generated for the storage-service sample application:

<?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: 1.0-ea-SNAPSHOT 10/02/2008 12:17 PM"/>
    <resources base="http://localhost:9998/storage/">
        <resource path="/containers">
            <method name="GET" id="getContainers">
                <response>
                    <representation mediaType="application/xml"/>
                </response>
            </method>
            <resource path="{container}">
                <param xmlns:xs="http://www.w3.org/2001/XMLSchema" 
									type="xs:string" style="template" name="container"/>
                <method name="PUT" id="putContainer">
                    <response>
                        <representation mediaType="application/xml"/>
                    </response>
                </method>
                <method name="DELETE" id="deleteContainer"/>
                <method name="GET" id="getContainer">
                    <request>
                        <param xmlns:xs="http://www.w3.org/2001/XMLSchema" 
													type="xs:string" style="query" name="search"/>
                    </request>
                    <response>
                        <representation mediaType="application/xml"/>
                    </response>
                </method>
                <resource path="{item: .+}">
                    <param xmlns:xs="http://www.w3.org/2001/XMLSchema" 
											type="xs:string" style="template" name="item"/>
                    <method name="PUT" id="putItem">
                        <request>
                            <representation mediaType="*/*"/>
                        </request>
                        <response>
                            <representation mediaType="*/*"/>
                        </response>
                    </method>
                    <method name="DELETE" id="deleteItem"/>
                    <method name="GET" id="getItem">
                        <response>
                            <representation mediaType="*/*"/>
                        </response>
                    </method>
                </resource>
            </resource>
        </resource>
    </resources>
</application>

The Bookstore Application

The Bookstore web application shows how to connect JSP pages to resources. The Bookstore web application presents books, CDs, and tracks from CDs. The sample application consists of five web resources, described and outlined in the following section.

This sample demonstrates how to support polymorphism of resources and JSP pages, which means that it allows values of different data types to be handled using a uniform interface. The use of polymorphism makes it possible to add another resource, such as a DVD resource with associated JSP pages, which would extend Item without having to change the logic of Bookstore or any of the existing JSP pages.

Web Resources for Bookstore Application

The Bookstore resource returns a list of items, either CDs or books. The resource dynamically references a Book or CD resource using the getItem method that is annotated with @Path. Both the Book and CD resources inherit from the Item class, therefore, the item can be managed polymorphically.

The following snippet of code from com.sun.jersey.samples.bookstore.resources.Bookstore.java adds some items to the bookstore application and, using a URI path template to pass in the item ID, provides a method for retrieving the item.

@Path("/")
@Singleton
public class Bookstore {    
    private final Map<String, Item> items = new TreeMap<String, Item>();
    
    private String name;
    
    public Bookstore() {
        setName("Czech Bookstore");
        getItems().put("1", new Book("Svejk", "Jaroslav Hasek"));
        getItems().put("2", new Book("Krakatit", "Karel Capek"));
        getItems().put("3", new CD("Ma Vlast 1", "Bedrich Smetana", new Track[]{
            new Track("Vysehrad",180),
            new Track("Vltava",172),
            new Track("Sarka",32)}));
    }
    
    @Path("items/{itemid}/")
    public Item getItem(@PathParam("itemid") String itemid) {
        Item i = getItems().get(itemid);
        if (i == null)
            throw new NotFoundException("Item, " + itemid + ", is not found");
        
        return i;
    }
   . . . 
  }

Both the Book and the CD resources inherit from the Item class. This allows the resources to be managed polymorphically. The Book resource has a title and an author. The CD resource has a title, author, and list of tracks. The Track resource has a name and a track length.

The following code snippet from the CD resource dynamically references the Track resource using the getTrack method that is annotated with @Path.

public class CD extends Item {
    
    private final Track[] tracks;
    
    public CD(final String title, final String author, final Track[] tracks) {
        super(title, author);
        this.tracks = tracks;
    }
    
    public Track[] getTracks() {
        return tracks;
    }
    
    @Path("tracks/{num}/")
    public Track getTrack(@PathParam("num") int num) {
        if (num >= tracks.length)
            throw new NotFoundException("Track, " + num + ", 
            of CD, " + getTitle() + ", is not found");
        return tracks[num];
    }    
}

Mapping the URI Path in the Bookstore Example

JSP pages are associated with resource classes. These JSP pages are resolved by converting the fully-qualified class name of the resource class into a path and appending the last path segment of the request URI path to that path. For example, when a GET is performed on the URI path "/", the path to the JSP page is /com/sun/jersey/samples/bookstore/resources/Bookstore/. For this example, since the last path segment is empty, index.jsp is appended to the path. The request then gets forwarded to the JSP page at that path. Similarly, when a GET is performed on the URI path count, the path to the JSP page is /com/sun/jersey/samples/bookstore/resources/Bookstore/count.jsp.

The JSP variable it is automatically set to the instance of Bookstore so that the index.jsp, or count.jsp, has access to the Bookstore instance as a Java bean.

If a resource class inherits from another resource class, it will automatically inherit the JSP pages from the super class.

A JSP page may also include JSP pages using the inheritance mechanism. For example, the index.jsp page associated with the Book resource class includes a footer.jsp page whose location is specified by the super class, Item.

The mapping of the URI path space is shown in the following table.

URI Path 

Resource Class 

HTTP method 

Bookstore 

GET 

/count 

Bookstore 

GET 

/time 

Bookstore 

GET 

/items/{itemid} 

Book, CD 

GET 

/items/{itemid}/tracks/{num} 

Track 

GET 

Mapping the URI Paths and JSP Pages

The mapping of the URI paths and JSP pages is shown in the following table.

URI Path  

JSP Page 

/com/sun/jersey/samples/bookstore/resources/Bookstore/index.jsp 

/count 

/com/sun/jersey/samples/bookstore/resources/Bookstore/count.jsp 

/time 

/com/sun/jersey/samples/bookstore/resources/Bookstore/time.jsp 

/items/{itemid} 

/com/sun/jersey/samples/bookstore/resources/Book/index.jsp 

/items/{itemid} 

/com/sun/jersey/samples/bookstore/resources/CD/index.jsp 

/items/{itemid}/tracks/{num} 

/com/sun/jersey/samples/bookstore/resources/Track/index.jsp 

ProcedureBuilding and Running the Bookstore Application from a Terminal Window

  1. Open a terminal prompt and navigate to as-install/jersey/samples/bookstore.

  2. Enter mvn glassfish:run and press Enter.

    This will build, package, and deploy the project onto GlassFish using Maven.

  3. In a web browser navigate to:


    http://localhost:8080/Bookstore/

ProcedureBuilding and Running the Bookstore Application from NetBeans IDE

  1. Select File->Open Project, and browse to the Jersey Bookstore sample application directory to open the project.

  2. Right-click the project and select Properties. On the Actions tab, make sure that Use external Maven for build execution is checked. Also on the Actions tab, select Run Project under Actions, then change the Execute Goals field to package glassfish:run, and change the Set Properties field to netbeans.deploy=true. On the Run tab, make sure that the server is set to GlassFish v3 Prelude.

  3. Right-click the project and select Run.

    This will build, package, and deploy the project onto GlassFish using Maven.

  4. In a web browser navigate to:


    http://localhost:8080/Bookstore/

    When the sample is running, it looks like the following example:


    URL: http://locahlhost:8080/Bookstore/
    Czech Bookstore
    
    Item List
    
    	* Svejk
    	* Krakatit
    	* Ma Vlast 1
    
    Others
    
    count inventory
    get the system time
    regular resources
    	

Other Jersey Examples

For more sample applications that demonstrate the Jersey technology, look in the as-install/jersey/samples directory. The following list provides a brief description of each sample application.

This list contains sample applications that are not installed with Jersey, but which also demonstrate the Jersey technology.