CS 639 Class 22
Handout: firstRest2 on front, orderService on back
Handout: WADL and XSD from firstRest2
Recall last time that we have ant targets to use JAXB for conversion of XSD to/from POJOs. The additional JAXB tool support code is in basedir/lib, not deployed to tomcat. This is using JAXB outside of Jersey. We are of course also using JAXB when we use Jersey with POJOs.
Let’s look at the slick setup generated by JAXB from our schema, OrderService.xsd, located in the gen-src directory of the orderService project.
To create gen-src, we used “ant gen-pojos-from-schema”, which runs the xjc schema compiler tool.
The generated classes are all in one package (specified in the XJC command in build.xml) with package-info to set the NS for all classes, and elementFormDefault. So OrderRepresentation (POJO name from XSD type name) gets the advantage of this setup, and has no NS markup itself. OrderRepresentation.java has
@XmlAccessorType(XmlAccessType.FIELD)
So that fields will be one-one with child elements. Then only the fields for the required child elements are given @XmlElement markup:
@XmlElement(required = true)
We see a new annotation of the class:
@XmlType(name
= "orderRepresentation", propOrder = {
"id",
"item",
"location",
"cost",
"status"
})
This tells JAXB what order to generate child elements in. At runtime, JAXB only has .class files to look at for serialization, and it turns out that reflection does not reliably report on the order of the fields, so this annotation is needed to bridge that gap. This also explains the “laxness” we discussed last time, that JAXB will accept XML with out of order child elements. So this annotation is important, and really should be in our orderService POJOs.
Mysteriously, there is no @XMLRootElement in gen-src’s OrderRepresentation or Item, although order and item are global elements in the OrderService.xsd. This is explained by reading the spec and finding there are two ways to specify root elements, the @XMLRootElement we used before and by having an ObjectFactory.java with class annotation @XmlRegistry and a createOrder method with @XmlElementDecl annotation, and this more obscure way is used here. We can easily convert to @XMLRootElement.
Back to Vogel’s JAX-RS
tutorial, Sec. 5. nicely set up, example of GET to collection URI
(re design problem in pa3) Available
in firstRest2, linked to class web page under Resources.
Packages
resources: ToDoResource,
ToDosResource
dao: TodoDao: uses
HashMap<String, Todo> to store Todos
model: Todo, with minimal
JAXB markup, just @XmlRootElement on the class.
These packages can be
corresponded to the three well-known web app layers:
·
Presentation: top
layer, where new requests come in: resources
·
Service: heart of
app, where business code resides: pretty trivial here, also in resources
·
Data access: dao
This leaves model, with the
POJO, separate from the layers, and this is right. The Todo POJO carries data
across the layers.
More on these layers when we
analyze orderService next time.
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.
His Todo service looks like
this:
GET
/todos ßGET
to collection URI
GET
/todos/count
POST
/todos
GET
/todos/{id]
PUT
/todos/{id}
DELETE
/todos/{id}
We looked at the resulting
WADL file shown on the second handout, available by “ant get-wadl” in the
firstRest2 project. The WADL file points to an available XSD using a relative
URL. A new target “get-xsd” is now in firstRest2’s build.xml to get the XSD.
Both are listed on the firstRest2 WADL-XSD handout.
Vogel’s GET /todos returns
List<Todo> to JAX-RS. Here’s the XML:
This is printed in the
tutorial, from a browser:
<?xml
version="1.0" encoding="UTF-8" standalone="yes"?>
<todoes>
<todo><id>3</id><summary>Blabla</summary>
</todo>
<todo><description>Read complete http://www.vogella.de</description>
<id>2</id><summary>Do
something</summary>
</todo>
<todo><description>Read
http://www.vogella.de/articles/REST/article.html</description>
<id>1</id><summary>Learn
REST</summary>
</todo>
</todoes>
Interestingly, it spells the
plural of todo with an e, different from the URI segment. It has some
pluralization algorithm of its own to go from “todo” to its plural. There is no
JAXB markup to guide it here.
Note that in the big table of
class 18, we saw in GET for collection URI:
List the URIs and perhaps other details of
the collection's members
So another option would be
List<URI>, but JAXB won’t serialize this type, or List<String>
without additional work (providing message handlers.)
JAXB will serialize List<POJO>
for any JAXB POJO, so we could set up a POJO for a URI and send that List off.
But for our purposes, sending
List<Todo> for the collection-URI GET fits with our URI-free responses.
// Return the list of todos to the user
in the browser
@GET
@Produces(MediaType.TEXT_XML)
public List<Todo>
getTodosBrowser() {
List<Todo> todos = new
ArrayList<Todo>();
todos.addAll(
TodoDao.instance.getModel().values() );
return todos;
}
So List<POJO> gets
serialized, using funny plural of todo to contain the list, because there is no
markup to specify that element name.
On the client side: set up a
service object of type WebResource, as before, and, for above display
// Get the Todos
System.out.println(service.path("rest").path("todos").accept(
MediaType.TEXT_XML).get(String.class));
The implementation of the
whole REST API 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
For now, just accept this as
an explanation for why there are two java files in resources. This service could easily be implemented all
in TodosResource, as follows:
Our simpler TodosResource has
@Path(“/todos”) on the class:
the root resource
sets first part of URI for
the class
@GET on a method, to handle
GET /todos (as in Vogel’s TodosResource)
@GET
@Path(“count”) on a method, to handle GET /todos /count (as
in Vogel’s TodosResource)
@POST on a method, to handle
POST /todos (as in Vogel’s TodosResource)
Different from Vogel’s TodosResource:
Following orderService, we
could handle GET /todos/1234 here too: The @Path on the method adds to the
@Path on the class, so the path here is /todos/{todo}
@Path(“{todo}”)
@GET
@Produces({MediaType.APPLICATION_XML,
MediaType.APPLICATION_JSON})
public
Todo getTodo(@PathParam("todo") String id) {
Todo todo = TodoDao.instance.getModel().get(id);
if(todo==null)
throw new
RuntimeException("Get: Todo with " + id + " not found");
return todo;
}
And similarly for the others.
The Actual Code in Vogel’s tutorial:
Instead, there’s a handoff
from here into the subresource-
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.
Another trick in this
firstRest2 example: Form handling
REST services are so
simple we can use them from HTML
Consider a HTML form in the
top-level directory of the web app called “todo_form.html” and make it
(simplified and corrected) as follows:
...
<form action="rest/todos"
method="POST">
<input name="id"
/><br/>
<input name="summary"
/><br/>
Description:
<TEXTAREA NAME="description"
COLS=40 ROWS=6></TEXTAREA><br/>
<input type="submit"
value="Submit" />
</form>
The resulting POST to the
local URL rest/todos with params id=6, summary=new+entry, description=new+idea,
brings it to the following in TodosResource.java:
@POST
@Produces(MediaType.TEXT_HTML)
@Consumes(MediaType.APPLICATION_FORM_URLENCODED) <--form data coming in
public void
newTodo(
@FormParam("id")
String id, <--and particular params
@FormParam("summary")
String summary,
@FormParam("description")
String description,
@Context
HttpServletResponse servletResponse
<-- for redirect
) throws
IOException {
Todo
todo = new Todo(id,summary);
if
(description!=null){
todo.setDescription(description);
}
TodoDao.instance.getModel().put(id,
todo);
URI
uri = uriInfo.getAbsolutePathBuilder().path(id).build();
Response.created(uri).build();
servletResponse.sendRedirect("../create_todo.html");
}
Then Jersey can give us the
params with @FormParam(“id”) annotation of a method parameter as we process the
POST, and also the servlet response object for a redirect. The create_todo.html
is in the top-level directory, .. to /rest.
To check your addition,
browse to /firstRest/rest/todos and get a new list.
See the new project posting
at the end of the class web page, with links for the project and for the form
and the list of Todos, from the deployed project at
sf08.cs.umb.edu:11600/firstRest2/.
Headers, Cookies can be picked up similarly—see
Javadocs