dominoGuru.com
Your Development & Design Resource
IBM Lotus Notes Domino REST Web Services via XPage XAgents - Part 1
02/15/2010 06:10 AM by Chris Toohey
I asked, and you (thankfully) responded that you wanted to hear about XPage XAgents! So here's what I was thinking...
To get us started, you should check out the following Show-and-Tell-Thursday post from Stephan Wissel:
Go on... I'll still be here when you get back, and it's an awesome read. You'll thank me.
...
OK, welcome back!
When I've discussed IBM Lotus Notes Domino XPage Development, I found that the majority of Notes and Domino Developers think of XPages as a simple skin for their existing applications. Sure, it can help you paint feature-rich UIs -- which I'm certain UI development will be the biggest entry point to XPage Development -- but that's like saying Lotus Notes Domino is just an email system!
... see what I did there?? Moving on...
Stephen's example of creating an XPage XAgent is a huge step in the right direction, where we start leveraging the power of SSJS and the JSF to do things that quite frankly just can't be done with IBM Lotus Notes Domino Standard Design Elements.
Like what?, I hear you ask? Well, like RESTful NotesData APIs. Tim and I were trying our damnedest to get RESTful Web Services out of the Domino HTTP Stack... and we found that it failed miserably.
A quick refresher: RESTful Web Services = using GET, POST, PUT, and DELETE Method States to READ, CREATE, UPDATE, and DELETE target resources. In the world of IBM Lotus Notes Domino, this would mean that calling the following:
http://dominoserver/notesdatabase.nsf/view/notesdocument
Via the DELETE Method, should actually delete the target NotesDocument.
Domino - regardless of configuration - consumes the DELETE Method but returns the NotesDocument as if we used the GET Method State.
You can see where this might be an issue, right? Yeah - no RESTful Web Services for Domino, sadly...
But what about for XPages? Specifically, what about for SSJS and the JSF that could be surfaced via XPages?
<?xml version="1.0" encoding="UTF-8"?>
<xp:view xmlns:xp="http://www.ibm.com/xsp/core"
rendered="false">
<xp:this.resources>
<xp:script src="/lib_api.jss"
clientSide="false"></xp:script>
</xp:this.resources>
<xp:this.afterRenderResponse><![CDATA[#{javascript:
var cgi = new CGIVariables();
var exCon = facesContext.getExternalContext();
var writer = facesContext.getResponseWriter();
var response = exCon.getResponse();
response.setContentType("text/plain");
response.setHeader("Cache-Control", "no-cache");
writer.write(cgi.REQUEST_METHOD);
writer.endDocument();}]]></xp:this.afterRenderResponse>
</xp:view>
The above lib_api.jss contains the Script Library from How to access CGI variables in XPages.
Before we test our proof of concept... we need to make sure that our Domino Server is configured to allow RESTful Methods.
We want to confirm that GET and POST -- which should be enabled by default -- are indeed enabled along with the PUT and DELETE Methods.
Now, using the REST Client of your choice -- I'm using a Firefox add-on aptly named RESTClient -- we'll test our XPage XAgent:
That's an IBM Lotus Notes Domino XPage XAgent consuming a DELETE Method HTTP Request!
The only issue here is -- quite frankly -- this still isn't REST. Aside from your XPage XAgent not actually doing anything with the DELETE HTTP Request, it's not pointing to a NotesDocument URI.
See, to properly Create, Read, Update or Delete via REST, the URI of the target object you wish to perform said action against needs to more or less be the recipient of your HTTP Request. We got this to work with an XPage XAgent, not with a NotesDocument. So our actual target URL schema should look something like this:
http://domino/dir/db.nsf/view/notesdocument
Call that with a POST, and it should Create a new NotesDocument.
Call that with a GET, and it should Read/Return an existing NotesDocument.
PUT will Update, and DELETE will -- say it with me -- Delete the defined NotesDocument.
(For my next trick, I'll need a Lotus Domino Admin...)
We're going to use Internet Site Documents in combination with Substitution Rules!
For those of you not familiar with Internet Site Documents and in particular Substitution Rules, I'll give you an example.
I use this approach mostly for maintining old/bad permalinks that are no longer permalinks (and all-around Search Engine Optimization) for dominoGuru.com, so it's high-time I put it to good use for an application! It just so happens to work rather brilliantly.
When I first setup the NotesDatabase that runs this site, it was located in the following subdirectory structure:
/dominoguru/home.nsf
A few years ago I moved it to the following subdirectory structure:
/hosting/dg/site.nsf
What you may not know is that -- with an IBM Lotus Notes Domino Internet Site Document Substitution Rule -- the target URL/URI is not redirected (there is no 302 HTTP Header Response issued). What this means, is that you could hit dominoGuru.com via http://www.dominoguru.com/dominoguru/home.nsf/index.html even though it's no longer in that subdirectory... and the HTTP Request URI remains unchanged.
Ya see where I'm going with this?!
Let's add two lines to our XPage XAgent:
<?xml version="1.0" encoding="UTF-8"?>
<xp:view xmlns:xp="http://www.ibm.com/xsp/core"
rendered="false">
<xp:this.resources>
<xp:script src="/lib_api.jss"
clientSide="false"></xp:script>
</xp:this.resources>
<xp:this.afterRenderResponse><![CDATA[#{javascript:
var cgi = new CGIVariables();
var exCon = facesContext.getExternalContext();
var writer = facesContext.getResponseWriter();
var response = exCon.getResponse();
response.setContentType("text/plain");
response.setHeader("Cache-Control", "no-cache");
writer.write(cgi.REQUEST_METHOD);
writer.write(cgi.REQUEST_URI);
writer.write(cgi.QUERY_STRING);
writer.endDocument();}]]></xp:this.afterRenderResponse>
</xp:view>
Now, let's say you create api.example.com as an Internet Site Document for a NotesDatabase Application that you want to REST Web Service-enable. You could easily add the following Substitution Rule to your Internet Site Document:
/api.nsf/* -> /api.nsf/api.xsp
That's it! All of your requests that go to api.nsf
will be
consumed by your XPage XAgent!
In the next part of this series, we'll actually make our XPage XAgent functional, and create our Method Handler Controller via SSJS -- Stay tuned!