CS639 Class 24 Start
on Chap 5. Hypermedia Service
Project 4:
Presentation May 9 on a serious REST API: 15 mins,
7-10 slides
One slide lists most
important URI templates
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
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) {
...
}
We could add a payments and receipts
to orderService, to make it follow Fig. 4-1 and more
exactly, Fig. 5-9, which shows the
additional resources involved. The orders go through states UNPAID, PREPARING,
READY, and TAKEN.
orderDap: New services over those in orderService:
·
PUT
/ payment/{orderId}/ to pay for this order, receipt is returned,
with URI /order/{orderId}/receipt
·
GET
/receipt/{orderId}
get receipt (receipt is also
returned from payment action)
·
DELETE
/receipt/{orderId}
to finish
Supplied
project orderDap, the hypermedia REST service
described in Chap. 5 is available in a form that works with tomcat.
Fig. 5-4 and
5-5 shows the app for Chap 5, what we’re trying to do here, with slightly
different URIs, and no hypermedia
POST /order creates new order, status
UNPAID
PUT
/order/1234 update UNPAID order,
fail if another status
DELETE
/order/1234 cancel order, succeeding
only if UNPAID
PUT / payment/1234 submit payment, succeeding only if order is
UNPAID; order becomes PREPARING, returning receipt
POST
/poke make oldest
PREPARING order READY (barista finished work)
GET /eceipt/1234 get copy of receipt, if order is PAID
DELETE /receipt/1234 if order is READY, make it TAKEN: complete
the order
GET
/order/1234 return order with
current status
DELETE /order delete all orders (is in orderService but not in orderDap,
but should be)
POST /poke
was added for cs639. It is an “admin”
action.
orderDap and Chap. 5
Fig. 5-5: slightly
different names for states, and “cancelled” is not a
state of order. The order is removed from the repository in this case.
Also, orderDap uses GUIDs for ids, much longer than 1234. I
simplified this for orderService.
Consider the
orderDap sources. Note these additions over orderService
resources: PaymentResource,
ReceiptResource
(as first-level resources)
representations: PaymentRepresentation, ReceiptRepresentation, for XML
domain: Payment is new, OrderStatus has 4 values,
note no Receipt object here: a receipt
can be generated from a Payment object
activities: PaymentActivity, ReadReceiptActivity,
CompleteOrderActivity, exceptions for them
repositories: PaymentRepository: just copy this
Where’s the hypermedia here?
Recall the basic idea that hypermedia
means responding to each service request with not only the answer but also a
description of what can be done next by the client, specifically by listing
URIs for such next steps.
See handout
on the Hypermedia service
The
Hypermedia service of Chap 5 has a DAP, a domain application protocol
The
following XML is returned by “ant restAdd” in orderDap, and put in file order-response.xml. The order id
here is 2a3fcd5c-0aef-41cf-9ebd-0b7de5d88382, not 1234.The constant strings are in lowercase here, because of additional markup in
the enum classes.
<?xml version="1.0"
encoding="UTF-8" standalone="yes"?>
<ns2:order xmlns="http://schemas.restbucks.com/dap"
xmlns:ns2="http://schemas.restbucks.com">
<link mediaType="application/vnd.restbucks+xml"
uri=http://localhost:8080/orderDap/rest/order/f0001d50-e244-4068-84f6-73b800244964
rel="http://relations.restbucks.com/cancel"/>
<link mediaType="application/vnd.restbucks+xml"
uri="http://localhost:8080/orderDap/rest/payment/f0001d50-e244-4068-84f6-73b800244964"
rel="http://relations.restbucks.com/payment"/>
<link mediaType="application/vnd.restbucks+xml"
uri=http://localhost:8080/orderDap/rest/order/f0001d50-e244-4068-84f6-73b800244964
rel="http://relations.restbucks.com/update"/>
<link mediaType="application/vnd.restbucks+xml"
uri="http://localhost:8080/orderDap/rest/order/f0001d50-e244-4068-84f6-73b800244964"
rel="self"/>
<ns2:item>
<ns2:milk>semi</ns2:milk>
<ns2:size>large</ns2:size>
<ns2:drink>cappuccino</ns2:drink>
</ns2:item><ns2:location>takeaway</ns2:location>
<ns2:cost>2.0</ns2:cost>
<ns2:status>unpaid</ns2:status>
</ns2:order>
Notes:
We see 4 <link>s, one for each action that
the user could take after ordering: cancel, pay, update, plus one for “self”.
Since the URIs are generated dynamically, the uses of localhost, etc. are
replaced in a real service installation by proper server URIs.
Each link has an attribute rel=name, giving a name for the type of link this is, and a URI.
The hypermedia links don’t themselves say what HTTP verb to use with the URI, or what XML data (or other representation) is expected.
The rel value, along with the
current URI, is used to look up what HTTP verb to use, (and representation
format) in (unsupplied) metadata for the service.
These links are like Atom links. Example: pg. 198, in Chap. 7, the Atom Syndication Format.
This XML has two namepaces:
ns2: prefix for “http://schemas.restbucks.com”,
the NS we are using in orderService
default: no prefix for
"http://schemas.restbucks.com/dap"
the NS for the DAP links
There could be schemas for these namespaces,
but none are shown in the book. Since the orderDap
project is implemented in Jersey, we can get schemas from the server for the
POJOs that are handled directly by Jersey.
Look at schemas next time.
Pg. 116 has similar XML, but
better prefixes. With more annotations, we should be able to specify our
prefixes, like dap: for the DAP NS, but my attempts at this worked only for
prefixes in the schemas, not in the generated XML, so JAXB is still in-progress
(or possibly I need to upgrade to a newer JDK).
This XML is POSTed with Content-Type header of application/vnd.restbucks+xml, the media type for this service. More on this later.
Following the DAP by looking at the message
responses and their links
Example 5-8, Pg. 118 has Payment response with only two links: for another order or for a receipt. The next normal step has to be done by the barista, so the customer doesn’t have much to do at this point.
Example 5-9 shows what you get back if you follow the receipt link. Now the only way forward is back to the order, to find out how it’s doing.
Example 5-10 shows that the links from order are really dynamic: if the order is “preparing”, there’s nothing to do but ask again for its status.
Metadata for
Hypermedia Services and Vendor-specific media types
Now all this hypermedia XML assumes the client knows how to use a <link> but this <link> is not really a WWW standard (though it looks a lot like an Atom link). How can the client get started, other than guessing?
This is not entirely answered in Chap. 5, but there are pieces of the puzzle.
The service does serve out WADL, but it’s not complete (S13: maybe better now, will see soon.). However, it does say that requests and responses for most resources will be of mediaType “application/vnd.restbucks+xml”, which warns the clients that there is a vendor-specific mediatype in use, so they need to find out about it. It also gives the base URI for the service, so you know it’s associated with some particular web address. Of course you needed that to get the WADL, normally.
The WADL is also supplying XSD. So it’s some help, but it’s not really meant for hypermedia services, since it gives out URI templates for direct access to various resources, rather than expecting them to be determined dynamically by links in responses.
But WADL isn’t in very active use anyway. For example, Amazon S3 doesn’t provide it.
Back to Fig. 5-2, pg. 109 to look at the big picture. We see Media type at the top, with schemas and Link relations and Processing models.
Media types are defined on pg. 103, as the server’s preferred scheme for interpreting the representation (i.e, the XML in our case) sent or received by a service.
The Processing models describe possible flows from one app state to another, where the Links show how we can go out from a particular state, each with its “rel” name and possibly associated schema. Here we would find out what schema is needed for a “payment” transition.
The app state here is mainly defined by the order and its status, and the order is the resource for a certain URI, but there is also a corresponding payment resource that is more app state, and has its own URI/unique id. The receipt has another URI, but it has no independent state: it’s just a reflection of the other resources.
The media type is using URIs as just identifiers, not HTTP-specific names. The media type has to be combined with a Protocol before anything can actually happen.
A Protocol specifies not only media types, but also HTTP verbs and entry-point URIs. In particular, it supplies a map from rel values to HTTP verbs to use. Here is where we find out that the “rel” name “payment” needs to use PUT.
Nice pic, but no actual metadata to show it in action. I think this is mainly a call for action, though they do give references to two existent languages related to the diagram.
Vendor-specific media types
Anyway, the use of
vendor-specific mediaTypes is more general. From
Wikipedia Internet media type
For example
application/vnd.google-earth.kml+xml
for Google Earth KML data
application/vnd.ms-excel
for
old-style Excel data
There is a media type registry at IANA (this is the application part). IANA runs the domain name system. The entry for google-earth has a link to a doc on it. The one for ms-excel has an email from 1996 for the registration.
Unfortunately, there is no standard way to go from the mediaType to a current description of it, other than web search (though this should work pretty well)
Note that the media type is usable for more than HTTP, for example email protocols using SMTP, so keeping the HTTP-specifics out of the media type has a purpose.
Specifying a Protocol Processing Model
How to describe a protocol? Good question. Can use FSMs (finite state machines).
In the .NET section, the authors show a DSL (domain-specific language) for the protocol. They devised this language, so it’s not specifically related to .NET. This is another way to describe a protocol. See Examples 5-26 and 5-31 if interested.
In the “enterprise world”, WS-BPEL (Web Services – Business Process Execution Language) is a framework for describing such workflows, but it is SOAP-centric.
Implementation
We can use JAXB to generate multi-NS XML, with attributes...
To
simplify annotations, packages representations and domain both have package-info.java setting up the
non-DAP NS as default, so we only have to override it
for the DAP NS
The annotations have been simplified relative to the orderService ones, and propOrder annotations added, to show a good application setup.
Handling Attributes
in JAXB: pretty easy
Class Link, in package representations, has fields rel, uri, mediaType, turned into XML
attributes as follows:
@XmlRootElement(namespace = Representation.DAP_NAMESPACE)
// make
sure the schema type is in the same namespace as the element itself
@XmlType(name="link", namespace = Representation.DAP_NAMESPACE, propOrder
= {
"rel", "uri", "mediaType"})
public class Link {
@XmlAttribute(name = "rel")
private String rel;
@XmlAttribute(name = "uri")
private String uri;
@XmlAttribute(name = "mediaType")
private String mediaType;
...
We see the DAP NS being specified for the class and the type, overriding the default set up in package-info.java for this package. Alternatively, we could put Link.java in its own package with a package-info specifying the DAP NS.
The message-level
POJOs: OrderRepresentation, PaymentRepresentation,
and ReceiptRepresentation, all extend Representation
Class Representation sets up a List<Link> field so
that each subclass can have its own number of Links as needed,
or even a variable number of links as needed (recall OrderRepresentation
having 4 at the start and none at the end). Since there is a List<Link>
here, there will be a sequence of link elements, each with attributes as
specified above, and the link elements are in the DAP NS.
public abstract class Representation {
public static final String RELATIONS_URI = "http://relations.restbucks.com/";
public static final String RESTBUCKS_NAMESPACE = "http://schemas.restbucks.com";
public static final String DAP_NAMESPACE = RESTBUCKS_NAMESPACE + "/dap";
public static final String RESTBUCKS_MEDIA_TYPE = "application/vnd.restbucks+xml";
public static final String SELF_REL_VALUE = "self";
@XmlElement(name = "link", namespace = DAP_NAMESPACE)
protected List<Link> links;
protected Link getLinkByName(String
uriName) {
if (links == null) {
return null;
}
for (Link l : links) {
if (l.getRelValue().toLowerCase().equals(uriName.toLowerCase()))
{
return l;
}
}
return null;
}
}
Each Representation class extends Representation, so it
gets the List<Link> by inheritance. Note
Example: OrderRepresentation.java: the orderService
structures are in the old namespace (because of the package-info setup), but
now it additionally has a List<Link> from its superclass,
using DAP NS.
@XmlRootElement(name = "order")
@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "orderRepresentation", propOrder = {
"items",
"location",
"cost",
"status"
})
public class OrderRepresentation extends Representation {
@XmlElement(name = "item", required = true)
private List<Item> items;
…
Pg. 152, end of Chap. 5, says we now have “all the elements at our disposal to build RESTful services”.
But we would like to understand the schemas too, not covered in the book.