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.

Using GET to Collection URI (relevant to pa3’s “Designing a better orderService”)

 

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