Académique Documents
Professionnel Documents
Culture Documents
Overview
Why REST? KNI Design Jersey/JAXB Patterns for creating your own REST projects TODO Q&A
Why REST?
Lightweight and simple -- just uses HTTP and URIs, and sends across the minimal amount of data. Stateless/Scalable
KNI Design
HTTP
KNI Web
(JSP, HTML, JS)
Jersey
The Reference Implementation for JAX-RS, the JSR for building RESTful services with Java. Intended to be production quality. Key Features: Lets you easily surface resources. Easily extract path elements and query parameters from URIs. Works with JAXB to easily read/write both XML and JSON.
JAXB
Binds XML Schema to Java objects With the Jersey integration, it is easy to spit out XML and JSON from Java objects, and read XML and JSON into Java objects. We tried a few other things before settling on JAXB; much happier with JAXB.
Adding JAXB
Add JAXB to pom.xml
<dependencies> <dependency> <groupId>com.sun.xml.bind</groupId> <artifactId>jaxb-impl</artifactId> <version>2.1.10</version> <dependency> <dependencies>
Adding JAXB
Add JAXB plugin to pom.xml
<plugin> <groupId>org.jvnet.jaxb2.maven2</groupId> <artifactId>maven-jaxb2-plugin</artifactId> <executions> <execution> <goals> <goal>generate</goal> </goals> </execution> </executions> <configuration> <generatePackage>com.ketera.rest.demo</generatePackage> </configuration> </plugin>
Create XSD
Create ../main/resources/demo.xsd
<?xml version="1.0" encoding="UTF-8"?> <xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:jxb="http://java.sun.com/xml/ns/jaxb" jxb:version="2.0"> <xsd:complexType name="ContactType"> <xsd:all> <xsd:element name="name" type="xsd:string" maxOccurs="1" minOccurs="0" /> <xsd:element name="age" type="xsd:integer" maxOccurs="1" minOccurs="0" /> </xsd:all> </xsd:complexType> <xsd:element name="contact" type="ContactType" /> </xsd:schema>xsd:element name="contact" type="ContactType"/> </xsd:schema>
>curl -d "<contact><name>Joe</name><age>15</age></contact>" -X PUT -H "Accept: application/json" -H "Content-Type : text/xml" coverbeck-d820.ketera.com:8080/kdemo/contact/1234 {"name":"Joe","age":"15"} >curl -d @foo.json -X PUT -H "Accept: application/json" -H "Content-Type: application/json" coverbeck-d820.ketera.com:8080/kdemo/contact/1234 {"name":"John","age":"15"} foo.json: {"name":"John","age":"15"}
Validating Input
If we want to input validated against the schema, need to do a little extra work:
@Provider public class DemoContextResolver implements ContextResolver<Unmarshaller> private static Schema schema = SchemaFactory.newInstance(XMLConstants. W3C_XML_SCHEMA_NS_URI).newSchema( new StreamSource(DemoContextResolver.class .getResourceAsStream("/demo.xsd"))) ; ... public Unmarshaller getContext(Class<?> type) { Unmarshaller unmarshaller = getJAXBContext().createUnmarshaller(); unmarshaller.setSchema(schema); return unmarshaller; }
Exception Handler
package com.ketera.rest.demo; import import import import import javax.ws.rs.core.Response; javax.ws.rs.core.Response.Status; javax.ws.rs.ext.ExceptionMapper; javax.ws.rs.ext.Provider; com.sun.jersey.api.NotFoundException;
@Provider public class DemoExceptionMapper implements ExceptionMapper<Exception> { public Response toResponse(Exception exception) { if (exception instanceof NotFoundException) { NotFoundException nfe = (NotFoundException) exception; return Response.status(Status.NOT_FOUND).entity("Oops, bad URI: " + nfe.getNotFoundUri()).build(); } return Response.status(Status.BAD_REQUEST).entity("An error occurred: " exception.getMessage()).build(); } }
Response
Sometimes you don't want to return a simple entity that can be represented with an XML document; need to modify the header, status code, etc. Use a Response
@Produces(MediaType.APPLICATION_OCTET_STREAM) @GET public Response getImage(@PathParam("id") String id) { byte [] bytes = getBytes(); String filename = getFilename(); GenericEntity<byte[]> entity = new GenericEntity<byte[]>(bytes){} return Response.status(Status.OK).header("Content-Disposition", "inline; filename=\"" + filename + "\"").entity(entity).build(); }
Foo.jsp
Howdy from the JSP. >curl coverbeck-d820.ketera.com:8080/kdemo/jsp -->Howdy from the JSP.
Logging
Add this to web.xml, to the Jersey Filter:
<init-param> <param-name>com.sun.jersey.spi.container.ContainerRequestFilters</param-name> <param-value>com.sun.jersey.api.container.filter.LoggingFilter</param-value> </init-param> <init-param> <param-name>com.sun.jersey.spi.container.ContainerResponseFilters</param-name> <param-value>com.sun.jersey.api.container.filter.LoggingFilter</param-value> </init-param> <init-param> <param-name>com.sun.jersey.config.feature.logging.DisableEntitylogging</param-name> <param-value>true</param-value> </init-param>
Automated Testing
Generally separate "services" from resources
@GET @Path("{id}") public JAXBElement<CustomerType> getCustomer(@PathParam("id" String id) KniException { // Handle exception with exception mapper CustomerType customerType = CustomerService.getInstance().getCustomer(id); return objectFactory.createCustomer(customerType); } throws
Now you can just write regular unit tests against CustomerService.
WebAPI Filter
A servlet filter for RESTful apps that handles authentication, authorization, and throttle control. http://engwiki.ketera.com/index.php/Web_API_Filter Public URI (example only, not real): http://portal.ketera.com/kni/customers/123 Internal URI (example only, not real):
http://portal.ketera.com/internal/customers/123? username=kelleybrown@officemax.com
Reusability
Went through KNI, not too much is reusable. AbstractResource: Ties-in to Web API. Injects UriInfo, HttpServletRequest into the resource. FormatFilter, to allow specifying document format via query parameter :
http://.../customers/1234?alt=json http://../customers/1234?alt=xml
Unresolved Issues/TODO
Documentation XSD published on site Jersey has a .WADL http://.../kdemo/applicaton.wadl Need to generate .WADL as part of site
WADL
<?xml version="1.0" encoding="UTF-8" standalone="yes"?> <application xmlns="http://research.sun.com/wadl/2006/10"> <doc xmlns:jersey="http://jersey.dev.java.net/" jersey:generatedBy="Jersey: 1.1.5.1 03/10/2010 02:33 PM"/> <resources base="http://coverbeck-d820.ketera.com:8080/kdemo/"> <resource path="jsp"> <method name="GET" id="showJsp"> <response> <representation mediaType="*/*"/> </response> </method> </resource> <resource path="contact"> <resource path="{id: [0-9]+}"> <param xmlns:xs="http://www.w3.org/2001/XMLSchema" type="xs:long" style="template" name=" id"/> <method name="GET" id="retrieveContact"> <response> <representation mediaType="text/xml"/> <representation mediaType="application/json"/> </response> </method> <method name="PUT" id="updateContact">
Links
THE Book: Restful Web Services, by Richardson and Ruby CVS Module: kni/rest Sample Project:
https://docs.google.com/a/ketera.com/leaf?id=0B605PbiG2YIYzI1ZTI3NDctOTVmZC00ZmIxLWE0ZjgtZGU3ZjY1ZDU4YWMx&hl=en