Upgrade to Pro — share decks privately, control downloads, hide ads and more …

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

sbasegmez
September 16, 2016

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

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

sbasegmez

September 16, 2016
Tweet

More Decks by sbasegmez

Other Decks in Programming

Transcript

  1. REST Assured, Freeing Your Domino Data Has Never Been That

    Easy! Serdar Basegmez, Developi Information Systems 16th September 2016
  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. 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. 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 →
  5. 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 →
  6. Web Applications Evolving User Interface Business Logic Datastore Front-end Back-end

    Modern Web frameworks, Angular.js, React.js, etc. ← HTML, CSS, JavaScript ← REST →
  7. 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
  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 Microservice Microservice Microservice
  9. Stateless / Cacheable / Layered Every request processed independently Everything

    cacheable Client does not care who cooked the meal in the kitchen ⇣ Scalable, Robust, Resilient
  10. 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": "[email protected]", "number": "DLEY-ACLH6Y", "city": "Syracuse", "firstName": "Jessica" }
  11. 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
  12. 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
  13. Options Domino Access Services (DAS) Extension Library Components for REST

    Hardcoding (XAgents, Web agents) Apache Wink Servlets
  14. 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
  15. 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
  16. 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
  17. Resource and Resource Representation Collection Resource Resource Resource Subresource Subresource

    Subresource 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.
  18. @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": "[email protected]", "number": "DLEY-ACLH6Y", "city": "Syracuse", "firstName": "Jessica" } Contact Resource Class Contact Resource Short JSON Representation
  19. 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/*
  20. 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.
  21. 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.
  22. 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.
  23. 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.
  24. 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.
  25. 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.
  26. 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.)
  27. 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
  28. 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
  29. Sketch an architecture Keep your architecture layered Let your luggage

    be history Design as if the consumer will exploit your application, even you!
  30. A sample architecture RESTful Resources Resource Resource Resource Subresource Subresource

    Subresource Model Classes Data Objects Conversion Resource Representation ←→ Model Data Access Model ←→ Documents Business Logic Actions (CRUD, etc.) Rules, validations, etc. Databases Resource Resource Documents Resource Resource Views Resource Resource etc. Security Utilities
  31. 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)
  32. 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.
  33. 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
  34. 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).
  35. 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
  36. 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”
  37. 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
  38. 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
  39. 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
  40. 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
  41. 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