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

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

Apr 12, 2017

Download

Software

Serdar Basegmez
Welcome message from author
This document is posted to help you gain knowledge. Please leave a comment to let me know what you think about it! Share it to your friends and learn new things together.
Transcript
Page 1: ICONUK 2016: REST Assured, Freeing Your Domino Data Has Never Been That Easy!

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

Serdar Basegmez, Developi Information Systems16th September 2016

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

• 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

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

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

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

History

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

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, JavaScriptForms →

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

Web Applications Evolving

User Interface Business Logic Datastore

Front-end Back-end

Async web apps, Ruby on Rails, Django, JSF, XPages, etc.

← HTML, CSS, JavaScriptForms, AJAX →

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

Web Applications Evolving

User Interface Business Logic Datastore

Front-end Back-end

Modern Web frameworks, Angular.js, React.js, etc.

← HTML, CSS, JavaScript ← REST →

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

Web Applications Evolving

User Interface Business Logic Datastore

Mobile ApplicationsBack-end

Modern Web frameworks, Angular.js, React.js, etc.

← HTML, CSS, JavaScript ← REST →

Front-end

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

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

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

RESTful, Everywhere!Solid Architecture

Well-defined practicesWidespread use in modern frameworks

Easily consumable in micro environments

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

Stateless / Cacheable / LayeredEvery request processed independently

Everything cacheableClient does not care who cooked the meal in the kitchen

⇣Scalable, Robust, Resilient

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

The Conversation Makes Sense!

Source: http://www.bizcoder.com/a-fresh-coat-of-rest-paint-on-a-soap-stack

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

The Conversation Makes Sense!GET/twink/contacts/DLEY-ACLH6YHTTP/1.1Host:homer.developi.infoCache-Control:no-cache

{"zip":"13202","state":"NY","lastName":"Abbate","middle":"J","country":"US","emailAddress":"[email protected]","number":"DLEY-ACLH6Y","city":"Syracuse","firstName":"Jessica"}

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

The Conversation Makes Sense!http://appserver.company.com/apps/contacts.nsf/

GiveMeTheContactWeNeedPleaseAgent?OpenAgent&id=1522

or…

http://appserver.company.com/api/contacts/1522

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

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…

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

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…

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

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

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

Unconventional uses in URLs

GET https://api.twitter.com/1.1/statuses/show.json?id=1234567890

Retrieve the Tweet with id=1234567890…

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

RESTful Servicesfor

IBM Domino Applications

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

MotivationPutting 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

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

OptionsDomino Access Services (DAS)

Extension Library Components for RESTHardcoding (XAgents, Web agents)

Apache Wink Servlets

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

RESTful Options on DominoBenefits Challenges Suggested When?

Domino Access Services (DAS)

No Backend CodeZero-setup

Limited ControlNo Business Logic

Exposes the InternalsSimple internal integrations

ExtLib Components for REST

Less Backend CodeMinimal Setup

Partial/Full Customization

Error HandlingSpaghetti Code

URL Conventions

Simple needs for a limited scope

Hardcoding (XAgents, Web agents)

Tailor-madeNo Learning Curve

Hardcoding EverythingSpaghetti Code

URL Conventions

Very specific needs for a limited scope

Apache Wink ServletsTailor-made

Based on JAX-RSOSGi Benefits

Learning CurveBarrier to Entry

Large scope implementations, API

Design

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

Apache Wink ProjectComplete implementation of JAX-RS v1.1 Specification

Also includes RESTful Client moduleExtension Library comes with Apache Wink 1.1.2

Open SourceSpring integration, WebDAV support

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

Apache Wink Runtime Application Code

Apache Wink Basic Architecture

Wink Servlet(Customizable)

HTTP/HTTPS Client

Datastore

Resource

Resource

Resource

Resource

ControllersData Accessors

Tools/Utilities

Request Processor

Helpers

/BaseURI/* /BaseURI/Path-Patterns

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

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.

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

@Path("/contacts")publicclassContactResource{

privateDominoAccessoraccessor=newDominoAccessor(ContextInfo.getUserSession()); @GET() publicResponsegetContactList(@QueryParam("start")intstart,@QueryParam("count")intcount){ List<Contact>contactList=accessor.pullContacts(start,count); Stringresult=ModelUtils.toJson(contactList).toString(); returnResponse.ok(result,MediaType.APPLICATION_JSON).build(); } @Path("/{id}") @GET() publicResponsegetContact(@PathParam("id")Stringid){ Contactcontact=accessor.findContact(id);

if(null==contact){ thrownewWebApplicationException(Response.Status.NOT_FOUND); }else{ Stringresult=ModelUtils.toJson(contact).toString(); returnResponse.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 ResourceShort JSON Representation

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

Resources@Path("/contacts")publicclassContactResource{

privateDominoAccessoraccessor=newDominoAccessor(ContextInfo.getUserSession()); @GET() publicResponsegetContactList(@QueryParam("start")intstart,@QueryParam("count")intcount){ List<Contact>contactList=accessor.pullContacts(start,count); Stringresult=ModelUtils.toJson(contactList).toString(); returnResponse.ok(result,MediaType.APPLICATION_JSON).build(); } @Path("/{id}") @GET() publicResponsegetContact(@PathParam("id")Stringid){ Contactcontact=accessor.findContact(id);

if(null==contact){ thrownewWebApplicationException(Response.Status.NOT_FOUND); }else{ Stringresult=ModelUtils.toJson(contact).toString(); returnResponse.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/*

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

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

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

Resources@Path("/contacts")publicclassContactResource{

privateDominoAccessoraccessor=newDominoAccessor(ContextInfo.getUserSession()); @GET() publicResponsegetContactList(@QueryParam("start")intstart,@QueryParam("count")intcount){ List<Contact>contactList=accessor.pullContacts(start,count); Stringresult=ModelUtils.toJson(contactList).toString(); returnResponse.ok(result,MediaType.APPLICATION_JSON).build(); } @Path("/{id}") @GET() publicResponsegetContact(@PathParam("id")Stringid){ Contactcontact=accessor.findContact(id);

if(null==contact){ thrownewWebApplicationException(Response.Status.NOT_FOUND); }else{ Stringresult=ModelUtils.toJson(contact).toString(); returnResponse.ok(result,MediaType.APPLICATION_JSON).build(); } }}

This method responds to GET requests.

No path defined, so this is the default responder.

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

Resources@Path("/contacts")publicclassContactResource{

privateDominoAccessoraccessor=newDominoAccessor(ContextInfo.getUserSession()); @GET() publicResponsegetContactList(@QueryParam("start")intstart,@QueryParam("count")intcount){ List<Contact>contactList=accessor.pullContacts(start,count); Stringresult=ModelUtils.toJson(contactList).toString(); returnResponse.ok(result,MediaType.APPLICATION_JSON).build(); } @Path("/{id}") @GET() publicResponsegetContact(@PathParam("id")Stringid){ Contactcontact=accessor.findContact(id);

if(null==contact){ thrownewWebApplicationException(Response.Status.NOT_FOUND); }else{ Stringresult=ModelUtils.toJson(contact).toString(); returnResponse.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.

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

Resources@Path("/contacts")publicclassContactResource{

privateDominoAccessoraccessor=newDominoAccessor(ContextInfo.getUserSession()); @GET() publicResponsegetContactList(@QueryParam("start")intstart,@QueryParam("count")intcount){ List<Contact>contactList=accessor.pullContacts(start,count); Stringresult=ModelUtils.toJson(contactList).toString(); returnResponse.ok(result,MediaType.APPLICATION_JSON).build(); } @Path("/{id}") @GET() publicResponsegetContact(@PathParam("id")Stringid){ Contactcontact=accessor.findContact(id);

if(null==contact){ thrownewWebApplicationException(Response.Status.NOT_FOUND); }else{ Stringresult=ModelUtils.toJson(contact).toString(); returnResponse.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.

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

Resources@Path("/contacts")publicclassContactResource{

privateDominoAccessoraccessor=newDominoAccessor(ContextInfo.getUserSession()); @GET() publicResponsegetContactList(@QueryParam("start")intstart,@QueryParam("count")intcount){ List<Contact>contactList=accessor.pullContacts(start,count); Stringresult=ModelUtils.toJson(contactList).toString(); returnResponse.ok(result,MediaType.APPLICATION_JSON).build(); } @Path("/{id}") @GET() publicResponsegetContact(@PathParam("id")Stringid){ Contactcontact=accessor.findContact(id);

if(null==contact){ thrownewWebApplicationException(Response.Status.NOT_FOUND); }else{ Stringresult=ModelUtils.toJson(contact).toString(); returnResponse.ok(result,MediaType.APPLICATION_JSON).build(); } }}

There are lots of options of returning response.

ResponseBuilders and some other helpers make it quite easy.

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

Resources@Path("/contacts")publicclassContactResource{

privateDominoAccessoraccessor=newDominoAccessor(ContextInfo.getUserSession()); @GET() publicResponsegetContactList(@QueryParam("start")intstart,@QueryParam("count")intcount){ List<Contact>contactList=accessor.pullContacts(start,count); Stringresult=ModelUtils.toJson(contactList).toString(); returnResponse.ok(result,MediaType.APPLICATION_JSON).build(); } @Path("/{id}") @GET() publicResponsegetContact(@PathParam("id")Stringid){ Contactcontact=accessor.findContact(id);

if(null==contact){ thrownewWebApplicationException(Response.Status.NOT_FOUND); }else{ Stringresult=ModelUtils.toJson(contact).toString(); returnResponse.ok(result,MediaType.APPLICATION_JSON).build(); } }}

Wink handles much of the error handling.

Still you can inject your own errors.

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

Resources@Path("/contacts")publicclassContactResource{

…………

@POST() @Consumes(MediaType.APPLICATION_JSON) publicResponsepostContactJson(Stringbody){ Contactcontact=ModelUtils.buildContactfromJson(body); accessor.saveNewContact(contact); Stringresult=ModelUtils.toJson(contact).toString(); returnResponse.ok(result,MediaType.APPLICATION_JSON).build(); }

@POST() @Consumes(MediaType.MULTIPART_FORM_DATA) publicResponsepostContactForm(BufferedInMultiPartformData){ Contactcontact=ModelUtils.buildContactfromMultipart(formData); accessor.saveNewContact(contact); Stringresult=ModelUtils.toJson(contact).toString(); returnResponse.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.

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

Resources@Path("/contacts")publicclassContactResource{

…………

@POST() @Consumes(MediaType.APPLICATION_JSON) publicResponsepostContactJson(Stringbody){ Contactcontact=ModelUtils.buildContactfromJson(body); accessor.saveNewContact(contact); Stringresult=ModelUtils.toJson(contact).toString(); returnResponse.ok(result,MediaType.APPLICATION_JSON).build(); }

@POST() @Consumes(MediaType.MULTIPART_FORM_DATA) publicResponsepostContactForm(BufferedInMultiPartformData){ Contactcontact=ModelUtils.buildContactfromMultipart(formData); accessor.saveNewContact(contact); Stringresult=ModelUtils.toJson(contact).toString(); returnResponse.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.)

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

How to Start

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

What is your purpose?Quick and narrow-scoped services

Moving your app to a different web frameworkEnable applications for native mobile access

Create a REST API for your apps

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

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

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

Sketch an architectureKeep your architecture layered

Let your luggage be history

Design as if the consumer will exploit your application, even you!

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

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

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

Further Details

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

Getting hands dirty

Test and Development

Local Domino ServerDomino Designer ClientEclipse / XPages SDK / Debug PluginREST testing utility (e.g. Postman)

Plugin Development

Guides / Demos / BlogsConfigure Eclipse

Plugin template for Wink projectAdd Libraries to your project

(See Resources section)

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

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)

• @ConsumesSpecifies the accepted request Internet media types.

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

Annotations• @PathParam

Binds the method parameter to a path segment

• @QueryParam, @MatrixParam, @FormParamBinds the method parameter to a query/matrix/form parameter

• @HeaderParam, @CookieParamBinds the method parameter to a HTTP header/cookie parameter

• @ContextReturns the entire context of the object@ContextHttpServletRequestrequest

• @DefaultValue Specifies a default value for the above bindings when the key is not found. @Default(“1”)@QueryParam(“start”)intstart

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

Annotations• @Provider

Providers are used for transformation between entities and representations. Wink comes with several providers and more can be developed for special purposes.

• @AssetMore advanced implementation of providers. Especially suitable for automatic transformation between data objects and representations.

• @ParentDefines 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).

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

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

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

Versioning

@Path("/v1")publicclasscom.developi.wink.demo.api.v1.VersionRoot{}

@Parent(com.developi.wink.demo.api.v1.VersionRoot.class)@Path("/ping")publicclasscom.developi.wink.demo.api.v1.PingResource{ @GETpublicResponseping(){ returnResponse.ok("<h1>HelloWorldVersion1!</h1>",MediaType.TEXT_HTML).build(); }}

@Parent(com.developi.wink.demo.api.v2.VersionRoot.class)@Path("/ping")publicclasscom.developi.wink.demo.api.v2.PingResource{ @GETpublicResponseping(){ returnResponse.ok("<h1>HelloWorldVersion2!</h1>",MediaType.TEXT_HTML).build(); }}

@Path("/v2")publicclasscom.developi.wink.demo.api.v2.VersionRoot{}

Responds to “/root/v2/ping”

Responds to “/root/v1/ping”

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

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

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

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

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

Wrap-up

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

SummaryRESTful Services Architecture

Designing RESTful services for Domino ApplicationsBasic Concepts around RESTful Services

Architecture ExamplesAnnotations used by Apache WinkSome tricks for Domino developers

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

TakeawayDownload and play with the template and demo plugins

Experiment JAX-RS annotationsGet yourself familiar with Plugin development

Download Extension Library source code and look its designStudy on RESTful design practices and JAX-RS concepts

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

Resources• Serdar Başeğmez: Demo Plugin and Apache Wink Template

https://github.com/sbasegmez/RestAssuredDemo

• Apache Wink Projecthttps://wink.apache.org/

• Paul Withers: From XPages Hero To OSGi Guru: Taking The Scary Out Of Building Extension Librarieshttp://www.slideshare.net/paulswithers1/ibm-connected-2015-mas103-xpages-performance-and-scalability

• Paul Withers: XPages OSGi Plugins serieshttp://www.intec.co.uk/xpages-osgi-plugins-1-an-introduction/

• John Cooper: Domino OSGI (Part 1) - Configuring Eclipse for XPages OSGI Pluginshttp://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 serieshttps://tobysamples.wordpress.com/2015/04/28/jax-rs-or-the-way-to-do-rest-in-domino-part-1/

• Jesse Gallagher: Eclipse Tutorial for Domino Developershttps://github.com/jesse-gallagher/eclipse-tutorial-oct2015/wiki/Java