Domino, XML-RPC and Java - A Primer

Jake Howlett, 5 June 2003

Category: Java; Keywords: XML-RPC server client

Yesterday, for reasons I won't go in to just yet, I started looking in to using a Domino agent as an XML-RPC server. What I found was that there was a complete lack of decent documentation. Sure there were quite a few articles but they all seemed to skip over the essential example code that I required.

This article is to try and fill that gap and hopefully the next time somebody is in my shoes they will find this to be just what they were looking for.

A Little Background:

Wondering what XML-RPC is? Read on. Even if you think it has no bearing on you whatsoever, you may well be pleasantly surprised what it has to offer!

From the XML-RPC homepage:

[a] set of implementations that allow software running on disparate operating systems, running in different environments to make procedure calls over the Internet.

That's all the technical mumbo-jumbo I will bother you with. The trouble with specs like this is that they go in to great detail about the ins and outs of the procedures when a lot of us don't really care. If you're like me you just want to know what it can do and how it does it.

In short RPC is a way of using HTTP to send a call to a function on any system that will reply with the result of that method. The beauty of RPC is that, because it uses HTTP as the transport, it operates through port 80 (by default). This means that it can be used in almost all environments. Including those that use paranoid firewalls and port blocking. If you can browse the web you can use RPC!

What XML-RPC looks like:

Obviously it's XML. No surprises there. Let's look at an example. Say we want to call a function on our Domino server that finds the location of a user's mail file. The function you might in imagine in your mind as:

getUsersMailFileLocation("Jake Howlett/codestore");

In RPC's XML it would look like this:

<?xml version="1.0"?>
<methodCall>

<methodName>getUsersMailFileLocation</methodName>

<params>

<param>

<value><string>Jake Howlett/codestore</string></value>

</param>

</params>

</methodCall>

You don't need to worry about this format too much as the XML-RPC server we are going to implement in our Java agent takes care of parsing it for all the relevant data.

So how would you send this XML to Domino? You would POST it, that's how. Like so:
POST /db.nsf/xml-rpc?OpenAgent HTTP/1.1
User-Agent: My Client 1.0 
Host: mydominoserver 
Content-Type: text/xml 
Content-Length: 209
How you do this depends on what client you're using to talk to Domino with and isn't really important just yet. Let's look at what the reply from the agent might look like.

<?xml version="1.0"?>
<methodResponse>

<params>

<param>

<value><string>prominic/codestore/mail/jake_howlett.nsf</string></value>

</param>

</params>

</methodResponse>

As you can see the answer to our question lies in the middle of all this XML. The calling client would then be responsible for parsing for this data.

The HTTP headers returned by Domino would look something like:
HTTP/1.1 200 OK
Server: Lotus-Domino
Date: Thu, 05 Jun 2003 10:25:39 GMT
Content-Type: text/xml
Content-Length: 158
So, now we know what the XML looks like, we need to start thinking about how we get Domino to return something useful to our client.

Coding the Domino agent:

I have to admit that, when I started this task yesterday, I wasn't 100% sure it would be possible. The problem (or at least I though it was) being that the XML is sent to the server as a POST reqeust. Having skimmed the spec of the Apache XML-RPC server I knew that it required the XML to be passed to it as an InputStream object. Would we be able to get a handle on this data and stream it to the XML-RPC server without resorting to a servlet? Well, yes, as it turned out, we could. Let's see how:
session = getSession();
AgentContext agentContext = session.getAgentContext();
db = agentContext.getCurrentDatabase();
Document doc = agentContext.getDocumentContext();
Item xml = doc.getFirstItem("Request_Content");
InputStream is = xml.getInputStream();

The important lines are the last two. In the second to last line we declare an Item object called "xml" that is the Request_Content field of the document context. When we POST to Domino all the data is held in here. In the case of RPC it's all the XML being sent. In the next line we use the getInputStream method of the Item and, hey presto, we have the InputStream we needed.

Now we need to initiate the XML-RPC server, define the method to handle the call and pass it the request. This is the part where I got really stuck. There's a lot of documentation out there but not a whole lot for Domino and Java. I got there in the end though.

The first thing you need to do is get yourself a copy of the Apacha XML-RPC code archive. I've attached this at the bottom of the document (xmlrpc-1.1.jar). With this on your local drive, click the Edit Project button in your Java agent and add it to the agent, as below:

Edit Project

You now have the server at your disposal and you can make it available to your code using the following import statement:
import org.apache.xmlrpc.*;
And you can create a new server like so:
XmlRpcServer xmlrpc = new XmlRpcServer ();
Now for the tricky part and the bit that I get very confused by. You need to add a "handler" to the server. This tells it what code (i.e. object) you would like to use to process the request. Here's how:
xmlrpc.addHandler ("domino", this );
Note the use of the keyword "this". This tells the server that the methods required to process calls reside in the current object. i.e within the JavaAgent object. If your code is in another class you would add a handler like so:
xmlrpc.addHandler ("domino", new myHandlerClass() );
Using code within the same class allows us easy access to all the Notes objects defined in the agent's code. Here's the method we would add if we wanted to process a call to the procedure domino.getUsersMailFileLocation:
public String getUsersMailFileLocation(String username) throws Exception
{
Database nab = session.getDatabase("", "names.nsf");
View people = nab.getView("($VIMPeople)");
Document person = people.getDocumentByKey(username, false);
return person.getItemValueString("MailFile");
}

To call it we have to call the execute method of our XML-RPC server, passing in to it the InputStream we defined above.
byte[] result = xmlrpc.execute( is );
Then we can pass result back to the calling client as some XML. For this bit of code I have to thank Johan Känngård for his debugging skills. I don't for one moment pretend to be any kind of Java guru. Having spent hours trying to work out why it wasn't working I got in touch with Johan. He soon told me it was because I was using result.toString(). Doh!
pw.println("content-type:text/xml");
pw.println( new String(result) );

And that about covers the basic requirements of an XML-RPC server running on top of Domino. The full code of the agent is attached at the bottom of the article if you want to play with it or, god forbid, actually use it.

In summary:

Unlike most of my articles, where I try and give an example you can get your hands on, this one doesn't have anything you can play with as such. However, I wrote it with the intention of saving anybody else the trouble I went through yesterday. Next time you find yourself thinking "Maybe I could use RPC for that" you know where to come. The alternative is a case of trying to find your way through IBM's Web Services page without getting bogged down in techy-talk.

The title of this article included the word primer. I used this because I still have a way to go with this method in trying to add Blogger API functionality to the version 2 of the DomBlog Template. As I learn and progress I will share more solid (real world?) techniques with you. Hopefully then you will get an ideas as to how useful this can be. The possibilities are almost endless. Remember what I said at the beginning of this article: RPC operates through port 80 so almost anybody can use it no matter where they are.

If you do a search for "xml-rpc clients" you soon see there are lots out there, including clients for Flash and Oracle.

Addendum:

As I mentioned when I first wrote this article, it would have been made a little easier if we were using servlets rather than agents. In case you wondered what the equivalent code might look like in a servlet, Keith Nolen has kindly coded us one and I've attached it alongside the agent code.