Slide 1

Slide 1 text

REST Assured, Freeing Your Domino Data Has Never Been That Easy! Serdar Basegmez, Developi Information Systems 16th September 2016

Slide 2

Slide 2 text

• 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

Slide 3

Slide 3 text

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

Slide 4

Slide 4 text

History

Slide 5

Slide 5 text

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 →

Slide 6

Slide 6 text

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 →

Slide 7

Slide 7 text

Web Applications Evolving User Interface Business Logic Datastore Front-end Back-end Modern Web frameworks, Angular.js, React.js, etc. ← HTML, CSS, JavaScript ← REST →

Slide 8

Slide 8 text

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

Slide 9

Slide 9 text

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

Slide 10

Slide 10 text

RESTful, Everywhere! Solid Architecture Well-defined practices Widespread use in modern frameworks Easily consumable in micro environments

Slide 11

Slide 11 text

Stateless / Cacheable / Layered Every request processed independently Everything cacheable Client does not care who cooked the meal in the kitchen ⇣ Scalable, Robust, Resilient

Slide 12

Slide 12 text

The Conversation Makes Sense! Source: http://www.bizcoder.com/a-fresh-coat-of-rest-paint-on-a-soap-stack

Slide 13

Slide 13 text

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" }

Slide 14

Slide 14 text

The Conversation Makes Sense! http://appserver.company.com/apps/contacts.nsf/ GiveMeTheContactWeNeedPleaseAgent?OpenAgent&id=1522 or… http://appserver.company.com/api/contacts/1522

Slide 15

Slide 15 text

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…

Slide 16

Slide 16 text

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…

Slide 17

Slide 17 text

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

Slide 18

Slide 18 text

Unconventional uses in URLs GET https://api.twitter.com/1.1/statuses/show.json?id=1234567890 Retrieve the Tweet with id=1234567890…

Slide 19

Slide 19 text

RESTful Services for IBM Domino Applications

Slide 20

Slide 20 text

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

Slide 21

Slide 21 text

Options Domino Access Services (DAS) Extension Library Components for REST Hardcoding (XAgents, Web agents) Apache Wink Servlets

Slide 22

Slide 22 text

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

Slide 23

Slide 23 text

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

Slide 24

Slide 24 text

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

Slide 25

Slide 25 text

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.

Slide 26

Slide 26 text

@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 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

Slide 27

Slide 27 text

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 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/*

Slide 28

Slide 28 text

GET /twink/contacts/DLEY-ACJS7H GET /twink/contacts?start=10000&count=10

Slide 29

Slide 29 text

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 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.

Slide 30

Slide 30 text

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 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.

Slide 31

Slide 31 text

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 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.

Slide 32

Slide 32 text

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 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.

Slide 33

Slide 33 text

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 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.

Slide 34

Slide 34 text

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.

Slide 35

Slide 35 text

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.)

Slide 36

Slide 36 text

How to Start

Slide 37

Slide 37 text

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

Slide 38

Slide 38 text

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

Slide 39

Slide 39 text

Sketch an architecture Keep your architecture layered Let your luggage be history Design as if the consumer will exploit your application, even you!

Slide 40

Slide 40 text

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

Slide 41

Slide 41 text

Further Details

Slide 42

Slide 42 text

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)

Slide 43

Slide 43 text

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.

Slide 44

Slide 44 text

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

Slide 45

Slide 45 text

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).

Slide 46

Slide 46 text

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

Slide 47

Slide 47 text

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("

Hello World Version 1!

", 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("

Hello World Version 2!

", 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”

Slide 48

Slide 48 text

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

Slide 49

Slide 49 text

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

Slide 50

Slide 50 text

Wrap-up

Slide 51

Slide 51 text

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

Slide 52

Slide 52 text

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

Slide 53

Slide 53 text

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