Storage Service Example

This example demonstrates a simple in-memory web storage service.

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 of an item is a URI path.

The web storage service ensures that content can be cached (by browsers and proxies) by supporting the HTTP features Last-Modified and ETag.

Contents

The example consists of three web resources implemented by the following:

com.sun.jersey.samples.storageservice.resources.ContainersResource
This Java class provides meta-data information on the containers. The resource references the ContainerResource resource using the Path annotation declared on the ContainersResource.getContainerResource method.
com.sun.jersey.samples.storageservice.resources.ContainerResource
This Java class provides read, create and delete of a container. Searching of items is supported using a URI query parameter. The resource dynamically references the ItemResource resource using the getItemResource method that is annotated with Path.
com.sun.jersey.samples.storageservice.resources.ItemResource
This Java class provides read, create, update, and delete of an item. The last modified time and entity tag are supported such that it conditionally returns content only if the browser or proxy has an older version of the content.

The mapping of the URI path space is presented in the following table:

URI path Resource class HTTP methods
/containers ContainersResource GET
/containers/{container} ContainerResource GET, PUT, DELETE
/containers/{container}/{item} ItemResource GET, PUT, DELETE

Running the Example

Run the example as follows:

mvn clean compile exec:java

This deploys the web storage service using Grizzly

A WADL description may be accessed at the URL:

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

Following steps are using cURL command line tool:

Get the containers:

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

This returns the following XML document:

<containers/>

No containers are present.

Create a container:

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

This creates a container called 'quotes'. Getting the containers:

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

returns information about the 'quotes' container:

<containers>
    <container>
        <name>quotes</name>
        <uri>http://127.0.0.1:9998/storage/containers/quotes</uri>
    </container>
</containers>

Create some content:

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 

The 'quotes' container has four items associated with keys 1, 2, 3, and 4 when GETing the quotes container:

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

it returns:

<container>
    <item>
        <digest>7a54c57975de11bffcda5bc6bd92a0460d17ad03</digest>
        <lastModified>2007-01-10T15:07:48+01:00</lastModified>
        <mimeType>text/plain</mimeType>
        <name>1</name>
        <uri>http://127.0.0.1:9998/storage/containers/quotes/1</uri>
    </item>
    <item>
        <digest>4769363fcf4d0513619c6a30724daab396a1d196</digest>
        <lastModified>2007-01-10T15:08:37+01:00</lastModified>
        <mimeType>text/plain</mimeType>
        <name>2</name>
        <uri>http://127.0.0.1:9998/storage/containers/quotes/2</uri>
    </item>
    <item>
        <digest>-71240529c42b5b92e3086e624618d19164658616</digest>
        <lastModified>2007-01-10T15:11:51+01:00</lastModified>
        <mimeType>text/plain</mimeType>
        <name>3</name>
        <uri>http://127.0.0.1:9998/storage/containers/quotes/3</uri>
    </item>
    <item>
        <digest>493388267529403c84628e12141ffe9a013205a4</digest>
        <lastModified>2007-01-10T15:12:08+01:00</lastModified>
        <mimeType>text/plain</mimeType>
        <name>4</name>
        <uri>http://127.0.0.1:9998/storage/containers/quotes/4</uri>
    </item>
    <name>quotes</name>
    <uri>http://127.0.0.1:9998/storage/containers/quotes</uri>
</container>

Search the container for all items containing the word 'king':

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

which returns the following XML document containing one item, 3:

<container>
    <item>
        <digest>-71240529c42b5b92e3086e624618d19164658616</digest>
        <lastModified>2007-01-10T15:11:51+01:00</lastModified>
        <mimeType>text/plain</mimeType>
        <name>3</name>
        <uri>http://127.0.0.1:9998/storage/containers/quotes/3</uri>
    </item>
    <name>quotes</name>
    <uri>http://127.0.0.1:9998/storage/containers/quotes</uri>
</container>

Get the contents of item 3:

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

which returns the following:

catch the conscience of the king

Update the contents of item 3:

curl -X PUT -HContent-type:text/plain --data "The play's the thing Wherein I'll catch the conscience of the king" http://127.0.0.1:9998/storage/containers/quotes/3 

A new search shows that the content has been updated, because the digest is different and the last modified time is more recent:

<container>
    <item>
        <digest>36d586647af7886aadbc21f238cee7740cf319e9</digest>
        <lastModified>2007-01-10T15:46:46+01:00</lastModified>
        <mimeType>text/plain</mimeType>
        <name>3</name>
        <uri>http://127.0.0.1:9998/storage/containers/quotes/3</uri>
    </item>
    <name>quotes</name>
    <uri>http://127.0.0.1:9998/storage/containers/quotes</uri>
</container>

Get the updated contents of item 3:

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

which returns the following:

The play's the thing Wherein I'll catch the conscience of the king

Delete item 3:

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

A new search using 'king' returns no items.

Delete container 'quotes':

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

The state of the service is now equivalent to that of a newly started storage service.

Caching of content

The getting of item 3 in the previous example supports the HTTP features Last-Modified and ETag. The following shows a set of HTTP requests and responses from the Firefox web browser to the web storage service that highlights the caching mechanism. (The headers can be viewed using Firefox's LiveHTTPHeaders plugin, available from the Firefox Tools menu after installation.)

Request, initial query to http://127.0.0.1:9998/storage/containers/quotes/3:

GET /storage/containers/quotes/3 HTTP/1.1
Host: 127.0.0.1:9998
User-Agent: Mozilla/5.0 (X11; U; SunOS i86pc; en-US; rv:1.8.1) Gecko/20061024 Firefox/2.0
Accept: text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5
Accept-Language: en-us,en;q=0.5
Accept-Encoding: gzip,deflate
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
Keep-Alive: 300
Connection: keep-alive

Response

HTTP/1.1 200 OK
Last-modified: Wed, 10 Jan 2007 15:12:05 GMT
Content-type: text/plain
Etag: "-71240529c42b5b92e3086e624618d19164658616"
Transfer-encoding: chunked

21
catch the conscience of the king

0

The response includes the Last-Modified and ETag header fields in the response.

Getting the same item again:

Request

GET /storage/containers/quotes/3 HTTP/1.1
Host: 127.0.0.1:9998
User-Agent: Mozilla/5.0 (X11; U; SunOS i86pc; en-US; rv:1.8.1) Gecko/20061024 Firefox/2.0
Accept: text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5
Accept-Language: en-us,en;q=0.5
Accept-Encoding: gzip,deflate
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
Keep-Alive: 300
Connection: keep-alive
If-Modified-Since: Wed, 10 Jan 2007 15:12:05 GMT
If-None-Match: "-71240529c42b5b92e3086e624618d19164658616"

Response

HTTP/1.1 304 Not Modified
Content-length: 0
Etag: "-71240529c42b5b92e3086e624618d19164658616"

The request contains the Last-Modified and ETag returned in the first response in the If-Modified-Since and If-None-Match header fields. The response contains no content, and a 304 status code is returned because the item has not been modified (updated).

Getting the updated item:

Request

GET /storage/containers/quotes/3 HTTP/1.1
Host: 127.0.0.1:9998
User-Agent: Mozilla/5.0 (X11; U; SunOS i86pc; en-US; rv:1.8.1) Gecko/20061024 Firefox/2.0
Accept: text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5
Accept-Language: en-us,en;q=0.5
Accept-Encoding: gzip,deflate
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
Keep-Alive: 300
Connection: keep-alive
If-Modified-Since: Wed, 10 Jan 2007 15:12:05 GMT
If-None-Match: "-71240529c42b5b92e3086e624618d19164658616"

Response

HTTP/1.1 200 OK
Last-modified: Wed, 10 Jan 2007 15:14:17 GMT
Content-type: text/plain
Etag: "36d586647af7886aadbc21f238cee7740cf319e9"
Transfer-encoding: chunked

43
The play's the thing Wherein I'll catch the conscience of the king

0

Since item 3 has been updated, the If-Modified-Since and If-None-Match of the request do not result in a 304 response being returned. The updated content is returned with the latest Last-Modified and ETag values.