CS 639 class 20
Demo on using
eclipse for orderService
More Complicated URI Templates (relevant to pa3 part on designing an extended orderService)
There is a way to pick up “the rest” of the URI after matching a prefix of it. Or picking up something fitting a regular expression, but we don’t really need these forms.
URI Templates can be more complicated:
Example from pa3.html: /{eventId}/{inviteId}
So if events are id’d by
“e001”, etc., and invites by 1, 2, 3,...
an individual invitation could have URI path “/e021/32”
There can be constant parts after a parameter, like this:
/customers/{id}/address
Or even multiple parameters for text between /s, like this:
/customers/{firstname}-{lastname}
which matches “/customers/bill-burke” for example.
This example is from Burke, RESTful Java with JAX-RS. He has detailed discussion of URI Templates.
Last time: We looked at Jersey
client code in Test2.java of firstRest. We saw how
Jersey can serialize and deserialize DOM object trees
and POJOs that express the same data structure as XML, using JAXB. The natural question comes up: what else can
Jersey serialize? Anything that JAXB can
do, plus at least DOM, we know from Test2. Jersey docs mention SAX too, of course
just for reading XML.
Jersey contains many serializers and deserializers. I haven’t yet figured out a way to get a list via documentation, but when try to serialize something it doesn’t support, say an ArrayList<String> object, on the server end (using a Resource object) you see:
SEVERE:
A message body writer for Java class java.util.ArrayList,
and Java type class java.util.ArrayList, and MIME
media type application/xml was not found
Apr
4, 2012 6:29:07 PM com.sun.jersey.spi.container.ContainerResponse
write
SEVERE:
The registered message body writers compatible with the MIME media type are:
application/xml -> <-- specially for media type application/xml
com.sun.jersey.core.impl.provider.entity.XMLJAXBElementProvider$App
com.sun.jersey.core.impl.provider.entity.DocumentProvider <
---DOM
com.sun.jersey.core.impl.provider.entity.SourceProvider$SourceWriter
com.sun.jersey.core.impl.provider.entity.XMLRootElementProvider$App
com.sun.jersey.core.impl.provider.entity.XMLListElementProvider$App
*/*
-> <--useful for all media types
com.sun.jersey.core.impl.provider.entity.FormProvider
com.sun.jersey.core.impl.provider.entity.StringProvider
com.sun.jersey.core.impl.provider.entity.ByteArrayProvider
com.sun.jersey.core.impl.provider.entity.FileProvider
com.sun.jersey.core.impl.provider.entity.InputStreamProvider
com.sun.jersey.core.impl.provider.entity.DataSourceProvider
com.sun.jersey.core.impl.provider.entity.XMLJAXBElementProvider$General
com.sun.jersey.core.impl.provider.entity.ReaderProvider
com.sun.jersey.core.impl.provider.entity.DocumentProvider
com.sun.jersey.core.impl.provider.entity.StreamingOutputProvider
com.sun.jersey.core.impl.provider.entity.SourceProvider$SourceWriter
com.sun.jersey.server.impl.template.ViewableMessageBodyWriter
com.sun.jersey.json.impl.provider.entity.JSONJAXBElementProvider$General
com.sun.jersey.json.impl.provider.entity.JSONArrayProvider$General
com.sun.jersey.json.impl.provider.entity.JSONObjectProvider$General
com.sun.jersey.core.impl.provider.entity.XMLRootElementProvider$General
com.sun.jersey.core.impl.provider.entity.XMLListElementProvider$General
com.sun.jersey.json.impl.provider.entity.JSONRootElementProvider$General
com.sun.jersey.json.impl.provider.entity.JSONListElementProvider$General
Unfortunately, trying serialization of ArrayList on the client with WebResource only yields the first part:
A
message body writer for Java class java.util.ArrayList,
and Java type class java.util.ArrayList, and MIME
media type application/xml was not found
Another way: look at this package in the online sources, say at grepcode.com. Click the Hierarchy button to see the subclasses. List is a subset of the above, has none of the names with XML in them.
It turns out that it’s the generic type involved in ArrayList<String> that trips up Jersey here. There is a newer way to handle them. Maybe we can get back to this advanced topic.
This gives an idea that many ways are there. And this list expands with time (i.e. versions of JAX-RS, still relatively new).
So you see that Jersey thinks of a serializer as a “message body writer”, and also has “message body readers” for XML-to-Java conversions. These important facilities are available for both server and client code. However, they are implementation objects, so no Javadoc easily available.
Look at Chap. 4 as
guide to basics of orderService, plus a few
differences...
pg. 57 summary of 4 basic actions using
URI templates, all "customer" actions, or "ordinary user"
actions
our orderService has 2 more, considered
"admin" actions
DELETE /order delete all orders
POST /poke make oldest order READY
So we see there are two kinds of users,
ordinary and admin, as is common with web apps.
If we added authentication and
authorization, we can restrict users to their proper actions--see Chap. 9 if
interested.
Another top-level view is the state
diagram of Fig 4-1, pg 56. This chapter does not do payments (that's in Chap.
5), so the states are simplified to PREPARING and READY
place-order-action poke-action
start-------------------->PREPARING--------------->READY
^ |
|_ _| update
Orders can also be deleted from PREPARING and READY
state.
Creating an Order: POST order for orderService
Creating an order--see pg. 58. Unlike GETs, the success code here is 201
Created, not 200 OK.
The order XML is shown on pg., 59, and
note that it uses a namespace related to the company, and application/xml as
the mediatype. Also note that <name> here is
<drink> for our orderService, and we have no
<quantity> element.
How does the client know the proper
format for this message?
Could be via good WADL served by the
server--see pg. 86, where local href to
"order.xsd" (order.xsd is not listed anywhere in the book, though)
Jersey server-side support does serve WADL, see "ant get-wadl"
in build.xml (and much improved over last year)
From application.wadl
served by orderService:
<grammars>
<include href="application.wadl/xsd0.xsd">
<doc xml:lang="en" title="Generated"/>
</include>
</grammars>
This promises a schema xsd0.xsd
obtainable at application.wadl/xsd0.xsd in the site. See this
schema in the handout.
Later, look at this XSD and the
corresponding JAXB annotations.
So there is a way for the client to get
the XSD and from it, figure out what is needed to be sent in to create an
order.
In Chap. 4, the client finds out the
assigned id of the order by accessing the Location header on the POST
response. From last class, you can see
you would need to use the wrapped response object to get this.
our orderService returns the id in the response
body.
So our response looks like the XML on
pg. 60 with
·
<quantity> dropped,
·
constants in all-caps,
·
<status> PREPARING,
·
<id>1234</id>
as first child of <order>
·
<location> the second
child, before <item>
·
no <items>, just sequence of <item>.
·
<drink> instead of
<name>
So the client does not have to access a
header to get the id and thus the URI for the new order.
Errors from Create order: 400 = Bad
Request, 500 = Server Error
Bad Order Requests and Default
Laxness of JAXB deserialization
Example of bad request that actually
returns 400: one with id specified. Only the server gets to assign the id.
Another example of possible bad
request, on pg. 61: no <name>/<drink> element. This is not actually
enforced by our orderService, because by default,
JAXB just processes what it finds in the XML that matches the POJO fields.
In fact, JAXB is very permissive unless
you turn on validation: child elements can be out of order and still be
processed. We don't have validation on. Maybe later.
Without validation, JAXB just ignores a
child element it doesn't understand. So misspelled LARGER instead of LARGE (as
in order.xml of the provided project) just returns an order without a size in
current system.
If you turn on validation, you can get
specific information on validation errors--
JAXB Deserialization
with Validation turned on (we're not doing this yet):
XML ---> validation -deserializaton --->
POJOs
|
v
validation errors
We can capture the validation errors in ValidationEvent objects, which contain line and col of the source XML problem, a message, and the severity level.
orderService
Status Requests using GET
GET status, pg. 64.
The first GET response should have
status (and id too, in our case). No change should occur in the resource due to
a GET.
In our case a POST to /poke can change
this order's status, so two GETs can see different status values
pg. 67: see URI template in action to
pull out the orderId from the URI coming in.
Also, @Path on the class sets a
starting path for the class, and then @Path on the method adds to it. (In firstRest, just had @Path on the class)
See return value of String from GET:
means this method needs to generate XML text in a String.
This is using XStream,
still another way to generate XML that we are not covering. We could use DOM here for example.
The code returns a String to Jersey
here, the simplest thing to serialize.
The throws show how to generate HTTP
error conditions (from the server) without wrapping the response up in a bigger
object.
pg. 67 Errata 2nd line from bottom: /{orderId} (not {/ordered},
transpose / and {
orderService Update
Order actions
PUT to update an order--pg. 68-69
First bullet on pg.68 does not agree
with our big table, but recall POST is also the catch-all, so not really
discordant.
Other two bullets do agree
For us, PUT expects an order already
there, so get 404 if not.
Choice of success returns: 200 with
order XML, 204 without
We allow updates of PREPARING orders
but not READY orders, so can get failure:
Choice of failure codes discussed: 405 Not Allowed or 409 Conflict (clashing representations) our orderService send s 409.
Note that PUT always replaces the whole
representation, not like database row update.
pp. 72-74 .NET implementation: see URI
template in use here too.
DELETE order, pp. 75-76: we currently
allow DELETE in either state. We do
return a rep on a successful DELETE.
OrderService.xsd: handout (AKA schema1.xsd)
This is generated by Jersey
from the POJOs of the implementation.
Note that the schema is
marked elementFormDefault=”qualified” as we have
restricted ourselves to, to avoid strange dependencies of NS prefix usage on
global and non-global elements. And it matters, because this schema has
non-global elements such as size.
A global element is one
defined as a direct child of <schema>.
We see that <order> and <item> are global elements, and the
other elements are non-global. Global elements can match against root elements
of XML documents. So we see that an XML
doc with <item> at the root can be validated with this schema, as well as
ones with <order> at the root.
We see the
application-level namespace is given a prefix tns,
but this is used only on a subset of app-specific names in use in the schema.
The ones with name=xxx may not have a prefix because they are adding new names
into the target namespace—you cannot use a schema to put new names into some
other namespace, so the xxx here must be a local name. It says so in the schema
of schemas.
ref=”tns:item”There is a new element attribute form for item within
<order>’s type: Since <item>
is defined at top level, the <item> child of <order> refers to that
declaration rather than repeating it here.
So it can be a root element of a document or a child element of
<order> in the document. This style of schema definition is fairly common
in practice. For one thing, such schemas can work properly even without the elementFormDefault being “qualified” (if all elements are
defined at top level) since the differences between qualified and unqualified
apply only to elements declared below the top level. For an example done both
ways, see http://www.w3schools.com/schema/schema_example.asp.
Also, po.xsd in the XML Schema
Primer (this doc is also linked from the class web page). The schema of
schemas says that the ref’s attribute value is of type QName,
so it can point out of the schema’s namespace into another namespace. That
brings us into the territory of multiple schemas, which we have been deferring.
<xs:complexType name="orderRepresentation">
<xs:sequence>
<xs:element name="id"
type="xs:string"
minOccurs="0"/>
<xs:element ref="tns:item" maxOccurs="unbounded"/>
<xs:element name="location"
type="tns:location"/>
<xs:element name="cost"
type="xs:decimal"
minOccurs="0"/>
<xs:element name="status"
type="tns:orderStatus"
minOccurs="0"/>
</xs:sequence>
</xs:complexType>
See this type
definition:
<xs:simpleType
name="size">
<xs:restriction
base="xs:string">
<xs:enumeration
value="LARGE"/>
<xs:enumeration
value="SMALL"/>
<xs:enumeration
value="MEDIUM"/>
</xs:restriction>
</xs:simpleType>
This is defining a type of local name
“size” in the target NS of restbucks, here using tns: prefix. Thus references
to this type will be tns:size.
So we should pay attention to simpleTypes in XSDs now.
XML Schema Simple Types
A simple type in XML
Schema is a type for element text content or attribute values. They are built up from the primitive data types listed in Table
2.1 such as xsd:string and xsd:decimal.
So
<temperature>66.9</temperature> can be xsd:decimal, or a restricted type that requires that
temperature < 200.
This type describes strings that are
exactly one of these three strings.
That's a restriction of xs:string.
Then we also have
<xs:complexType name="item">
<xs:sequence>
<xs:element name="milk"
type="tns:milk"/>
<xs:element name="size"
type="tns:size"/>
<xs:element name="drink"
type="tns:drink"/>
</xs:sequence>
</xs:complexType>
as a global element, i.e,, this is a direct child
of <xs:schema>. So this schema will validate
XML with root element <item>, as well as allow this construct in other
places.
However, since size is “inside” item only,
the schema will not validate XML with root element <size> or <milk>
etc.
In Harold, pg. 36 we have in the DTD--
<!ATTLIST Price Currency (USD|CAN|GBP) #REQUIRED>
but on pg. 40, in the XSD for MoneyType, used for
Price element:
<xsd:attribute name="currency" type = "xsd:string"/>
We can do better now: define a simple
type for currency:
<xsd:simpleType name="CurrencyType">
<xsd:restriction base="xsd:string">
<xsd:enumeration
value="USD" />
<xsd:enumeration
value="CAN" />
<xsd:enumeration
value="GBP" />
</xsd:restriction>
</xsd:simpleType>
<xsd:attribute name="currency" type = "CurrencyType"/>
Next time: more simple types, more on JAXB