0
REST Carol McDonald, Java Architect
Agenda <ul><li>REST Primer </li></ul><ul><li>RESTful Design and API Elements </li></ul><ul><li>Building a Simple Service <...
REpresentational State Transfer <ul><li>Get  </li></ul><ul><li>http://www.depot.com/parts </li></ul>Response XML data = RE...
REST architecture for the Web  Web platforms Addressable Resources Web Container Addressable Resources Web Container
REST Tenets <ul><li>Resources   ( nouns ) </li></ul><ul><ul><li>Identified  by a  URI , For example: </li></ul></ul><ul><u...
HTTP Example Request GET   /music/artists/beatles/recordings  HTTP/1.1 Host: media.example.com Accept: application/xml Res...
REST in Five Steps* <ul><li>Give everything an ID </li></ul><ul><li>Use standard methods </li></ul><ul><li>Link things tog...
Give Every Thing an Id  http://company.com/customers/123456 Resource Collection name Primary key http://company.com/custom...
REST in Five Steps* <ul><li>Give everything an ID </li></ul><ul><li>Use standard methods </li></ul><ul><li>Link things tog...
Use Standard  HTTP Methods: GET  = Read <ul><li>GET to retrieve information </li></ul><ul><ul><li>Should  not modify  anyt...
Use Standard  HTTP Methods: POST  = Create <ul><li>POST to  add new  information </li></ul><ul><li>add the entity ,  appen...
Use Standard  HTTP Methods: PUT = Update <ul><li>PUT to  update  information </li></ul><ul><li>Full entity  create/replace...
Use Standard  HTTP Methods: DELETE <ul><li>Remove (logical) an entity </li></ul><ul><ul><li>Example </li></ul></ul><ul><ul...
Use Standard  Methods:  <ul><ul><li>http://www.infoq.com/articles/rest-introduction </li></ul></ul>Order Customer Mgmt Exa...
Use Standard  Methods: <ul><li>/orders </li></ul><ul><ul><li>GET - list all orders </li></ul></ul><ul><ul><li>POST - submi...
Use Standard  HTTP Methods <ul><li>HTTP Get, Head </li></ul><ul><ul><li>Should  not modify  anything  </li></ul></ul><ul><...
REST in Five Steps* <ul><li>Give everything an ID </li></ul><ul><li>Use standard methods </li></ul><ul><li>Link things tog...
Link Things Together <ul><li>Representations contain links to other resources </li></ul><ul><li>Service provides  links in...
Link Things Together <ul><li>Representations  contain  links  to other  resources </li></ul><ul><li>Put a service in diffe...
REST in Five Steps* <ul><li>Give everything an ID </li></ul><ul><li>Use standard methods </li></ul><ul><li>Link things tog...
Multiple Representations <ul><li>Offer data in a variety of formats, for different needs </li></ul><ul><ul><li>XML </li></...
<ul><ul><li>content negotiation </li></ul></ul>Multiple Representations Content-type HTTP header Accept HTTP header Reques...
REST in Five Steps* <ul><li>Give everything an ID </li></ul><ul><li>Use standard methods </li></ul><ul><li>Link things tog...
Stateless Communications <ul><li>HTTP protocol is  stateless </li></ul><ul><li>Everything  required to process  a  request...
Common Patterns: Container, Item Server in control of URI  <ul><li>Container  – a  collection  of items </li></ul><ul><li>...
Common Patterns: Map, Key, Value Client in control of URI   <ul><li>List  key-value pairs :  GET /map </li></ul><ul><li>Pu...
Key Benefits <ul><li>Server side </li></ul><ul><ul><li>Uniform  Interface </li></ul></ul><ul><ul><li>Cacheable </li></ul><...
Agenda <ul><li>REST Primer </li></ul><ul><li>RESTful Design and API Elements with JAX-RS </li></ul><ul><li>Building a Simp...
JAX-RS: Clear mapping to REST concepts <ul><li>High level, Declarative </li></ul><ul><ul><li>Uses  @ annotation  in POJOs ...
Give Everything an ID <ul><li>“Thing” == resource class </li></ul><ul><ul><li>POJO, No required interfaces </li></ul></ul>...
Use Standard Methods <ul><li>Annotate resource class methods with standard method </li></ul><ul><ul><li>@GET ,  @PUT ,  @P...
Multiple Representations Static and dynamic content negotiation <ul><li>Annotate methods or classes  </li></ul><ul><ul><li...
Multiple Representations : JAX-RS consuming <ul><li>Annotated  method  parameters  extract client request information </li...
Multiple Representations : JAX-RS consuming http://host/catalog/items/?start=0 http://host/catalog/items/123 @Path(&quot;/...
Multiple Representations : Supported Types <ul><li>JAX-RS can automatically (un)-marshall between  HTTP request/response  ...
Multiple Representations @Post @ConsumeMime(“application/x-www-form-urlencoded”) @ProduceMime(“application/xml”) public  J...
Multiple Representations : producing a response Use  Response  class to  build “created”response @Path(“/items”) class Ite...
Uniform interface: HTTP request and response C: POST /items HTTP/1.1 C: Host: host.com C: Content-Type: application/xml C:...
Response Codes and Custom Responses <ul><li>JAX-RS returns default response codes </li></ul><ul><ul><li>GET returns 200 OK...
JAX-RS provides Response class @PUT public  Response  putUser( @PathParam(“id”) int id) { if (!id.equals(userid) ) return ...
WebApplicationException Example @GET public  Employee  getEmployee( @PathParam(“id”) int id) { if (!doesEmployeeExist(id))...
Link Things Together <ul><li>UriInfo  provides  information  about the  request URI  and the route to the resource </li></...
Statelessness: JAX-RS <ul><li>The default  component lifecycle  is  per-request </li></ul><ul><ul><li>A  new instance  cre...
Agenda <ul><li>REST Primer </li></ul><ul><li>RESTful Design and API Elements </li></ul><ul><li>Building a Simple Service  ...
Example RESTful Catalog
Example RESTful Catalog http://weblogs.java.net/blog/caroljmcdonald/archive/2008/08/a_restful_pet_c.html
Example RESTful Catalog Service  Catalog Database Web container (GlassFish™) + REST API  Browser (Firefox) HTTP
URIs  and  Methods: <ul><li>/items </li></ul><ul><ul><li>GET - list all items </li></ul></ul><ul><ul><li>POST – add item t...
Methods  <ul><ul><li>Java method name is not significant </li></ul></ul><ul><ul><ul><li>The @HTTP method is the method </l...
RESTful Catalog  <ul><ul><ul><li>Dojo client, JAX-RS, JAXB, JPA </li></ul></ul></ul>DB Registration Application JAX-RS cla...
Entity Classes <ul><li>Use JPA to map/retrieve data from the database as entities </li></ul><ul><li>expose entities as RES...
Converter Classes <ul><li>JAXB  to convert the domain objects (JPA  entities ) into  XML and/or JSON . </li></ul>DB JAX-RS...
ItemConverter JAXB annotated  @XmlRootElement(name = &quot;item&quot;) public class ItemConverter { private Item entity; p...
XML <item  uri=&quot;http://localhost/Web/resources/items/1/&quot; > <description> black cat is nice</description> <id>1</...
JSON <ul><li>{ </li></ul><ul><li>&quot;@uri&quot;:&quot;http://host/catalog/resources/items/1/&quot;, </li></ul><ul><li>&q...
ItemsConverter JAXB annotated  @XmlRootElement(name = &quot;items&quot;) public class ItemsConverter { private Collection<...
XML <?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?> <items uri=&quot;http://localhost/Web/resources/items/&quot...
JSON <ul><li>{ </li></ul><ul><li>&quot;@uri&quot;:&quot;http://host/catalog/resources/items/&quot;, </li></ul><ul><li>&quo...
Resource Classes <ul><ul><li>Items Resource  retrieves updates a  collection of Item entities </li></ul></ul><ul><ul><li>/...
Get Items  @Path(&quot;/items/&quot;) public class ItemsResource { @Context protected UriInfo uriInfo; @GET @Produces (&qu...
Get  Item @Path(&quot;/items/&quot;) public class  ItemsResource  { @Path(&quot;{id}/&quot;) public ItemResource getItemRe...
Dojo Client <ul><li>Use dojo.xhrGet  to make HTTP method call to catalog service to get JSON items data: </li></ul><ul><li...
Example RESTful Catalog
Dojo client  index.html <button dojoType=&quot;dijit.form.Button&quot; onclick=&quot; next &quot;> Next </button> <div id=...
Dojo client.js   formatImage  = function(value) { if (!value) return '&nbsp;';  return &quot;<img src='&quot; + value + &q...
RESTful Pet Catalog Web Service  http://petstore/catalog/resources/items/ HTTP  GET {&quot;url&quot;:&quot;http://store/ca...
Dojo client.js   // make request to the items web service function  loadTable (page){ start = page * batchSize; var  targe...
Client  & WADL <ul><li>NetBeans IDE can generate JavaScript client stubs from a  WADL  or  projects  containing RESTful re...
Example RESTful Catalog
 
JavaFX Stage Scene  Stage  is the  top level container window Scene  is a drawing surface  container that holds the scene ...
JavaFX Stage Scene  // Application User Interface var stageContent: Node[]; stageContent = [bgImage,  nextButton, backButt...
JavaFX Stage Scene  var stageContent: Node[]; stageContent  = [bgImage,  nextButton, backButton,  titleText,  thumbImageVi...
RESTful Pet Catalog Web Service  http://petstore/catalog/resources/items/ HTTP  GET Response XML items <item> <imageurl>ht...
JavaFX  HttpRequest function loadImageMetadata() { var start=page * 9;  var request: HttpRequest = HttpRequest { location:...
JavaFX  HttpRequest public class PhotoPullParser { public function parse(input: InputStream): Photo[] { var  photos: Photo...
Agenda <ul><li>REST Primer </li></ul><ul><li>RESTful Design and API Elements </li></ul><ul><li>Building a Simple Service <...
Java SE <ul><li>RuntimeDelegate  used to create instances of a desired endpoint class </li></ul><ul><li>Application suppli...
Servlet <ul><li>JAX-RS application packaged in  WAR  like a servlet </li></ul><ul><li>For JAX-RS aware containers </li></u...
Java EE 6 Plans <ul><li>JAX-RS is part of Java EE 6 </li></ul><ul><li>Applications deployed in a Java EE 6 Web container w...
Agenda <ul><li>REST Primer </li></ul><ul><li>RESTful Design and API Elements </li></ul><ul><li>Building a Simple Service <...
Summary <ul><li>REST architecture is gaining popularity </li></ul><ul><ul><li>Simple, scalable and the infrastructure is a...
For More Information <ul><li>Official JSR Page </li></ul><ul><ul><li>http://jcp.org/en/jsr/detail?id=311 </li></ul></ul><u...
<ul><li>Presenter’s Name </li></ul><ul><ul><li>[email_address] </li></ul></ul>Carol McDonald  Java Architect http://weblog...
Upcoming SlideShare
Loading in...5
×

RESTful Web Services with JAX-RS

11,897

Published on

How to use JAX-RS to build REST Web services

Published in: Technology
0 Comments
24 Likes
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total Views
11,897
On Slideshare
0
From Embeds
0
Number of Embeds
2
Actions
Shares
0
Downloads
823
Comments
0
Likes
24
Embeds 0
No embeds

No notes for slide
  • Transcript of "RESTful Web Services with JAX-RS"

    1. 1. REST Carol McDonald, Java Architect
    2. 2. Agenda <ul><li>REST Primer </li></ul><ul><li>RESTful Design and API Elements </li></ul><ul><li>Building a Simple Service </li></ul><ul><li>Status </li></ul><ul><li>Q & A </li></ul>
    3. 3. REpresentational State Transfer <ul><li>Get </li></ul><ul><li>http://www.depot.com/parts </li></ul>Response XML data = REpresentational State Transfer <ul><ul><ul><li>The URL identifies the resource </li></ul></ul></ul><ul><ul><ul><li>HTTP GET method operates on the resource </li></ul></ul></ul><ul><ul><ul><li>html page is transferred to the browser </li></ul></ul></ul><ul><ul><ul><ul><li>REpresentational State transfer </li></ul></ul></ul></ul><ul><ul><ul><li>The page ( application state ) is stored on the client (browser) </li></ul></ul></ul><ul><ul><ul><li>Click on the link (resource) in page ( hypermedia ) </li></ul></ul></ul><ul><ul><ul><ul><li>New state transfer occurs </li></ul></ul></ul></ul>
    4. 4. REST architecture for the Web Web platforms Addressable Resources Web Container Addressable Resources Web Container
    5. 5. REST Tenets <ul><li>Resources ( nouns ) </li></ul><ul><ul><li>Identified by a URI , For example: </li></ul></ul><ul><ul><ul><li>http://www.parts-depot.com/parts </li></ul></ul></ul><ul><li>Methods ( verbs ) to manipulate the nouns </li></ul><ul><ul><li>Small fixed set: </li></ul></ul><ul><ul><ul><li>GET, PUT, POST, DELETE </li></ul></ul></ul><ul><ul><ul><ul><li>Read, Update, Create, Delete </li></ul></ul></ul></ul><ul><li>Representation of the Resource </li></ul><ul><ul><li>data and state transferred between client and server </li></ul></ul><ul><ul><li>XML, JSON ... </li></ul></ul><ul><li>Use verbs to exchange application state and representation </li></ul>
    6. 6. HTTP Example Request GET /music/artists/beatles/recordings HTTP/1.1 Host: media.example.com Accept: application/xml Response HTTP/1.1 200 OK Date: Tue, 08 May 2007 16:41:58 GMT Server: Apache/1.3.6 Content-Type: application/xml; charset=UTF-8 <?xml version=&quot;1.0&quot;?> <recordings xmlns=&quot;…&quot;> <recording>…</recording> … </recordings> Method Resource Representation State transfer
    7. 7. REST in Five Steps* <ul><li>Give everything an ID </li></ul><ul><li>Use standard methods </li></ul><ul><li>Link things together </li></ul><ul><li>Multiple representations </li></ul><ul><li>Stateless communications </li></ul><ul><ul><li>http://www.infoq.com/articles/rest-introduction </li></ul></ul>Uniform Interface
    8. 8. Give Every Thing an Id http://company.com/customers/123456 Resource Collection name Primary key http://company.com/customers/123456/orders/12 http://example.com/orders/2007/11 http://example.com/products?color=green URI is the id, Every resource has a URI <ul><li>URIs identify : </li></ul><ul><ul><li>items, collections of items, virtual and physical objects, or computation results. </li></ul></ul>
    9. 9. REST in Five Steps* <ul><li>Give everything an ID </li></ul><ul><li>Use standard methods </li></ul><ul><li>Link things together </li></ul><ul><li>Multiple representations </li></ul><ul><li>Stateless communications </li></ul><ul><ul><li>http://www.infoq.com/articles/rest-introduction </li></ul></ul>Uniform Interface
    10. 10. Use Standard HTTP Methods: GET = Read <ul><li>GET to retrieve information </li></ul><ul><ul><li>Should not modify anything </li></ul></ul><ul><ul><li>Cacheable </li></ul></ul><ul><ul><li>Example </li></ul></ul><ul><ul><ul><li>GET /store/customers/123456 </li></ul></ul></ul>
    11. 11. Use Standard HTTP Methods: POST = Create <ul><li>POST to add new information </li></ul><ul><li>add the entity , append to the container resource </li></ul><ul><ul><ul><li>POST /store/customers </li></ul></ul></ul><ul><ul><ul><li><customer> </li></ul></ul></ul><ul><ul><ul><li><name>carol</name> </li></ul></ul></ul><ul><ul><ul><li></customer> </li></ul></ul></ul><ul><ul><ul><li>Response </li></ul></ul></ul><ul><ul><ul><li>HTTP/1.1 201 Created </li></ul></ul></ul><ul><ul><ul><li>Location: http://store/customers/789 </li></ul></ul></ul>
    12. 12. Use Standard HTTP Methods: PUT = Update <ul><li>PUT to update information </li></ul><ul><li>Full entity create/replace used when you know the “ id ” </li></ul><ul><ul><li>Example </li></ul></ul><ul><ul><ul><li>PUT /store/customers/123456 </li></ul></ul></ul><ul><ul><ul><li><customer> </li></ul></ul></ul><ul><ul><ul><li><name>jane</name> </li></ul></ul></ul><ul><ul><ul><li></customer> </li></ul></ul></ul>
    13. 13. Use Standard HTTP Methods: DELETE <ul><li>Remove (logical) an entity </li></ul><ul><ul><li>Example </li></ul></ul><ul><ul><ul><li>DELETE /store/customers/123456 </li></ul></ul></ul>
    14. 14. Use Standard Methods: <ul><ul><li>http://www.infoq.com/articles/rest-introduction </li></ul></ul>Order Customer Mgmt Example
    15. 15. Use Standard Methods: <ul><li>/orders </li></ul><ul><ul><li>GET - list all orders </li></ul></ul><ul><ul><li>POST - submit a new order </li></ul></ul><ul><li>/orders/{order-id} </li></ul><ul><ul><li>GET - get an order representation </li></ul></ul><ul><ul><li>PUT - update an order </li></ul></ul><ul><ul><li>DELETE - cancel an order </li></ul></ul><ul><li>/orders/average-sale </li></ul><ul><ul><li>GET - calculate average sale </li></ul></ul><ul><li>/customers </li></ul><ul><ul><li>GET - list all customers </li></ul></ul><ul><ul><li>POST - create a new customer </li></ul></ul><ul><li>/customers/{cust-id} </li></ul><ul><ul><li>GET - get a customer representation </li></ul></ul><ul><ul><li>DELETE- remove a customer </li></ul></ul><ul><li>/customers/{cust-id}/orders </li></ul><ul><ul><li>GET - get the orders of a customer </li></ul></ul>Order Customer Mgmt Example <ul><ul><li>http://www.infoq.com/articles/rest-introduction </li></ul></ul>
    16. 16. Use Standard HTTP Methods <ul><li>HTTP Get, Head </li></ul><ul><ul><li>Should not modify anything </li></ul></ul><ul><ul><li>Cache-able </li></ul></ul><ul><ul><ul><li>With Correct use of Last-Modified and ETag </li></ul></ul></ul><ul><li>Idempotency: </li></ul><ul><ul><li>PUT, DELETE, GET, HEAD can be repeated and the results are the same </li></ul></ul>
    17. 17. REST in Five Steps* <ul><li>Give everything an ID </li></ul><ul><li>Use standard methods </li></ul><ul><li>Link things together </li></ul><ul><li>Multiple representations </li></ul><ul><li>Stateless communications * Inspired by Stefan Tilkov: http://www.innoq.com/blog/st/presentations/2008/2008-03-13-REST-Intro--QCon-London.pdf </li></ul>
    18. 18. Link Things Together <ul><li>Representations contain links to other resources </li></ul><ul><li>Service provides links in response to the Client </li></ul><ul><ul><li>Enables client to move the application from one state to the next by following a link </li></ul></ul><ul><ul><li>Representations contain links to other resources: </li></ul></ul><prop self=&quot; http://example.com/orders/101230 &quot;> <customer ref=&quot; http://example.com/customers/bar &quot;> <product ref=&quot; http://example.com/products/21034 &quot;/> <amount value=&quot;1&quot;/> </order>
    19. 19. Link Things Together <ul><li>Representations contain links to other resources </li></ul><ul><li>Put a service in different states by following links and filling in forms </li></ul><ul><ul><li>“ Hypermedia as the engine of application state” </li></ul></ul><ul><ul><li>HTML, XML, JSON, N3 can all be Hypermedia </li></ul></ul><ul><li>Client is in charge </li></ul><ul><ul><li>Service offers potential states </li></ul></ul><ul><ul><li>Service does not impose state transition order </li></ul></ul>
    20. 20. REST in Five Steps* <ul><li>Give everything an ID </li></ul><ul><li>Use standard methods </li></ul><ul><li>Link things together </li></ul><ul><li>Multiple representations </li></ul><ul><li>Stateless communications * Inspired by Stefan Tilkov: http://www.innoq.com/blog/st/presentations/2008/2008-03-13-REST-Intro--QCon-London.pdf </li></ul>
    21. 21. Multiple Representations <ul><li>Offer data in a variety of formats, for different needs </li></ul><ul><ul><li>XML </li></ul></ul><ul><ul><li>JSON </li></ul></ul><ul><ul><li>(X)HTML </li></ul></ul><ul><li>Support content negotiation </li></ul><ul><ul><li>Accept header GET /foo Accept: application/json </li></ul></ul><ul><ul><li>URI-based GET / foo.json </li></ul></ul>
    22. 22. <ul><ul><li>content negotiation </li></ul></ul>Multiple Representations Content-type HTTP header Accept HTTP header Request GET /music/artists/beatles/recordings HTTP/1.1 Host: media.example.com Accept : application/xml Response HTTP/1.1 200 OK Date: Tue, 08 May 2007 16:41:58 GMT Server: Apache/1.3.6 Content-Type : application/xml; charset=UTF-8 <?xml version=&quot;1.0&quot;?> <recordings xmlns=&quot;…&quot;> <recording>…</recording> … </recordings> Format Representation
    23. 23. REST in Five Steps* <ul><li>Give everything an ID </li></ul><ul><li>Use standard methods </li></ul><ul><li>Link things together </li></ul><ul><li>Multiple representations </li></ul><ul><li>Stateless communications * Inspired by Stefan Tilkov: http://www.innoq.com/blog/st/presentations/2008/2008-03-13-REST-Intro--QCon-London.pdf </li></ul>
    24. 24. Stateless Communications <ul><li>HTTP protocol is stateless </li></ul><ul><li>Everything required to process a request contained in the request </li></ul><ul><ul><li>No client session on the server </li></ul></ul><ul><ul><li>Eliminates many failure conditions </li></ul></ul><ul><li>application state kept on Client </li></ul><ul><li>Service responsible for resource state </li></ul>
    25. 25. Common Patterns: Container, Item Server in control of URI <ul><li>Container – a collection of items </li></ul><ul><li>List catalog items: GET /catalog/items </li></ul><ul><li>Add item to container: POST /catalog/items </li></ul><ul><ul><li>with item in request </li></ul></ul><ul><ul><li>URI of item returned in HTTP response header </li></ul></ul><ul><ul><li>e.g. http://host/ catalog/items/1 </li></ul></ul><ul><li>Update item: PUT /catalog/items/1 </li></ul><ul><ul><li>with updated item in request </li></ul></ul><ul><li>Good example: Atom Publishing Protocol </li></ul>
    26. 26. Common Patterns: Map, Key, Value Client in control of URI <ul><li>List key-value pairs : GET /map </li></ul><ul><li>Put new value to map: PUT /map/{key} </li></ul><ul><ul><li>with entry in request </li></ul></ul><ul><ul><li>e.g. PUT /map/dir/contents.xml </li></ul></ul><ul><li>Read value: GET /map/{key} </li></ul><ul><li>Update value: PUT /map/{key} </li></ul><ul><ul><li>with updated value in request </li></ul></ul><ul><li>Remove value: DELETE /map/{key} </li></ul><ul><li>Good example: Amazon S3 </li></ul>
    27. 27. Key Benefits <ul><li>Server side </li></ul><ul><ul><li>Uniform Interface </li></ul></ul><ul><ul><li>Cacheable </li></ul></ul><ul><ul><li>Scalable </li></ul></ul><ul><ul><li>Easy failover </li></ul></ul><ul><li>Client side </li></ul><ul><ul><li>Easy to experiment in browser </li></ul></ul><ul><ul><li>Broad programming language support </li></ul></ul><ul><ul><li>Choice of data formats </li></ul></ul><ul><ul><li>bookmarkable </li></ul></ul>
    28. 28. Agenda <ul><li>REST Primer </li></ul><ul><li>RESTful Design and API Elements with JAX-RS </li></ul><ul><li>Building a Simple Service </li></ul><ul><li>Status </li></ul><ul><li>Q & A </li></ul>
    29. 29. JAX-RS: Clear mapping to REST concepts <ul><li>High level, Declarative </li></ul><ul><ul><li>Uses @ annotation in POJOs </li></ul></ul><ul><li>Jersey – reference implementation of JSR 311 </li></ul><ul><ul><ul><li>Download it from http://jersey.dev.java.net </li></ul></ul></ul><ul><ul><ul><li>Comes with Glassfish, Java EE 6 </li></ul></ul></ul><ul><ul><ul><li>Tools support in NetBeans </li></ul></ul></ul><ul><ul><li>Four other open source implementations: </li></ul></ul><ul><ul><ul><li>Apache CXF </li></ul></ul></ul><ul><ul><ul><li>JBoss RESTEasy </li></ul></ul></ul><ul><ul><ul><li>Restlet </li></ul></ul></ul><ul><ul><ul><li>Triaxrs </li></ul></ul></ul>
    30. 30. Give Everything an ID <ul><li>“Thing” == resource class </li></ul><ul><ul><li>POJO, No required interfaces </li></ul></ul><ul><li>ID provided by @Path annotation </li></ul><ul><ul><li>Relative to deployment context </li></ul></ul><ul><ul><li>Annotate class or “ sub-resource locator” method </li></ul></ul>http://host/ctx/orders/12 http://host/ctx/orders/12/customer @Path( &quot;orders/{id}&quot;) public class OrderResource { @Path(&quot;customer&quot;) CustomerResource getCustomer(...) {...} }
    31. 31. Use Standard Methods <ul><li>Annotate resource class methods with standard method </li></ul><ul><ul><li>@GET , @PUT , @POST , @DELETE , @HEAD </li></ul></ul><ul><li>annotations on parameters specify mapping from request data </li></ul><ul><li>Return value mapped to http response </li></ul>@Path(&quot;orders/ {order_id} &quot;) public class OrderResource { @GET Order getOrder( @PathParam (&quot; order_id &quot;) String id) { ... } }
    32. 32. Multiple Representations Static and dynamic content negotiation <ul><li>Annotate methods or classes </li></ul><ul><ul><li>@Produces matches Accepts header </li></ul></ul><ul><ul><li>@Consumes matches Content-Type header </li></ul></ul>@GET @Consumes(&quot;application/json&quot;) @Produces({&quot;application/xml&quot;,&quot;application/json&quot;}) String getOrder(@PathParam(&quot;order_id&quot;) String id) { ... }
    33. 33. Multiple Representations : JAX-RS consuming <ul><li>Annotated method parameters extract client request information </li></ul><ul><ul><li>@PathParam extracts information from the request URI </li></ul></ul><ul><ul><li>@QueryParam extracts information from the request URI query parameters </li></ul></ul>http://host/catalog/items/ 123 http://host/catalog/items/ ?start=0
    34. 34. Multiple Representations : JAX-RS consuming http://host/catalog/items/?start=0 http://host/catalog/items/123 @Path(&quot;/items/&quot;) @ConsumeMime(“application/xml”) public class ItemsResource { @GET ItemsConverter get( @QueryParam (&quot;start&quot;) int start ) { ... } @Path( &quot;{id}/&quot; ) ItemResource getItemResource( @PathParam (&quot;id&quot;)Long id ){ ... } }
    35. 35. Multiple Representations : Supported Types <ul><li>JAX-RS can automatically (un)-marshall between HTTP request/response and Java types </li></ul><ul><li>“Out-of-the-box” support for the following </li></ul><ul><ul><li>text/xml, application/xml, application/json – JAXB class </li></ul></ul><ul><ul><li>*/* – byte[] </li></ul></ul><ul><ul><li>text/* – String </li></ul></ul><ul><ul><li>application/x-www-form-urlencoded - MultivaluedMap<String, String> </li></ul></ul>response
    36. 36. Multiple Representations @Post @ConsumeMime(“application/x-www-form-urlencoded”) @ProduceMime(“application/xml”) public JAXBClass updateEmployee( MultivalueMap<String, String> form) { ... Converted to a map for accessing form's field converted to XML
    37. 37. Multiple Representations : producing a response Use Response class to build “created”response @Path(“/items”) class Items { @POST @ProduceMime(“application/xml”) Response create(Ent e) { // persist the new entry, create URI return Response.created ( uriInfo.getAbsolutePath(). resolve(uri+&quot;/&quot;)).build(); } }
    38. 38. Uniform interface: HTTP request and response C: POST /items HTTP/1.1 C: Host: host.com C: Content-Type: application/xml C: Content-Length: 35 C: C: <item><name>dog</name></item> S: HTTP/1.1 201 Created S: Location: http://host.com/employees/1234 S: Content-Length: 0
    39. 39. Response Codes and Custom Responses <ul><li>JAX-RS returns default response codes </li></ul><ul><ul><li>GET returns 200 OK </li></ul></ul><ul><ul><li>PUT returns 201 CREATED </li></ul></ul><ul><li>See </li></ul><ul><ul><li>http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html </li></ul></ul><ul><ul><li>200 OK </li></ul></ul><ul><ul><li>201 Created </li></ul></ul><ul><ul><li>202 Accepted </li></ul></ul><ul><ul><li>203 Non-Authoritative Information </li></ul></ul><ul><ul><li>204 No Content </li></ul></ul><ul><ul><li>205 Reset Content </li></ul></ul><ul><ul><li>206 Partial Content </li></ul></ul><ul><ul><li>207 Multi-Status </li></ul></ul><ul><ul><li>226 IM Used </li></ul></ul><ul><ul><li>. . . </li></ul></ul>
    40. 40. JAX-RS provides Response class @PUT public Response putUser( @PathParam(“id”) int id) { if (!id.equals(userid) ) return Response .status(409).entity( &quot;userids differ&quot;).build(); <ul><ul><li>JAX-RS provides Response class: </li></ul></ul><ul><ul><ul><li>to specify response codes, to add onto the response </li></ul></ul></ul><ul><li>can construct more sophisticated return result </li></ul><ul><li>Can build responses for redirects, errors, ok, setting headers, etc </li></ul>
    41. 41. WebApplicationException Example @GET public Employee getEmployee( @PathParam(“id”) int id) { if (!doesEmployeeExist(id)) throw new WebApplicationException( new Throwable(&quot;Resource for &quot; + uriInfo.getAbsolutePath() + &quot; does not exist.&quot;), 404); Response: HTTP Status 404 - <ul><li>Use it when the method has a return type </li></ul><ul><li>Simple error indicator </li></ul>
    42. 42. Link Things Together <ul><li>UriInfo provides information about the request URI and the route to the resource </li></ul><ul><li>UriBuilder provides facilities to easily build URIs for resources </li></ul>@Context UriInfo info ; OrderResource r = ... UriBuilder b = info.getBaseUriBuilder(); URI u = b.path(OrderResource.class).build(r.id);
    43. 43. Statelessness: JAX-RS <ul><li>The default component lifecycle is per-request </li></ul><ul><ul><li>A new instance created for every request </li></ul></ul><ul><ul><li>Reduces concurrency issues </li></ul></ul><ul><li>HTTP session life-cycle is not supported </li></ul><ul><ul><li>On server side Developer must model state </li></ul></ul><ul><ul><ul><li>As resource state in the representations </li></ul></ul></ul>
    44. 44. Agenda <ul><li>REST Primer </li></ul><ul><li>RESTful Design and API Elements </li></ul><ul><li>Building a Simple Service </li></ul><ul><li>Deployment Options </li></ul><ul><li>Status </li></ul>
    45. 45. Example RESTful Catalog
    46. 46. Example RESTful Catalog http://weblogs.java.net/blog/caroljmcdonald/archive/2008/08/a_restful_pet_c.html
    47. 47. Example RESTful Catalog Service Catalog Database Web container (GlassFish™) + REST API Browser (Firefox) HTTP
    48. 48. URIs and Methods: <ul><li>/items </li></ul><ul><ul><li>GET - list all items </li></ul></ul><ul><ul><li>POST – add item to catalog </li></ul></ul><ul><li>/items/{id} </li></ul><ul><ul><li>GET - get an item representation </li></ul></ul><ul><ul><li>PUT - update an item </li></ul></ul><ul><ul><li>DELETE – remove an item </li></ul></ul>Item Catalog Example <ul><ul><li>http://www.infoq.com/articles/rest-introduction </li></ul></ul>
    49. 49. Methods <ul><ul><li>Java method name is not significant </li></ul></ul><ul><ul><ul><li>The @HTTP method is the method </li></ul></ul></ul>@Path(“/items”) class ItemsResource { @GET Items get() { ... } @POST Response create(Item) { ... } } class ItemResource { @GET Item get(...) { ... } @PUT void update(...) { ... } @DELETE void delete(...) { ... } }
    50. 50. RESTful Catalog <ul><ul><ul><li>Dojo client, JAX-RS, JAXB, JPA </li></ul></ul></ul>DB Registration Application JAX-RS class Dojo client JAXB class Entity Class ItemsConverter Item ItemsResource
    51. 51. Entity Classes <ul><li>Use JPA to map/retrieve data from the database as entities </li></ul><ul><li>expose entities as RESTful resources </li></ul><ul><ul><li>entity = resource identified by URL </li></ul></ul>http://host/catalog/items/123 public class Item { int id; String name; String descr; String url; } @Entity @Id Item ID NAME DESC URL
    52. 52. Converter Classes <ul><li>JAXB to convert the domain objects (JPA entities ) into XML and/or JSON . </li></ul>DB JAX-RS class Dojo client JAXB class Entity Class ItemsConverter Item ItemsResource
    53. 53. ItemConverter JAXB annotated @XmlRootElement(name = &quot;item&quot;) public class ItemConverter { private Item entity; private URI uri; @XmlAttribute public URI getUri() { return uri; } @XmlElement public Long getId() { return (expandLevel > 0) ? entity.getId() : null; } ... }
    54. 54. XML <item uri=&quot;http://localhost/Web/resources/items/1/&quot; > <description> black cat is nice</description> <id>1</id> <imagethumburl>/images/anth.jpg</imagethumburl> <name>not Friendly Cat</name> <price>307.10</price> <productid>feline01</productid> </item>
    55. 55. JSON <ul><li>{ </li></ul><ul><li>&quot;@uri&quot;:&quot;http://host/catalog/resources/items/1/&quot;, </li></ul><ul><li>&quot;name&quot;:&quot;Friendly Cat&quot;, </li></ul><ul><li>&quot;description&quot;:&quot;This black and white colored cat is super friendly.&quot;, </li></ul><ul><li>&quot;id&quot;:&quot;1&quot;, </li></ul><ul><li>&quot;imageurl&quot;:&quot; http://localhost:8080/CatalogService/images/anthony.jpg &quot; </li></ul><ul><li>} </li></ul>
    56. 56. ItemsConverter JAXB annotated @XmlRootElement(name = &quot;items&quot;) public class ItemsConverter { private Collection<ItemConverter> items; private URI uri; @XmlAttribute public URI getUri() { return uri; } @XmlElement public Collection<ItemConverter> getItem() { ... return items; } }
    57. 57. XML <?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?> <items uri=&quot;http://localhost/Web/resources/items/&quot;> <item uri=&quot;http://localhost/Web/resources/items/1/&quot; > <description> black cat is nice</description> <id>1</id> <imagethumburl>/images/anth.jpg</imagethumburl> <name>not Friendly Cat</name> <price>307.10</price> <productid>feline01</productid> </item> <item . . . </item> </items>
    58. 58. JSON <ul><li>{ </li></ul><ul><li>&quot;@uri&quot;:&quot;http://host/catalog/resources/items/&quot;, </li></ul><ul><li>&quot; item &quot;:[ </li></ul><ul><li>{&quot;@uri&quot;:&quot;http://host/catalog/resources/items/1/&quot;, </li></ul><ul><li>&quot;name&quot;:&quot;Friendly Cat&quot;, </li></ul><ul><li>&quot;description&quot;:&quot;This black and white colored cat is super friendly.&quot;, </li></ul><ul><li>&quot;id&quot;:&quot;1&quot;, </li></ul><ul><li>&quot;imageurl&quot;:&quot;http://localhost:8080/CatalogService/images/anthony.jpg&quot;}, </li></ul><ul><li>{&quot;@uri&quot;:&quot;http://host/catalog/resources/items/2/&quot;, </li></ul><ul><li>&quot;name&quot;:&quot;Fluffy Cat&quot;, </li></ul><ul><li>&quot;description&quot;:&quot;A great pet for a hair stylist! </li></ul><ul><li>&quot;id&quot;:&quot;2&quot;, </li></ul><ul><li>&quot;imageurl&quot;:&quot;http://localhost:8080/CatalogService/images/bailey.jpg&quot;} </li></ul><ul><li>] </li></ul><ul><li>} </li></ul>
    59. 59. Resource Classes <ul><ul><li>Items Resource retrieves updates a collection of Item entities </li></ul></ul><ul><ul><li>/items – URI for a list of Items </li></ul></ul><ul><ul><li>Item resource retrieves or updates one Item entity </li></ul></ul><ul><ul><li>/item/1 – URI for item 1 </li></ul></ul>DB JAX-RS class Dojo client JAXB class Entity Class ItemsConverter Item ItemsResource
    60. 60. Get Items @Path(&quot;/items/&quot;) public class ItemsResource { @Context protected UriInfo uriInfo; @GET @Produces (&quot;application/json&quot;) public ItemsConverter get(){ return new ItemsConverter( getEntities(), uriInfo.getAbsolutePath()); } Performs JPA Query, returns list of entities JAXB class responds with JSON responds to the URI http://host/catalog/items/ responds to HTTP GET
    61. 61. Get Item @Path(&quot;/items/&quot;) public class ItemsResource { @Path(&quot;{id}/&quot;) public ItemResource getItemResource( @PathParam (&quot;id&quot;) Long id) { return new ItemResource (id, context); } } public class ItemResource { @GET @Produces ( &quot;application/json&quot;) public ItemConverter get() { return new ItemConverter(getEntity(), context.getAbsolutePath(), expandLevel); } JAXB class http://host/catalog/items/123
    62. 62. Dojo Client <ul><li>Use dojo.xhrGet to make HTTP method call to catalog service to get JSON items data: </li></ul><ul><li>{&quot; items &quot;: </li></ul><ul><li>{&quot;@uri&quot;:&quot;http://host/catalog/resources/items/&quot;, </li></ul><ul><li>&quot; item &quot;:[ </li></ul><ul><li>{&quot;@uri&quot;:&quot;http://host/catalog/resources/items/1/&quot;, </li></ul><ul><li>&quot;name&quot;:&quot;Friendly Cat&quot;, </li></ul><ul><li>&quot;description&quot;:&quot;This black and white colored cat is super friendly.&quot;, </li></ul><ul><li>&quot;id&quot;:&quot;1&quot;, </li></ul><ul><li>&quot;imageurl&quot;:&quot;http://localhost:8080/CatalogService/images/anthony.jpg&quot;}, </li></ul><ul><li>{&quot;@uri&quot;:&quot;http://host/catalog/resources/items/2/&quot;, </li></ul><ul><li>&quot;name&quot;:&quot;Fluffy Cat&quot;, </li></ul><ul><li>&quot;description&quot;:&quot;A great pet for a hair stylist! </li></ul><ul><li>&quot;id&quot;:&quot;2&quot;, </li></ul><ul><li>&quot;imageurl&quot;:&quot;http://localhost:8080/CatalogService/images/bailey.jpg&quot;} </li></ul><ul><li>] </li></ul><ul><li>} </li></ul><ul><li>} </li></ul>
    63. 63. Example RESTful Catalog
    64. 64. Dojo client index.html <button dojoType=&quot;dijit.form.Button&quot; onclick=&quot; next &quot;> Next </button> <div id=&quot;grid&quot; dojoType =&quot; dojox.Grid &quot; model =&quot; model &quot; structure =&quot; layout &quot; autoWidth=&quot;true&quot; > http://weblogs.java.net/blog/caroljmcdonald/archive/2008/08/a_restful_pet_c.html Grid widget
    65. 65. Dojo client.js formatImage = function(value) { if (!value) return '&nbsp;'; return &quot;<img src='&quot; + value + &quot;'/>&quot;; }; // Data Grid layout // A grid view is a group of columns var view1 = { cells: [ [ {name: 'Name', field: &quot;name&quot;}, {name: 'Description', field: &quot;description&quot;}, {name: 'Photo',field: &quot;imagethumburl&quot;, formatter: formatImage , }, {name: 'Price',field: &quot;price&quot;} ] ] }; // a grid layout is an array of views. var layout = [ view1 ]; // the model= collection of objects to be displayed in the grid model = new dojox.grid.data.Objects(null,null);
    66. 66. RESTful Pet Catalog Web Service http://petstore/catalog/resources/items/ HTTP GET {&quot;url&quot;:&quot;http://store/catalog/item1&quot;, {&quot;url&quot;:&quot;http://store/catalog/item2&quot;} Response JSON slide urls Server Client Addressable Resources Web Container
    67. 67. Dojo client.js // make request to the items web service function loadTable (page){ start = page * batchSize; var targetURL = &quot;resources/items/?start=&quot;+ encodeURIComponent(start); dojo.xhrGet ({ url: targetURL , handleAs: &quot;json&quot;, load: handleResponse , error: handleError }); } // Process the response from the items web service function handleResponse ( responseObject , ioArgs){ // set the model object with the returned items list model .setData( responseObject.items.item ); } function next() { page =page + 1; loadTable (page); } Performs HTTP GET on url catalog/items
    68. 68. Client & WADL <ul><li>NetBeans IDE can generate JavaScript client stubs from a WADL or projects containing RESTful resources </li></ul><ul><li>NetBeans IDE exposes popular SaaS services using WADLs and can generate Java code to access them </li></ul><ul><ul><li>Learn more at https://wadl.dev.java.net/ </li></ul></ul><ul><ul><li>Example: </li></ul></ul><ul><ul><ul><li><application> </li></ul></ul></ul><ul><ul><ul><li><resources base=&quot; http://localhost:11109/CustomerDB/resources/ &quot;> </li></ul></ul></ul><ul><ul><ul><li><resource path=&quot;/items/&quot;> </li></ul></ul></ul><ul><ul><ul><li><method name=&quot;GET&quot;> </li></ul></ul></ul><ul><ul><ul><li><request> </li></ul></ul></ul><ul><ul><ul><li><param default=&quot;0&quot; type=&quot;xs:int&quot; style=&quot;query&quot; name=&quot;start&quot;/> </li></ul></ul></ul><ul><ul><ul><li><param default=&quot;10&quot; type=&quot;xs:int&quot; style=&quot;query&quot; name=&quot;max&quot;/> </li></ul></ul></ul><ul><ul><ul><li></request> </li></ul></ul></ul><ul><ul><ul><li><response> </li></ul></ul></ul><ul><ul><ul><li><representation mediaType=&quot;application/xml&quot;/> </li></ul></ul></ul><ul><ul><ul><li></response> </li></ul></ul></ul><ul><ul><ul><li></method> </li></ul></ul></ul><ul><ul><ul><li></resource> </li></ul></ul></ul><ul><ul><ul><li></resources> </li></ul></ul></ul><ul><ul><ul><li>..... </li></ul></ul></ul><ul><ul><ul><li></application> </li></ul></ul></ul>
    69. 69. Example RESTful Catalog
    70. 71. JavaFX Stage Scene Stage is the top level container window Scene is a drawing surface container that holds the scene graph nodes. content holds JavaFX graphical elements which define the graphical content of the application.
    71. 72. JavaFX Stage Scene // Application User Interface var stageContent: Node[]; stageContent = [bgImage, nextButton, backButton, titleText, thumbImageViewGroup, fullImageView ]; def stage = Stage { title: &quot;Pet Catalog&quot; width: 201 height: 201 scene : Scene { content : Group { content: bind stageContent } fill: Color.TRANSPARENT } } top level container window container holds the scene graph holds graphical elements
    72. 73. JavaFX Stage Scene var stageContent: Node[]; stageContent = [bgImage, nextButton, backButton, titleText, thumbImageViewGroup, fullImageView ]; def stage = Stage { title: &quot;Pet Catalog&quot; width: 201 height: 201 scene : Scene { content : Group { content: bind stageContent } fill: Color.TRANSPARENT } }
    73. 74. RESTful Pet Catalog Web Service http://petstore/catalog/resources/items/ HTTP GET Response XML items <item> <imageurl>http://host/catalog/images/anthony.jpg</imageurl> <name>Friendly Cat</name> <price>307.10</price> <productid>feline01</productid> </item> Server Client Addressable Resources Web Container
    74. 75. JavaFX HttpRequest function loadImageMetadata() { var start=page * 9; var request: HttpRequest = HttpRequest { location: &quot; http://localhost:8080/catalog/resources/items/ &quot; method: HttpRequest.GET onInput: function(input: java.io.InputStream) { var parser = PhotoPullParser{}; photos = parser.parse(input) ; } onDone: function() { updateImages() ; } } request.enqueue(); } Performs HTTP GET on url catalog/items
    75. 76. JavaFX HttpRequest public class PhotoPullParser { public function parse(input: InputStream): Photo[] { var photos: Photo[]; var photo: Photo; def parser = PullParser {input: input onEvent : function(event: Event) { if ( event.type == PullParser.START_ELEMENT ) { if(event.qname.name == &quot; item &quot; and event.level == 1) { photo = Photo { }; } } else if ( event.type == PullParser.END_ELEMENT ) { if( event.qname.name == &quot; item &quot; and event.level == 1) { insert photo into photos; } else if( event.qname.name == &quot; imagethumburl &quot; and event.level == 2) { photo.imagethumburl = event.text; } ... } } } parser.parse(); return photos; } <item> <imageurl>http://y.jpg</imageurl> <name>Friendly Cat</name> ... </item>
    76. 77. Agenda <ul><li>REST Primer </li></ul><ul><li>RESTful Design and API Elements </li></ul><ul><li>Building a Simple Service </li></ul><ul><li>Status </li></ul><ul><li>Q & A </li></ul>
    77. 78. Java SE <ul><li>RuntimeDelegate used to create instances of a desired endpoint class </li></ul><ul><li>Application supplies configuration information </li></ul><ul><ul><li>List of resource classes and providers as subclass of ApplicationConfig </li></ul></ul><ul><li>Implementations can support any Java type </li></ul><ul><ul><li>Jersey supports Grizzly (see below), LW HTTP server and JAX-WS Provider </li></ul></ul>ApplicationConfig config = ... RuntimeDelegate rd = RuntimeDelegate.getInstance(); Adapter a = rd.createEndpoint(config, Adapter.class); SelectorThread st = GrizzlyServerFactory.create( “ http://127.0.0.1:8084/”, a);
    78. 79. Servlet <ul><li>JAX-RS application packaged in WAR like a servlet </li></ul><ul><li>For JAX-RS aware containers </li></ul><ul><ul><li>web.xml can point to ApplicationConfig subclass </li></ul></ul><ul><li>For non-JAX-RS aware containers </li></ul><ul><ul><li>web.xml points to implementation-specific Servlet ; and </li></ul></ul><ul><ul><li>an init-param identifies the ApplicationConfig subclass </li></ul></ul><ul><li>Resource classes and providers can access Servlet request, context, config and response via injection </li></ul>
    79. 80. Java EE 6 Plans <ul><li>JAX-RS is part of Java EE 6 </li></ul><ul><li>Applications deployed in a Java EE 6 Web container will have access to additional resources: </li></ul><ul><ul><li>Resources @Resources </li></ul></ul><ul><ul><li>EJB @EJB </li></ul></ul><ul><ul><li>... </li></ul></ul><ul><li>Stateless session EJBs as resource classes </li></ul><ul><li>More portable deployment with Servlet 3.0 </li></ul>
    80. 81. Agenda <ul><li>REST Primer </li></ul><ul><li>RESTful Design and API Elements </li></ul><ul><li>Building a Simple Service </li></ul><ul><li>Deployment Options </li></ul><ul><li>Status </li></ul>
    81. 82. Summary <ul><li>REST architecture is gaining popularity </li></ul><ul><ul><li>Simple, scalable and the infrastructure is already in place </li></ul></ul><ul><li>JAX-RS (JSR-311) provides a high level declarative programming model </li></ul><ul><ul><li>http://jersey.dev.java.net </li></ul></ul><ul><li>Jersey available </li></ul><ul><li>1.0 and 1.0.1: Glassfsh v2/v3 </li></ul><ul><li>1.0: NetBeans 6.5 </li></ul>
    82. 83. For More Information <ul><li>Official JSR Page </li></ul><ul><ul><li>http://jcp.org/en/jsr/detail?id=311 </li></ul></ul><ul><li>JSR Project </li></ul><ul><ul><li>http://jsr311.dev.java.net/ </li></ul></ul><ul><li>Reference Implementation </li></ul><ul><ul><li>http://jersey.dev.java.net/ </li></ul></ul><ul><li>Marc's Blog </li></ul><ul><ul><li>http://weblogs.java.net/blog/mhadley/ </li></ul></ul><ul><li>Paul's Blog </li></ul><ul><ul><li>http://blogs.sun.com/sandoz/ </li></ul></ul><ul><li>Jakub's Blog </li></ul><ul><ul><li>http://blogs.sun.com/japod/ </li></ul></ul><ul><li>Carol's Blog </li></ul><ul><ul><li>http://weblogs.java.net/blog/caroljmcdonald/ </li></ul></ul>
    83. 84. <ul><li>Presenter’s Name </li></ul><ul><ul><li>[email_address] </li></ul></ul>Carol McDonald Java Architect http://weblogs.java.net/blog/caroljmcdonald/
    1. A particular slide catching your eye?

      Clipping is a handy way to collect important slides you want to go back to later.

    ×