CS639 Class 23  REST Services: more advanced features

 

paper handout of pg. 180 of RESTful Web Services, by Richardson and Ruby, showing URI templates for their bookmark service

 

 

Richardson and Ruby’s Bookmark Service: see paper handout on URIs

Their book on the actual delicious site: http://delicious.com/leonardr/restbook

 

Look at R&R’s bookmark service, based on del.icio.us bookmark service.

R&R’s book has lots of good guidance on designing REST APIs.

 

See handed out URI templates to describe the service.  Well, not all of it, but the core part.

 

See users resource, familiar setup, like orders in orderService.

 

See bookmark management running at second level in URIs: a new feature to us.

Template: /users/{userId}/bookmarks/{bookmarkId}.

 

Each user has a set of bookmarks, each id’d by MD5 hashes of the actual bookmark.

(it’s a problem to use the URL itself, see the book, and del.icio.us does it this way.)

Java can compute MD5 hashes using MessageDigest in JDK.

 

POST to collection URI to add one, GET, PUT, DELETE on individual bookmarks.

Also here, as in firstRest2: GET to a collection URI: GET to bookmarks for a user to get all or some of them using query string. This returns the XML representations of the bookmarks, not just a list of URIs back into the service, so this is not a hypermedia service. (You can ask for atom feeds.)

 

Use of query string: a new feature to us, for REST services.

--to specify a date or range of dates on a query

.../?timestamp >= 1280296860145  (Unix time, seconds since Jan 1 1970)

 

--Another query: ?limit=100

Could have ?start=50, but not implemented here.

 

Another idea: use of conditional HTTP GET

From HTTP 1.1 spec, now linked from class web page under HTTP:

 

The semantics of the GET method change to a "conditional GET" if the request message includes an If-Modified-Since, If-Unmodified-Since, If-Match, If-None-Match, or If-Range header field. A conditional GET method requests that the entity be transferred only under the circumstances described by the conditional header field(s). The conditional GET method is intended to reduce unnecessary network usage by allowing cached entities to be refreshed without requiring multiple requests or transferring data already held by the client.

 

The If-Modified-Since is a request header, and the corresponding server response usually has the Last-Modified header showing the timestamp related to the response representation.  See Webber et al, pg. 174 for actual header listings, there discussed in terms of caching. If there are no new-enough ones, the client gets back HTTP 304 “Not Modified”.  Of course this means the server needs to timestamp the data used for the representations.  Webber et al, pg. 83 notes that the time used here is good to one second.

 

Looking at the implementation (R&R, pg. 192), we see that this server uses the If-Modified-Since header.  The client can use this header to filter out old bookmarks the client doesn’t want to see.    If there are bookmarks recent enough, they are represented in XML and returned to the user. If not, HTTP 304 is returned.  Note this is implemented in Ruby in this book.

 

Handling query params in JAX-RS: close to HTML form params, since both are request parameter values. Recall @FormParam annotation for service method param to provide incoming form param to Java. Similarly,  could have, for GET /users/{username}/bookmarks?timestamp > 12345678—

 

@GET

@Path(“/{username}/bookmarks)”)

public BookmarkList getBookmarks (@PathParm(“username”) String user, @QueryParam(“timestamp”) long timestamp) …

 

 For the incoming header, use @HeaderParam(“If-Modified-Since”) annotation on a method param.

 

Handling Hierarchical URIs in Jersey:  Using Subresources

 

Hierarchical URIs like /users/{userId}/bookmarks/{bookmarkId}.

 

Jersey implementation of services for these multilevel URI patterns—isn’t it a mess?

 

There’s a concept of subresource that keeps things from blowing up in complexity.

 

Basic Idea: processing the URI from left to right--

/users/{userId}/bookmarks/{bookmarkId}

<-main resource class-> <--subresource class------------->

                       |

                       |handoff from main resource to subresource object

 

 

Back to Vogel’s JAX-RS tutorial, Sec. 5. nicely set up:

 

Packages

resources: ToDoResource, ToDosResource

dao: TodoDao: uses HashMap<String, Todo> to store Todos

model: Todo, with minimal JAXB markup, just @XmlRootElement on the class.

 

Note: no namespace in use here, simplifying the JAXB needs. If we want to add a namespace, we can annotate the child elements as in OrderRepresentation, or set up package annotations.

 

No “activities” package, though, simple activities get absorbed into resources.

 

His Todo service looks like this:

GET /todos

GET /todos/count

POST /todos

GET /todos/{id]

PUT /todos/{id}

DELETE /todos/{id}

 

The implementation could be all in TodosResource, but instead it’s split up into two source files to show the power of subresources to handle hierarchy in URIs.

 

/todos/{id}

<----> <--->

TodosResource: main resource for handling collection of Todos

                TodoResource: a subresource handling a single Todo

 

TodosResource has

@Path(“/todos”) on the class: the root resource, starts with / (not allowed in subresources)

sets first part of URI for the class

 

@GET on a method, to handle GET /todos

 

@GET

@Path(“count”)  on a method, to handle GET /todos /count

 

@POST on a method, to handlle POST /todos

 

Finally, we see the handoff to the subresource: when JAX-RS sees /todos/something, but not /todos/count, the more specific match, it looks here and sees a method returning an object that (hopefully) qualifies as a resource object, i.e., has JAX-RS annotations itself:

 

@Path(“{todo}”)

public TodoResource getTodo(@PathParam(“todo”) String id {

  return new TodoResource(uriInfo, request, id);

}

 

Note we could drop the uriInfo and request params: they are never used, and the uriInfo can be (and is) obtained another way in the subresource.  More on this below.

 

This is understood by Jersey: it will process the GET to such a URI to @GET in the newly created subresource. We are not required to create a new resource object here, we could just look one up somehow, or create different subclasses based on something.  Whatever object gets returned will be inspected for @GET, etc and used to finish doing the request, using the “rest” of the URI.

 

The subresource class TodoResource has no root resource @Path annotation (starting with/), since it is working off the main resource’s root path @Path(“/todos”) processing.  In this case it has @GET, @PUT, and @DELETE for the three /todos/{id} templates.

 

A subresouce can have relative @Path annotations.  For example, we could put the @Path(“status”) in the TodoResource on a method to handle /todos/12/status requests.

 

Or use another subresource to handle status—this can go on for multiple levels.

 

Note on uriInfo and request and dependency injection:  The uriInfo is the path in use wrapped up in a UriInfo object, and request is a HTTPServletRequest wrapped up in a Request object. These are both fields in ToDosResource, ToDoResource, and uriInfo is also in OrderResource.  In all these cases, the field is annotated with @Context.  This annotation directs JAX-RS to fill in these fields at request time before calling the service method. This action of filling in a field of an object from infrastructure code is called dependency injection.

 

The Bookmarks Project: Sketch of Jersey Implementation using a Subresource

 

It’s common to handle two related URI segments in one class, so for example for bookmarks

/users/{username}/bookmarks/{id}

<--users resource-><--bookmarks-->

 

UsersResource:

@Path(/users)  on the class

@GET on a method to handle GET  /users             

 

@Path(“{username}”)

@GET on a method to handle GET /users/username

 

@Path(“bookmarks”)

return new Bookmarks subresource object

 

Bookmarks SubResource: no @Path on class

 

@GET on a method to handle GET /users/username/bookmarks

 

@Path(“{bookmarkId}”)

@GET on a method to handle GET /users/username/bookmarks/id

 

Handling two or more PathParams at once: examples from Burke’s RESTful Java with JAX-RS

 

@Path (“/customers”)

public class CustomerResource {

...

@Path(“{first}-{last}”)

@GET

@Produces (“application/xml”)

public Customer getCustomer(@PathParam(“first”) String firstName,

                                                   @PathParam(“last”) String lastName) {

...

}

 

handles GET /customers/bill-burke.  Assumes no –‘s in names.

 

Example returning an image (pg. 66, Burke)

 

@Path(“/cars/{make}”)

public class CarResource {

 

@GET

@Path(“/{model}/{year}”)

@Produces(“image/jpeg”)

public Jpeg getPicture(@PathParam(“make”) String make,

                                @PathParm(“model”) String model,

                                @PathParam(“year”) String year)

{

...

}

 

handles  GET /cars/ford/taurus/2003, sends jpeg for it.

 

Handling queries: more examples from Burke’s RESTful Java with JAX-RS

 

@Path (“/customers”)

public class CustomerResource {

...

@GET

@Produces (“application/xml”)

public Customer getCustomers(@QueryParam(“start”) int start,

                        @QueryParam(“size”) int size) {

...

}

 

Handles GET /customers?start=10&size=20

 

We can specify default values too:

@Path (“/customers”)

public class CustomerResource {

...

@GET

@Produces (“application/xml”)

public Customer getCustomers(@DefaultValue(“0”)  @QueryParam(“start”) int start,

                       @DefaultValue(“10”)  @QueryParam(“size”) int size) {

...

}