ANN: Jabber-RPC support in Groovy using the Smack library

Hi!

I just checked in an update to the Groovy XML-RPC implementation which provides Jabber-RPC support using the Smack library (The implementation took less than two days - your debugger is a godsend). It works just fine over GTalk.

There’'s an example of its use at http://docs.codehaus.org/pages/viewpage.action?pageId=31491

Thanks for the help on this forum and for producing such an easy to use library.

John Wilson

John,

Nice job! Would you be interested in contributing the Jabber-RPC to smackx as a standard extension? I think many users would be interested in the feature.

Regards,

Matt

I’'d be delighted if you were able to adopt some of this code.

The provider and packet classes for the JabberRPC iq packets would go into your smackx.jar with no trouble (the package name needs to be changed and a call to my XML-RPC processor which escapes <, & and > would need to be replaced - I’'m sure you have the equivalent function somewhere in Smack). They are pretty trivial classes

Key parts of the rest of the code has dependancies on Groovy.

I’'d be happy to work with you to see what bits we could reuse as part of Smack.

John Wilson

John,

Great! I’‘ve now given you permission to post attachments in the forum, which should make discussing code changes easier. For the escaping – the StringUtils.escapeForXML might do what you’'re looking for.

Do you want to start off by posting an overview of the API you’'ve created for doing RPC? We find that it usually helps to get general agreement on that followed by ensuring the implementation checks out.

Also, you’'ll need to sign a Smack Contributor Agreement at some point before the code goes into SVN. Info on the agreement is equivalent to the JM agreement at: http://www.jivesoftware.org/forums/ann.jspa?annID=2. The actual URL to the PDF is http://www.jivesoftware.org/smack_contributor_agreement.pdf.

Regards,

Matt

I have changed the code to use StringUtils and that looks fine. I attach the provider and the packet class files. As you can see they are completely trivial!

Its probably best if I give you an example of using the API in Groovy and than in Java

We are trying to expose the two static methods called “echo” on the following Java class:

class Echo {

public static String echo(final String s) {return s;}

public static int echo(final int i) {return i;}

}

This is how we do it in Groovy:

import groovy.net.xmlrpc.*

import org.jivesoftware.smack.XMPPConnection

def serverConnection = new XMPPConnection(“127.0.0.1”, 5222, “handel”)

def clientConnection = new XMPPConnection(“127.0.0.1”, 5222, “handel”)

serverConnection.login(“server”, “1234”)

clientConnection.login(“client”, “1234”)

println “Starting Server”

def server = new JabberRPCServer()

server.echo = Echo

server.startServer(serverConnection)

println “Started Server”

def proxy = new JabberRPCServerProxy(clientConnection, “server@handel”)

println proxy.echo(“hello”)

println proxy.echo(-3)

println proxy.echo(false)

The first few lines will be familliar to you

The JabberRPCServer instance will provide the service.

The line “server.echo = Echo” tells the server to expose all the static methods called “echo” in the class Echo.

Calling startServer starts the server listening on the connection for jabber-RPC packets. It’'s a multithreaded server so it can deal with mutiple simultaneous calls.

The rest of the script sets up a client connection to the server we have just started.

The JabberRPCServerProxy object wraps the client connection to be used to send the call.

You can just make calls on the proxy object and it will forward them to the remote server and return the result.

The result of executing the script is:

Starting Server

Started Server

hello

-3

Caught: groovy.net.xmlrpc.XMLRPCCallFailureException: XML-RPC call Failure: fault string = "No signature of method Echo.echo() is applicable for argument types: (java.lang.Boolean) values: ", fault code = 0

at Test.run(Test.groovy:24)

at Test.main(Test.groovy)

This is the eqivalent Java version:

public class TestSmack {

public static void main(String[] args) throws Exception {

final XMPPConnection serverConnection = new XMPPConnection(“127.0.0.1”, 5222, “handel”);

final XMPPConnection clientConnection = new XMPPConnection(“127.0.0.1”, 5222, “handel”);

serverConnection.login(“server”, “1234”);

clientConnection.login(“client”, “1234”);

System.out.println(“Starting Server”);

final JabberRPCServer server = new JabberRPCServer();

server.setProperty(“echo”, Echo.class);

server.startServer(serverConnection);

System.out.println(“Started Server”);

final JabberRPCServerProxy proxy = new JabberRPCServerProxy(clientConnection, “server@handel”);

System.out.println(proxy.invokeMethod(“echo”, new Object[]{“hello”}).toString());

System.out.println(proxy.invokeMethod(“echo”, new Object[]{new Integer(-2)}).toString());

System.out.println(proxy.invokeMethod(“echo”, new Object[]{Boolean.FALSE}).toString());

}

}
JabberRPCProvider.java (1871 Bytes)
JabberRPC.java (1003 Bytes)

Ok, makes sense so far. So, where’'s the “meat”? Is it in JabberRPCServerProxy?

Regards,

Matt

I have attached a zip of all the files in the Groovy XML-RPC/Jabber-RPC package. Other than the smack jars it just needs the groovy jar to compile.

The heart of the code is uk.co.wilson.net.xmlrpc.XMLMessageProcessor. This is a subclass of a very small and fast XML parser called MinML. MinML supports SAX1.

XMLMessageProcessor has an instance method called ParseMessage which expects an XML-RPC call, response or fault document. If it gets a call it extracts the name of the method being called and the parametres to the call - the parameters are held as an array of Object. If it gets a reponse it extracts the reurn value into a one elment Object array. If it gets a fault it thows an exception containing the fault details.

It has two static helper methods:

encodeString() does the normal XML escaping (currently it assumes and encoding of 8859-1) and checks for illegal caracter values (0X00, for example).

emit() does most of the work in prodicung the XML call and response. The top and tail of the message is currently generated elsewhere.

Thre is one dependancy on Groovy at the moment as it need to handle Groovy’‘s special String type. I’’ reasonably confiden thet this dependancy could be removed.

I’'ll post separtate messages explaining how messages are sent and recieved
code.zip (40079 Bytes)

Message sending is done by JabberRPCServerProxy which is a subclass of RPCServerProxy. RPCServerProxy is a Groovy object and uses Groovy’'s dynamic properties to function. The Groovy dependancies cannot be removed from these classes but a lot of the code can be reused.

RPCServerProxy contains the code to generate the top and tail of an XML rpc call document. This code could esilly be moved into XMLRPCMessageProcessor (where it probably should have been in the first place )

XMLRPCMessageProcessor does it’'s buisness in the invokeMethod method. It creates a PacketCollector for the respons to the call (a return or a fault). It sends the call and processes the result.

As you can see it’'s not very complex code.

The server is implemented by JabberRPCServer whic is a subclass of RPCServer which is a Groovy object. Most of the code in these two classes involves using Groovy voodoo and you can sefely ignore it.

Focus on the subclass of MinMLJabberPacketServer which is created in line 88.

MinMLJabberPacketServer is a subclass of MinMLThreadPool which is an abstract class which manages a pool of threads for the service.

MinMLJabberPacketServer is pretty trivial (the version iseed by XML-RPC MinMLSocketServer is slightly more complex). MinMLJabberPacketServer doen’‘t know what sort of Jabber packets it’'s dealing with.

The real work is donre in the process() method of the subclass of JabberPacketWorker which is created on line 99 of MinMLJabberPacketServer . This recieves a request, routes the call to the correct Groovy closure and returns the result. The code supports closures to be called before and after the call and also supports an optional closure to be called if there is no closure which is registered against the method name.

Note: a Closure id a Groovy object. Basically it’'s a class woth a single callable method.

The code in MinMLJabberPacketServer and its superclass are not Groovy dependant everything else is.

Much of the code in JabberRPCServer could be reused in a non Groovy implementation.

Hope that you follow all that

I have now refactored the code to remove the Groovy dependancies from the code modules.

Would you like to see the changes?

Cheers

John