REST & JSR 311 Stefan Tilkov, innoQ Deutschland GmbH stefan.tilkov@innoq.com April 2009 Stefan Tilkov Geschäftsführer und Principal Consultant, innoQ Deutschland GmbH Fokus auf SOA, Web-Services, REST SOA-Editor InfoQ.com Herausgeber "SOA-Expertenwissen" (mit Dr. Gernot Starke) Mitglied JSR 311 EG Copyright (c) 2008 innoQ Beratungsunternehmen für SoftwareArchitekturen ~50 Mitarbeiter in D (Ratingen) und CH (Zürich) Strategische IT-Beratung, Architekturconsulting, Entwicklung Service-orientierte Architekturen (SOA) (WS-*, REST, OSS-Lösungen, Governance) Rationelle Software-Produktion (MDA, MDSD, Java EE, Ruby on Rails) Copyright (c) 2008 innoQ Contents An Introduction to REST Why REST Matters REST And Web Services JSR 311 Intro Demo Discussion Copyright (c) 2008 innoQ REST Explained in 5 Easy Steps Copyright (c) 2008 innoQ What is REST? REpresentational State Transfer Described by Roy Fielding in his dissertation One of a number of "architectural styles" Architectural principles underlying HTTP, defined a posteriori See: http://www.ics.uci.edu/~fielding/pubs/dissertation/top.htm Copyright (c) 2008 innoQ 0. Prerequisite: Let's equate "REST" with "RESTful HTTP usage" ... Copyright (c) 2008 innoQ 1. Give Every "Thing" an ID http://example.com/customers/1234 http://example.com/orders/2007/10/776654 http://example.com/products/4554 http://example.com/processes/sal-increase-234 Copyright (c) 2008 innoQ 2. Link Things To Each Other 23 Copyright (c) 2008 innoQ 3. Use Standard Methods GET PUT POST DELETE retrieve information, possibly cached Update or create with known ID Create or append sub-resource (Logically) remove Copyright (c) 2008 innoQ 4. Allow for Multiple "Representations" GET /customers/1234 Host: example.com Accept: application/vnd.mycompany.customer+xml Copyright (c) 2008 innoQ 4. Allow for Multiple "Representations" GET /customers/1234 Host: example.com Accept: application/vnd.mycompany.customer+xml ... Copyright (c) 2008 innoQ 4. Allow for Multiple "Representations" GET /customers/1234 Host: example.com Accept: application/vnd.mycompany.customer+xml ... GET /customers/1234 Host: example.com Accept: text/x-vcard Copyright (c) 2008 innoQ 4. Allow for Multiple "Representations" GET /customers/1234 Host: example.com Accept: application/vnd.mycompany.customer+xml ... GET /customers/1234 Host: example.com Accept: text/x-vcard begin:vcard ... end:vcard Copyright (c) 2008 innoQ 5. Communicate Statelessly GET /customers/1234 Host: example.com Accept: application/vnd.mycompany.customer+xml time Copyright (c) 2008 innoQ 5. Communicate Statelessly GET /customers/1234 Host: example.com Accept: application/vnd.mycompany.customer+xml shutdown update software replace hardware startup time Copyright (c) 2008 innoQ 5. Communicate Statelessly GET /customers/1234 Host: example.com Accept: application/vnd.mycompany.customer+xml shutdown update software replace hardware startup GET /customers/1234/orders/46 Host: example.com Accept: application/vnd.mycompany.order+xml time ... Copyright (c) 2008 innoQ REST (Pragmatic Version) 1 2 3 4 5 Give everything an ID Link things to each other Use standard methods Allow for multiple representations Communicate Statelessly Copyright (c) 2008 innoQ REST (Academic Version) 1 2 3 4 5 Identifiable resources Hypermedia as the engine of application state Uniform interface Resource representations Stateless communication Copyright (c) 2008 innoQ Some HTTP features Verbs (in order of popularity): GET, POST PUT, DELETE HEAD, OPTIONS, TRACE Standardized (& meaningful) response codes Content negotiation Redirection Caching (incl. validation/expiry) Compression Chunking Copyright (c) 2008 innoQ Web Services OrderManagementService + getOrders() + submitOrder() + getOrderDetails() + getOrdersForCustomers() + updateOrder() + addOrderItem() + cancelOrder() + cancelAllOrders() A separate interface (façade) for each purpose As known CORBA, DCOM, RMI/EJB Often used for SOA ("CORBA w/ angle brackets) Application-specific protocol Copyright (c) 2008 innoQ CustomerManagementService + getCustomers() + addCustomer() + getCustomerDetails() + updateCustomer() + deleteCustomer() + deleteAllCustomers() Contribution to the Net's Value 2 URLs http://example.com/customerservice http://example.com/orderservice 1 method POST Copyright (c) 2008 innoQ Web Services Issues Web Services are "Web" in name only WS-* tends to ignore the web Abstractions leak, anyway Protocol independence is a bug, not a feature Copyright (c) 2008 innoQ www.devoxx.com Tunneling Through POST www.devoxx.com (a.k.a. The SOAP Way) www.devoxx.com POST http://example.com/CustomerMgmt 13 www.devoxx.com POST http://example.com/CustomerMgmt 13 Method www.devoxx.com POST http://example.com/CustomerMgmt 13 Method ID www.devoxx.com POST http://example.com/CustomerMgmt 13 Method ID Endpoint www.devoxx.com "Endpoint"? www.devoxx.com www.devoxx.com www.devoxx.com Designing a RESTful application Identify resources & design URIs Select formats (or create new ones) Identify method semantics Select response codes See: http://bitworking.org/news/How_to_create_a_REST_Protocol Copyright (c) 2008 innoQ REST Approach A single generic (uniform) interface for everything Generic verbs mapped to resource semantics A standard application protocol (e.g. HTTP) Copyright (c) 2008 innoQ Contribution to the Net's Value Millions of URLs every customer every order 4-7 supported methods per resource GET, PUT, POST, DELETE TRACE, OPTIONS, HEAD Cacheable, addressable, linkable, ... Copyright (c) 2008 innoQ RESTful HTTP Advantages Universal support (programming languages, operating systems, servers, ...) Proven scalability "Real" web integration for machine-2-machine communication Support for XML, but also other formats Copyright (c) 2008 innoQ What's cool about REST? interface Resource { Resource(URI u) Response get() Response post(Request r) Response put(Request r) Response delete() } interface Resource { Resource(URI u) Response get() Response post(Request r) Response put(Request r) Response delete() } interface Resource { Resource(URI u) Response get() Response post(Request r) Response put(Request r) Response delete() } class CustomerCollection : Resource { ... Response post(Request r) { id = createCustomer(r) return new Response(201, r) } ... } interface Resource { Resource(URI u) Response get() Response post(Request r) Response put(Request r) Response delete() } Any HTTP client (Firefox, IE, curl, wget) class CustomerCollection : Resource { ... Response post(Request r) { id = createCustomer(r) return new Response(201, r) } ... } interface Resource { Resource(URI u) Response get() Response post(Request r) Response put(Request r) Response delete() } Any HTTP client (Firefox, IE, curl, wget) Any HTTP server class CustomerCollection : Resource { ... Response post(Request r) { id = createCustomer(r) return new Response(201, r) } ... } interface Resource { Resource(URI u) Response get() Response post(Request r) Response put(Request r) Response delete() } Any HTTP client (Firefox, IE, curl, wget) Any HTTP server Caches class CustomerCollection : Resource { ... Response post(Request r) { id = createCustomer(r) return new Response(201, r) } ... } interface Resource { Resource(URI u) Response get() Response post(Request r) Response put(Request r) Response delete() } Any HTTP client (Firefox, IE, curl, wget) Any HTTP server Caches Proxies class CustomerCollection : Resource { ... Response post(Request r) { id = createCustomer(r) return new Response(201, r) } ... } interface Resource { Resource(URI u) Response get() Response post(Request r) Response put(Request r) Response delete() } Any HTTP client (Firefox, IE, curl, wget) Any HTTP server Caches Proxies Google, Yahoo!, MSN class CustomerCollection : Resource { ... Response post(Request r) { id = createCustomer(r) return new Response(201, r) } ... } interface Resource { Resource(URI u) Response get() Response post(Request r) Response put(Request r) Response delete() } Any HTTP client (Firefox, IE, curl, wget) Any HTTP server Caches Proxies Google, Yahoo!, MSN class CustomerCollection : Resource { ... Response post(Request r) { id = createCustomer(r) return new Response(201, r) } ... } Anything that knows your app interface Resource { Resource(URI u) Response get() Response post(Request r) Response put(Request r) Response delete() } Any HTTP client (Firefox, IE, curl, wget) Any HTTP server Caches Proxies Google, Yahoo!, MSN class CustomerCollection : Resource { ... Response post(Request r) { id = createCustomer(r) return new Response(201, r) } ... } Anything that knows your app interface Resource { Resource(URI u) Response get() Response post(Request r) Response put(Request r) Response delete() } generic Any HTTP client (Firefox, IE, curl, wget) Any HTTP server Caches Proxies Google, Yahoo!, MSN class CustomerCollection : Resource { ... Response post(Request r) { id = createCustomer(r) return new Response(201, r) } ... } Anything that knows your app interface Resource { Resource(URI u) Response get() Response post(Request r) Response put(Request r) Response delete() } generic Any HTTP client (Firefox, IE, curl, wget) Any HTTP server Caches Proxies Google, Yahoo!, MSN class CustomerCollection : Resource { ... Response post(Request r) { id = createCustomer(r) return new Response(201, r) } ... } Anything that knows your app specific Mapping Examples Mapping Examples getFreeTimeSlots(Person) rejectApplication(Application) GET /people/{id}/timeslots?state=free POST /rejections http://... Unsuitable for us! POST /contracts Data Location: http://.../contracts/4711 GET /contracts/4711/tariff Result PUT /orders/0815 shipped POST /shipments Data Location: http://.../shipments/4711 performTariffCalculation(Data) shipOrder(ID) shipOrder(ID) [variation] Mapping Examples getFreeTimeSlots(Person) rejectApplication(Application) GET /people/{id}/timeslots?state=free POST /rejections http://... Unsuitable for us! POST /contracts Data Location: http://.../contracts/4711 GET /contracts/4711/tariff Result PUT /orders/0815 shipped POST /shipments Data Location: http://.../shipments/4711 performTariffCalculation(Data) shipOrder(ID) shipOrder(ID) [variation] Mapping Examples getFreeTimeSlots(Person) rejectApplication(Application) GET /people/{id}/timeslots?state=free POST /rejections http://... Unsuitable for us! POST /contracts Data Location: http://.../contracts/4711 GET /contracts/4711/tariff Result PUT /orders/0815 shipped POST /shipments Data Location: http://.../shipments/4711 performTariffCalculation(Data) shipOrder(ID) shipOrder(ID) [variation] Mapping Examples getFreeTimeSlots(Person) rejectApplication(Application) GET /people/{id}/timeslots?state=free POST /rejections http://... Unsuitable for us! POST /contracts Data Location: http://.../contracts/4711 GET /contracts/4711/tariff Result PUT /orders/0815 shipped POST /shipments Data Location: http://.../shipments/4711 performTariffCalculation(Data) shipOrder(ID) shipOrder(ID) [variation] Mapping Examples getFreeTimeSlots(Person) rejectApplication(Application) GET /people/{id}/timeslots?state=free POST /rejections http://... Unsuitable for us! POST /contracts Data Location: http://.../contracts/4711 GET /contracts/4711/tariff Result PUT /orders/0815 shipped POST /shipments Data Location: http://.../shipments/4711 performTariffCalculation(Data) shipOrder(ID) shipOrder(ID) [variation] Mapping Examples getFreeTimeSlots(Person) rejectApplication(Application) GET /people/{id}/timeslots?state=free POST /rejections http://... Unsuitable for us! POST /contracts Data Location: http://.../contracts/4711 GET /contracts/4711/tariff Result PUT /orders/0815 shipped POST /shipments Data Location: http://.../shipments/4711 performTariffCalculation(Data) shipOrder(ID) shipOrder(ID) [variation] Mapping Examples getFreeTimeSlots(Person) rejectApplication(Application) GET /people/{id}/timeslots?state=free POST /rejections http://... Unsuitable for us! POST /contracts Data Location: http://.../contracts/4711 GET /contracts/4711/tariff Result PUT /orders/0815 shipped POST /shipments Data Location: http://.../shipments/4711 performTariffCalculation(Data) shipOrder(ID) shipOrder(ID) [variation] Mapping Examples getFreeTimeSlots(Person) rejectApplication(Application) GET /people/{id}/timeslots?state=free POST /rejections http://... Unsuitable for us! POST /contracts Data Location: http://.../contracts/4711 GET /contracts/4711/tariff Result PUT /orders/0815 shipped POST /shipments Data Location: http://.../shipments/4711 performTariffCalculation(Data) shipOrder(ID) shipOrder(ID) [variation] Mapping Examples getFreeTimeSlots(Person) rejectApplication(Application) GET /people/{id}/timeslots?state=free POST /rejections http://... Unsuitable for us! POST /contracts Data Location: http://.../contracts/4711 GET /contracts/4711/tariff Result PUT /orders/0815 shipped POST /shipments Data Location: http://.../shipments/4711 performTariffCalculation(Data) shipOrder(ID) shipOrder(ID) [variation] Mapping Examples getFreeTimeSlots(Person) rejectApplication(Application) GET /people/{id}/timeslots?state=free POST /rejections http://... Unsuitable for us! POST /contracts Data Location: http://.../contracts/4711 GET /contracts/4711/tariff Result PUT /orders/0815 shipped POST /shipments Data Location: http://.../shipments/4711 performTariffCalculation(Data) shipOrder(ID) shipOrder(ID) [variation] Why You Should Care Copyright (c) 2008 innoQ WS-* Roots The Enterprise RPC, COM, CORBA, RMI, EJB Transaction Systems Controlled Environment Top-down Approach Copyright (c) 2008 innoQ REST Roots The Internet Text formats Wire Standards FTP, POP, SMTP Bottom-up Approach Copyright (c) 2008 innoQ Copyright (c) 2008 innoQ Internet vs. Enterprise Copyright (c) 2008 innoQ What's the difference between the Internet and a typical enterprise? Copyright (c) 2008 innoQ Internet vs. Enterprise One is a gigantic, uncontrollable anarchy of heterogeneous systems with varying quality that evolve independently and constantly get connected in new and unexpected ways. Copyright (c) 2008 innoQ Internet vs. Enterprise One is a gigantic, uncontrollable anarchy of heterogeneous systems with varying quality that evolve independently and constantly get connected in new and unexpected ways. The other is a worldwide, publicly accessible series of interconnected computer networks that transmit data by packet switching using the standard Internet Protocol (IP). Copyright (c) 2008 innoQ If web services are supposed to work on Internet scale, they should be inspired by the Web, not by Distributed Objects Copyright (c) 2008 innoQ JSR 311: JAX-RS: The JavaTM API for RESTful Web Services Copyright (c) 2008 innoQ Goals Create a Java API for building applications that are on the Web easily Follow REST principles and best practices Format-independent (not only XML) HTTP-centric (no protocol independence) Container-independent Copyright (c) 2008 innoQ Status Feb 2007 Oct 2007 May 2008 October 2008 March 2009 Initiated, Expert Group formed Early Draft Review (end: Nov 23, 2007) Public Review Final 1.1 Draft Copyright (c) 2008 innoQ Spec and RI Specification available at http://jcp.org/aboutJava/communityprocess/edr/jsr311/index.html Jersey (reference implementation from Sun), currently at V1.0.2 (JAX-RS 1.0) https://jersey.dev.java.net Copyright (c) 2008 innoQ Approach One class per resource "type" Methods to handle HTTP requests Use of Java 5 Annotations to specify URI Mapping Mapping to HTTP methods Mapping of URI components, HTTP headers, HTTP entities to method parameters and return types MIME type information Copyright (c) 2008 innoQ Example /customers GET - list all customers PUT - unused POST - add new customer DELETE - delete all customers public class CustomersResource { public String getAsPlainText() { return toString() + "\n\n"; } } Copyright (c) 2008 innoQ Example /customers GET - list all customers PUT - unused POST - add new customer DELETE - delete all customers @Path("/customers/") public class CustomersResource { public String getAsPlainText() { return toString() + "\n\n"; } } Copyright (c) 2008 innoQ Example /customers GET - list all customers PUT - unused POST - add new customer DELETE - delete all customers @Path("/customers/") public class CustomersResource { @GET @Produces("text/plain") public String getAsPlainText() { return toString() + "\n\n"; } } Copyright (c) 2008 innoQ Example /customers GET - list all customers PUT - unused POST - add new customer DELETE - delete all customers import javax.ws.rs.ProduceMime; import javax.ws.rs.UriTemplate; import javax.ws.rs.GET; @Path("/customers/") public class CustomersResource { @GET @Produces("text/plain") public String getAsPlainText() { return toString() + "\n\n"; } } Copyright (c) 2008 innoQ URI Templates URI Templates define URI strings with embedded variables http://example.org/products/{upc}/buyers?page={page_num} Based on Joe Gregorio's URI Templates IETF Draft (see http://bitworking.org/projects/URI-Templates/) @Path annotation can be applied to classes and methods Copyright (c) 2008 innoQ @Path @Path on a class "anchors" a class into URI space, relative to a base URI Method-specific @Path is relative to the class URI @PathParam, @QueryParam, @MatrixParam to access URI templates variables Copyright (c) 2008 innoQ @GET, @PUT, @POST, @DELETE Specify the HTTP "verb" a method handles (GET, PUT, POST, DELETE, ...) HEAD and OPTIONS handled by implementation (unless overridden in case of HEAD) Copyright (c) 2008 innoQ Example @Path("/helloworld/{section}") public class HelloWorldResource { @GET @Path("/{id}") public String findBySectionAndId( @PathParam("section") String section, @PathParam("id") int id) { return "Hello World - section is " + section + ", id is " + id + "\n"; } } Copyright (c) 2008 innoQ Example @Path("/helloworld/{section}") public class HelloWorldResource { @GET @Path("/{id}") public String findBySectionAndId( @PathParam("section") String section, @PathParam("id") int id) { return "Hello World - section is " + section + ", id is " + id + "\n"; } } http://localhost:9998/helloworld/main/23 Copyright (c) 2008 innoQ Example @Path("/helloworld/{section}") public class HelloWorldResource { @GET @Path("/{id}") public String findBySectionAndId( @PathParam("section") String section, @PathParam("id") int id) { return "Hello World - section is " + section + ", id is " + id + "\n"; } } http://localhost:9998/helloworld/main/23 Hello World - section is main, id is 23 Copyright (c) 2008 innoQ Content Negotiation: @Consumes, @Produces @Consumes and @Produces specify accepted and delivered MIME types Can be specified on class and method level (method level overrides) Special treatment for MessageBodyWriter and MessageBodyReader classes Copyright (c) 2008 innoQ Request dispatching 1.Find class and method according to Actual URI and @Path HTTP method and @GET, @POST, @PUT, @DELETE "Content-type:" header and @Consumes "Accept:" header and @Produces 2.Map @UriParam, @QueryParam, @MatrixParam parameters from URI 3.Map body (for POST and PUT) to unannotated parameter 4.Invoke method 5.Map return value (if any) Copyright (c) 2008 innoQ @Path("/procurement2/customers/") public class CustomersResource { @GET @Produces("application/vnd.innoq.customers+xml") public String getAsXml() { List customers = Customer.findAll(); Element root = new Element("customers", Utilities.NAMESPACE); for (Customer customer : customers) { Element customerElement = new Element("customer", Utilities.NAMESPACE); customerElement.appendChild(customer.getName()); root.appendChild(customerElement); } return elementToXmlString(root); } @POST @Consumes("application/vnd.innoq.customer+xml") public Response newCustomer(String body) { Builder b = new Builder(); try { System.out.println("Received: " + body); Document doc = b.build(body, "."); Customer c = new Customer(doc.query("/i:customer/i:name", new XPathContext("i", Utilities.NAMESPACE)).get(0).getValue()); Customer.add(c); return Response.ok().build(); } catch (Exception e) { e.printStackTrace(); return Response.status(400).entity("Please send well-formed XML\n").type("text/plain").build(); } } @Path("{id}") public CustomerResource customerById(@PathParam("id") int id) { return new CustomerResource(Customer.get(id)); } } Copyright (c) 2008 innoQ MessageBodyReader/ MessageBodyWriter Converts between Java types and representations Class marked with @Provider, implements MessageBody{Reader|Writer} Provides methods for conversion InputStream/OutputStream to/from Java object of type T Copyright (c) 2008 innoQ @Provider @Produces("application/vnd.innoq.customers+xml") public class CustomerListWriter implements MessageBodyWriter { public long getSize(CustomerList t, Class type, Type genericType, Annotation[] annotations, MediaType mediaType) { return -1; } public boolean isWriteable(Class type, Type genericType, Annotation[] annotations, MediaType mediaType) { return CustomerList.class == type; } public void writeTo(CustomerList customers, Class type, Type genericType, Annotation[] annotations, MediaType mediaType, MultivaluedMap httpHeaders, OutputStream entityStream) throws IOException, WebApplicationException { Element root = new Element("customers", NAMESPACE); for (Customer customer : customers) { if (customer != null) { Element customerElement = new Element("customer", NAMESPACE); customerElement.addAttribute(new Attribute("ref", CustomersResource.uriFor(customer))); customerElement.appendChild(customer.getName()); root.appendChild(customerElement); } } writeElementToStream(root, entityStream); } } Copyright (c) 2008 innoQ Sub Resource support Methods annotated with @Path without @GET, @POST, ... allow for hierarchical resources Typical use: Collection resources @Path("{id}") public CustomerResource customerById(@UriParam("id") int id) { return new CustomerResource(Customer.get(id)); } Copyright (c) 2008 innoQ Resource hierarchy /orders GET - list all orders PUT - unused POST - add a new order DELETE - cancel all orders "Root" resource collections /customers GET - list all customers PUT - unused POST - add new customer DELETE - delete all customers /orders/{id} GET - get order details PUT - update order POST - add item DELETE - cancel order Sub resources /customers/{id} GET - get customer details PUT - update customer POST - unused DELETE - delete customer /customers/{id}/orders GET - get all orders for customer PUT - unused POST - add order DELETE - cancel all customer orders Nested resource collection Copyright (c) 2008 innoQ Response Builder Pattern Enables creation of objects with additional HTTP metadata return Response .status(404) .entity("Huh?\n") .type("text/plain") .build(); Copyright (c) 2008 innoQ UriBuilder Enables creation of URIs without repeating URI template content Used to support hypermedia - i.e., create links Builder pattern, again: URI uri = UriBuilder .fromUri(BASEURI) .path(CustomersResource.class) .path(id).build(); Copyright (c) 2008 innoQ @HttpContext @HttpContext to access URI Info (Class UriInfo) HTTP Headers (Class HeaderParam) Preconditions (Class HttpHeaders) Copyright (c) 2008 innoQ Environments Deployment to multiple different environments: Embedded HTTP Server (Java 6) Servlets Java EE JAX-WS Others (e.g. Restlet, ...) Copyright (c) 2008 innoQ Demo Copyright (c) 2008 innoQ More ... Multiple implementations: Jersey, Restlet, RESTEasy, Apache CXF Restlet: interesting for lower-level code Jersey includes client-side API, Netbeans support, view support http://www.innoq.com/resources/REST Copyright (c) 2008 innoQ Stefan Tilkov http://www.innoq.com/blog/st/ Thank you! Any questions? Architectural Consulting SOA MDA J(2)EE WS-* MDSD RoR REST MDE .NET http://www.innoq.com Copyright (c) 2008 innoQ