Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

ICONUK 2016: REST Assured, Freeing Your Domino Data Has Never Been That Easy!

126 views

Published on

ICONUK 2016 Developer Session on RESTful Services with IBM Domino Server and Apache Wink. September 16, 2016

Published in: Software
  • Be the first to comment

  • Be the first to like this

ICONUK 2016: REST Assured, Freeing Your Domino Data Has Never Been That Easy!

  1. 1. REST Assured, Freeing Your Domino Data Has Never Been That Easy! Serdar Basegmez, Developi Information Systems 16th September 2016
  2. 2. • IBM Champion (2011 - 2016) • Developi Information Systems, Istanbul • Contributing… • OpenNTF / LUGTR / LotusNotus.com • Featured on… • Engage UG, IBM Connect, ICON UK, NotesIn9… • Also… • Blogger and Podcaster on Scientific Skepticism Serdar Başeğmez
  3. 3. RESTful Web Services Representational state transfer (REST) is an architectural style used for web development. Systems and sites designed using this style aim for fast performance, reliability and the ability to scale (to grow and easily support extra users). To achieve these goals, developers work with reusable components that can be managed and updated without affecting the system as a whole while it is running. Source: https://en.wikipedia.org/wiki/Representational_state_transfer
  4. 4. History
  5. 5. Old School Web Applications Source: https://speakerdeck.com/jeffschenck/rest-easy-api-security-done-right User Interface Business Logic Datastore Front-end Back-end ASP, PHP, CGI, Web Agents, JSP, etc. ← HTML, CSS, JavaScript Forms →
  6. 6. Web Applications Evolving User Interface Business Logic Datastore Front-end Back-end Async web apps, Ruby on Rails, Django, JSF, XPages, etc. ← HTML, CSS, JavaScript Forms, AJAX →
  7. 7. Web Applications Evolving User Interface Business Logic Datastore Front-end Back-end Modern Web frameworks, Angular.js, React.js, etc. ← HTML, CSS, JavaScript ← REST →
  8. 8. Web Applications Evolving User Interface Business Logic Datastore Mobile Applications Back-end Modern Web frameworks, Angular.js, React.js, etc. ← HTML, CSS, JavaScript ← REST → Front-end
  9. 9. Web Applications Evolving User Interface Business Logic Datastore Mobile Applications Back-end Modern Web frameworks, Angular.js, React.js, etc. ← HTML, CSS, JavaScript ← REST → Front-end Microservice Microservice Microservice
  10. 10. RESTful, Everywhere! Solid Architecture Well-defined practices Widespread use in modern frameworks Easily consumable in micro environments
  11. 11. Stateless / Cacheable / Layered Every request processed independently Everything cacheable Client does not care who cooked the meal in the kitchen ⇣ Scalable, Robust, Resilient
  12. 12. The Conversation Makes Sense! Source: http://www.bizcoder.com/a-fresh-coat-of-rest-paint-on-a-soap-stack
  13. 13. The Conversation Makes Sense! GET /twink/contacts/DLEY-ACLH6Y HTTP/1.1 Host: homer.developi.info Cache-Control: no-cache { "zip": "13202", "state": "NY", "lastName": "Abbate", "middle": "J", "country": "US", "emailAddress": "Jessica.J.Abbate@trashymail.com", "number": "DLEY-ACLH6Y", "city": "Syracuse", "firstName": "Jessica" }
  14. 14. The Conversation Makes Sense! http://appserver.company.com/apps/contacts.nsf/ GiveMeTheContactWeNeedPleaseAgent?OpenAgent&id=1522 or… http://appserver.company.com/api/contacts/1522
  15. 15. Conventions on URLs GET http://appserver.company.com/api/contacts GET http://appserver.company.com/api/contacts/UK/London POST http://appserver.company.com/api/contacts Retrieve Contacts / Create a new Contact…
  16. 16. Conventions on URLs GET http://appserver.company.com/api/contacts/1522 PUT http://appserver.company.com/api/contacts/1522 DELETE http://appserver.company.com/api/contacts/1522 Retrieve/Update/Delete the Contact resource with id=1522…
  17. 17. URI GET PUT POST DELETE /contacts/ List Contacts Replace Contacts Create New Contact Delete Contacts /contacts/id Retrieve a Contact Replace a Contact N/A (generally) Delete a Contact Source: https://en.wikipedia.org/wiki/Representational_state_transfer Conventions on URLs
  18. 18. Unconventional uses in URLs GET https://api.twitter.com/1.1/statuses/show.json?id=1234567890 Retrieve the Tweet with id=1234567890…
  19. 19. RESTful Services for IBM Domino Applications
  20. 20. Motivation Putting stuff into a small device! Socializing with other developers! Opening to the wild… New animals out there! Enough! We are moving… All / Some / None of the above
  21. 21. Options Domino Access Services (DAS) Extension Library Components for REST Hardcoding (XAgents, Web agents) Apache Wink Servlets
  22. 22. RESTful Options on Domino Benefits Challenges Suggested When? Domino Access Services
 (DAS) No Backend Code Zero-setup Limited Control No Business Logic Exposes the Internals Simple internal integrations ExtLib Components
 for REST Less Backend Code Minimal Setup Partial/Full Customization Error Handling Spaghetti Code URL Conventions Simple needs for a limited scope Hardcoding
 (XAgents, Web agents) Tailor-made No Learning Curve Hardcoding Everything Spaghetti Code URL Conventions Very specific needs for a limited scope Apache Wink Servlets Tailor-made Based on JAX-RS OSGi Benefits Learning Curve Barrier to Entry Large scope implementations, API Design
  23. 23. Apache Wink Project Complete implementation of JAX-RS v1.1 Specification Also includes RESTful Client module Extension Library comes with Apache Wink 1.1.2 Open Source Spring integration, WebDAV support
  24. 24. Apache Wink Runtime Application Code Apache Wink Basic Architecture Wink Servlet (Customizable) HTTP/HTTPS Client Datastore Resource Resource Resource Resource Controllers Data Accessors Tools/Utilities Request Processor Helpers /BaseURI/* /BaseURI/Path-Patterns
  25. 25. Resource and Resource Representation Collection ResourceResourceResource SubresourceSubresourceSubresource Resource • Any addressable object is a resource. • A resource class is; • Implements RESTful interactions (GET, POST, etc.) • A pure Java object decorated with annotations • Do not confuse with Model class. Resource Representation • The content of an object is called as Representation • JSON, XML, Text, Form data, etc.
  26. 26. @Path("/contacts") public class ContactResource { private DominoAccessor accessor = new DominoAccessor(ContextInfo.getUserSession()); @GET() public Response getContactList( @QueryParam("start") int start, @QueryParam("count") int count) { List<Contact> contactList = accessor.pullContacts(start, count); String result = ModelUtils.toJson(contactList).toString(); return Response.ok(result, MediaType.APPLICATION_JSON).build(); } @Path("/{id}") @GET() public Response getContact(@PathParam("id") String id) { Contact contact = accessor.findContact(id); if(null == contact) { throw new WebApplicationException(Response.Status.NOT_FOUND); } else { String result = ModelUtils.toJson(contact).toString(); return Response.ok(result, MediaType.APPLICATION_JSON).build(); } } } { "zip": "13202", "state": "NY", "lastName": "Abbate", "middle": "J", "country": "US", "emailAddress": "Jessica.J.Abbate@trashymail.com", "number": "DLEY-ACLH6Y", "city": "Syracuse", "firstName": "Jessica" } Contact Resource Class Contact Resource Short JSON Representation
  27. 27. Resources @Path("/contacts") public class ContactResource { private DominoAccessor accessor = new DominoAccessor(ContextInfo.getUserSession()); @GET() public Response getContactList( @QueryParam("start") int start, @QueryParam("count") int count) { List<Contact> contactList = accessor.pullContacts(start, count); String result = ModelUtils.toJson(contactList).toString(); return Response.ok(result, MediaType.APPLICATION_JSON).build(); } @Path("/{id}") @GET() public Response getContact(@PathParam("id") String id) { Contact contact = accessor.findContact(id); if(null == contact) { throw new WebApplicationException(Response.Status.NOT_FOUND); } else { String result = ModelUtils.toJson(contact).toString(); return Response.ok(result, MediaType.APPLICATION_JSON).build(); } } } The base URI for the resource In the demo, the root path of the plugin is “/twink”. So this class is enabled for requests made to: /twink/contacts/*
  28. 28. GET /twink/contacts/DLEY-ACJS7HGET /twink/contacts?start=10000&count=10
  29. 29. Resources @Path("/contacts") public class ContactResource { private DominoAccessor accessor = new DominoAccessor(ContextInfo.getUserSession()); @GET() public Response getContactList( @QueryParam("start") int start, @QueryParam("count") int count) { List<Contact> contactList = accessor.pullContacts(start, count); String result = ModelUtils.toJson(contactList).toString(); return Response.ok(result, MediaType.APPLICATION_JSON).build(); } @Path("/{id}") @GET() public Response getContact(@PathParam("id") String id) { Contact contact = accessor.findContact(id); if(null == contact) { throw new WebApplicationException(Response.Status.NOT_FOUND); } else { String result = ModelUtils.toJson(contact).toString(); return Response.ok(result, MediaType.APPLICATION_JSON).build(); } } } This method responds to GET requests. No path defined, so this is the default responder.
  30. 30. Resources @Path("/contacts") public class ContactResource { private DominoAccessor accessor = new DominoAccessor(ContextInfo.getUserSession()); @GET() public Response getContactList( @QueryParam("start") int start, @QueryParam("count") int count) { List<Contact> contactList = accessor.pullContacts(start, count); String result = ModelUtils.toJson(contactList).toString(); return Response.ok(result, MediaType.APPLICATION_JSON).build(); } @Path("/{id}") @GET() public Response getContact(@PathParam("id") String id) { Contact contact = accessor.findContact(id); if(null == contact) { throw new WebApplicationException(Response.Status.NOT_FOUND); } else { String result = ModelUtils.toJson(contact).toString(); return Response.ok(result, MediaType.APPLICATION_JSON).build(); } } } This method also responds to GET requests. But it the request path will be elected based on this format.
  31. 31. Resources @Path("/contacts") public class ContactResource { private DominoAccessor accessor = new DominoAccessor(ContextInfo.getUserSession()); @GET() public Response getContactList( @QueryParam("start") int start, @QueryParam("count") int count) { List<Contact> contactList = accessor.pullContacts(start, count); String result = ModelUtils.toJson(contactList).toString(); return Response.ok(result, MediaType.APPLICATION_JSON).build(); } @Path("/{id}") @GET() public Response getContact(@PathParam("id") String id) { Contact contact = accessor.findContact(id); if(null == contact) { throw new WebApplicationException(Response.Status.NOT_FOUND); } else { String result = ModelUtils.toJson(contact).toString(); return Response.ok(result, MediaType.APPLICATION_JSON).build(); } } } Parameters will be injected into methods. /contacts?start=X&count=Y /contacts/someId Wink servlet will handle type conversion. It supports ordinary java objects, enums, primitives, etc.
  32. 32. Resources @Path("/contacts") public class ContactResource { private DominoAccessor accessor = new DominoAccessor(ContextInfo.getUserSession()); @GET() public Response getContactList( @QueryParam("start") int start, @QueryParam("count") int count) { List<Contact> contactList = accessor.pullContacts(start, count); String result = ModelUtils.toJson(contactList).toString(); return Response.ok(result, MediaType.APPLICATION_JSON).build(); } @Path("/{id}") @GET() public Response getContact(@PathParam("id") String id) { Contact contact = accessor.findContact(id); if(null == contact) { throw new WebApplicationException(Response.Status.NOT_FOUND); } else { String result = ModelUtils.toJson(contact).toString(); return Response.ok(result, MediaType.APPLICATION_JSON).build(); } } } There are lots of options of returning response. ResponseBuilders and some other helpers make it quite easy.
  33. 33. Resources @Path("/contacts") public class ContactResource { private DominoAccessor accessor = new DominoAccessor(ContextInfo.getUserSession()); @GET() public Response getContactList( @QueryParam("start") int start, @QueryParam("count") int count) { List<Contact> contactList = accessor.pullContacts(start, count); String result = ModelUtils.toJson(contactList).toString(); return Response.ok(result, MediaType.APPLICATION_JSON).build(); } @Path("/{id}") @GET() public Response getContact(@PathParam("id") String id) { Contact contact = accessor.findContact(id); if(null == contact) { throw new WebApplicationException(Response.Status.NOT_FOUND); } else { String result = ModelUtils.toJson(contact).toString(); return Response.ok(result, MediaType.APPLICATION_JSON).build(); } } } Wink handles much of the error handling. Still you can inject your own errors.
  34. 34. Resources @Path("/contacts") public class ContactResource { ………… @POST() @Consumes(MediaType.APPLICATION_JSON) public Response postContactJson(String body) { Contact contact = ModelUtils.buildContactfromJson(body); accessor.saveNewContact(contact); String result = ModelUtils.toJson(contact).toString(); return Response.ok(result, MediaType.APPLICATION_JSON).build(); } @POST() @Consumes(MediaType.MULTIPART_FORM_DATA) public Response postContactForm(BufferedInMultiPart formData) { Contact contact = ModelUtils.buildContactfromMultipart(formData); accessor.saveNewContact(contact); String result = ModelUtils.toJson(contact).toString(); return Response.ok(result, MediaType.APPLICATION_JSON).build(); } } This methods respond to POST requests. This time the selection depends on the incoming data type. Client marks the request with Content-Type header and Wink will select the appropriate method here.
  35. 35. Resources @Path("/contacts") public class ContactResource { ………… @POST() @Consumes(MediaType.APPLICATION_JSON) public Response postContactJson(String body) { Contact contact = ModelUtils.buildContactfromJson(body); accessor.saveNewContact(contact); String result = ModelUtils.toJson(contact).toString(); return Response.ok(result, MediaType.APPLICATION_JSON).build(); } @POST() @Consumes(MediaType.MULTIPART_FORM_DATA) public Response postContactForm(BufferedInMultiPart formData) { Contact contact = ModelUtils.buildContactfromMultipart(formData); accessor.saveNewContact(contact); String result = ModelUtils.toJson(contact).toString(); return Response.ok(result, MediaType.APPLICATION_JSON).build(); } } Wink injects the incoming data into the method automatically. Apache Wink also provides several classes to process different data formats (Multipart, Atom, XML, JSON, etc.)
  36. 36. How to Start
  37. 37. What is your purpose? Quick and narrow-scoped services Moving your app to a different web framework Enable applications for native mobile access Create a REST API for your apps
  38. 38. Plan first! Determine resource types and capabilities to be allowed
 (Resources, Representations, actions, etc.) The distribution of tasks
 (Front-end and Back-end) responsibilities Collaborate with consumers, if you can Versioning / Test API
  39. 39. Sketch an architecture Keep your architecture layered Let your luggage be history Design as if the consumer will exploit your application, even you!
  40. 40. A sample architecture RESTful Resources ResourceResourceResource SubresourceSubresourceSubresource Model Classes Data Objects Conversion Resource Representation ←→ Model Data Access Model ←→ Documents Business Logic Actions (CRUD, etc.) Rules, validations, etc. Databases ResourceResourceDocuments ResourceResourceViews ResourceResourceetc. Security Utilities
  41. 41. Further Details
  42. 42. Getting hands dirty Test and Development Local Domino Server Domino Designer Client Eclipse / XPages SDK / Debug Plugin REST testing utility (e.g. Postman) Plugin Development Guides / Demos / Blogs Configure Eclipse Plugin template for Wink project Add Libraries to your project (See Resources section)
  43. 43. Annotations • @Path
 Specifies the relative path for a resource class or method • @GET, @PUT, @POST, @DELETE, @HEAD
 Specify the HTTP request type of a resource • @Produces
 Specifies the response Internet media types (content negotiation) • @Consumes
 Specifies the accepted request Internet media types.
  44. 44. Annotations • @PathParam
 Binds the method parameter to a path segment • @QueryParam, @MatrixParam, @FormParam
 Binds the method parameter to a query/matrix/form parameter • @HeaderParam, @CookieParam
 Binds the method parameter to a HTTP header/cookie parameter • @Context
 Returns the entire context of the object
 @Context HttpServletRequest request • @DefaultValue
 Specifies a default value for the above bindings when the key is not found.
 @Default(“1”) @QueryParam(“start”) int start
  45. 45. Annotations • @Provider
 Providers are used for transformation between entities and representations. Wink comes with several providers and more can be developed for special purposes. • @Asset
 More advanced implementation of providers. Especially suitable for automatic transformation between data objects and representations. • @Parent
 Defines a parent resource that has a base URI. (See Versioning) • @Scope
 By default, every resource class instantiated per request. Scope can define longer life cycles for resource instances (e.g. singletons).
  46. 46. JSON Handling • Wink and IBM Commons provide JSON Object helpers • A library for JSON processing strongly suggested • Hardcoding JSON data structure becomes more and more difficult. • Automatic Serialization / Deserialization is life saving • Tip: Look into Jackson and GSON libraries
  47. 47. Versioning @Path("/v1") public class com.developi.wink.demo.api.v1.VersionRoot {} @Parent(com.developi.wink.demo.api.v1.VersionRoot.class) @Path("/ping") public class com.developi.wink.demo.api.v1.PingResource { @GET public Response ping() { return Response.ok("<h1>Hello World Version 1!</h1>", MediaType.TEXT_HTML).build(); } } @Parent(com.developi.wink.demo.api.v2.VersionRoot.class) @Path("/ping") public class com.developi.wink.demo.api.v2.PingResource { @GET public Response ping() { return Response.ok("<h1>Hello World Version 2!</h1>", MediaType.TEXT_HTML).build(); } } @Path("/v2") public class com.developi.wink.demo.api.v2.VersionRoot {} Responds to “/root/v2/ping” Responds to “/root/v1/ping”
  48. 48. Notes Session • NotesSession related to the authenticated user: • ContextInfo.getUserSession() • At the servlet level, • No SessionAsSigner • No SessionAsSignerWithFullAccess • No CurrentDatabase • Elevated level of access is a bit tricky. • Refer to DominoRunner XSnippet
  49. 49. OpenNTF Domino API • OpenNTF Domino API is compatible with Apache Wink • One trick: You need to customize the servlet • Refer to the blog post by Paul Withers • Advantages • No recycle! • Modern Java practices (Maps, generics, etc.) • Much better development experience • Ability to use elevated session • Refer to the OpenNTF Domino API Project page for more
  50. 50. Wrap-up
  51. 51. Summary RESTful Services Architecture Designing RESTful services for Domino Applications Basic Concepts around RESTful Services Architecture Examples Annotations used by Apache Wink Some tricks for Domino developers
  52. 52. Takeaway Download and play with the template and demo plugins Experiment JAX-RS annotations Get yourself familiar with Plugin development Download Extension Library source code and look its design Study on RESTful design practices and JAX-RS concepts
  53. 53. Resources • Serdar Başeğmez: Demo Plugin and Apache Wink Template
 https://github.com/sbasegmez/RestAssuredDemo • Apache Wink Project
 https://wink.apache.org/ • Paul Withers: From XPages Hero To OSGi Guru: Taking The Scary Out Of Building Extension Libraries
 http://www.slideshare.net/paulswithers1/ibm-connected-2015-mas103-xpages-performance-and-scalability • Paul Withers: XPages OSGi Plugins series
 http://www.intec.co.uk/xpages-osgi-plugins-1-an-introduction/ • John Cooper: Domino OSGI (Part 1) - Configuring Eclipse for XPages OSGI Plugins
 http://developmentblog.johnmcooper.co.uk/2014/05/configuring-eclipse-for-xpages-osgi-plugins-part1.html • John Dalsgaard: Wrap An Existing Jar File Into A Plug-in
 https://www.dalsgaard-data.eu/blog/wrap-an-existing-jar-file-into-a-plug-in/ • Toby Samples: JAX-RS or THE way to do REST in Domino series
 https://tobysamples.wordpress.com/2015/04/28/jax-rs-or-the-way-to-do-rest-in-domino-part-1/ • Jesse Gallagher: Eclipse Tutorial for Domino Developers
 https://github.com/jesse-gallagher/eclipse-tutorial-oct2015/wiki/Java

×