CS 639 class 20
handout:
OrderService.xsd
pa3: clients for
orderService, which more-or-less follows Chap 4.
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 normal way to get a list, but when try to serialize something it doesn’t support, say an ArrayList object, on the server end, 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 end 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
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 xsd0.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.
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