XOAP_Request | XMLRPC-ObjTree_converter

Introduction

AJAX and web applications

Being familiar with the AJAX concept, I realise that a huge amount of AJAX enabled web applications get their information from multiple pages. You might have developed an AJAX enabled web application yourself.
I've been looking for a couple of ways to create AJAX enabled web applications. Most applications I encountered, create requests to different pages, which return some form of XML-ized data. The result is then parsed and written to the application using DOM scripting. So, they depend on multiple 'server' pages that return the data to you or perform actions based upon the input. A good example of this usage is found in an impressive IDE called Joyistar AJAX Webshop
Joyistar is a big package that enables you to make use of a default scripting library: JCL or Joyistar Component Library.
The actual development parts of the joyistar package is not very big, though the included Apache, PHP, Tomcat and JRE installations make the package a big one (about 100MB).
While this environment can be fun and very usefull indeed, it was'nt what I was looking for. For each and every (client) page you create that gets it's data from an AJAX request, another page (the 'server' page if u will) is to be created. Furthermore, I encountered a lot of examples that had 2 server pages for each client page: 1 to fetch data from, and 1 to send data to (and thus to update data). I refer to this as seperate pages for GET and POST.

AJAX, prototype.js and webservices

However, I just did not find very much examples on AJAX getting their data from a real webservice such as XMLRPC or SOAP.
Since THIS was what I wanted, I got on looking for another way of having AJAX calls to a webservice.
One can imagine that having a webservice serve all your requests, this makes development easier (though this is based on personal preferences and opinion). Havind a webservice means that pretty much ALL actions and logic could be done in a single (or a couple) back-end files. The webservice thus serves as a central location for manipulating and fetching your server data.
Having said this, I started looking for ways to do so. Being familiar with the prototype.js library, I wanted to be able to use it. And there my problems began. Prototype is a very good library that's able to perform an ajax request to virtually ANY location. However, creating a call to an XMLRPC enabled webservice, was still NOT very straightforward. I still had to go through the whole process of generating a post body to send to the server. Anyone who's familiar with the XMLRPC protocol knows this is probably the easiest 'webservice' protocol around (well at least is easier then SOAP). Still I had to generate the whole XML POST body myself. This was not the hardest part however. It got more difficult when the result was returned. An XMLRPC response always has a couple of default entities wrapped around the actual result (as is the case with the request). Having to parse the whole XMLRPC response still was a big pain because of this...

Prototype.js and XML/ObjTree.js

As I was still looking for a more generic way to convert the XML to a workable piece of data (whether it be an array or an object), I found a library called XML/ObjTree. XML/ObjTree is a generic formatter that either writes XML from an object, or generates an object from XML, reflecting the way the XML was structured.
This library enabled me to perform the AJAX Request based on the prototype library and convert the result to somewhat more 'workable' data. While this WAS what I needed, it STILL wasn't generic enough for me.

I still had to go through the process of getting all variables, building the body to send to the server, set up all callback functions, converting the result from ObjTree and then manipulate the document with DOM scripting.
Ok, I know, this is default when enabling your site for AJAX, but to me it still was too much work.
Furthermore, playing around with ObjTree, I found that the default object as returned by ObjTree had the following format (remember that this is a result from an XMLRPC call):
methodResponse.params.param.value.#DATA#, where #DATA# could virtually be anything.

When dealing with structures, getting a named property and corresponding value requires two calls:
fetching the name: methodResponse.params.param.value.struct.member[0].name
fetching the value: methodResponse.params.param.value.struct.member[0].value
Now imagine a response having multiple objects, associative arrays and so on. This quickly makes a mess of your code.
Well... mess... let's just say it involves writing more code per function then you want or need to...

Making things easy: XOAP_Request and XMLRPC_ObjTree_converter

I think you get the picture that this way of getting data was NOT generic enough for me. Besides, I lost feeling with the structure of data!
Thus, I decided to create a little library for myself that makes use of both prototype AND XML/ObjTree that would allow me to do the following:

If you're wondering, quickly here means 'with as little lines of code as possible'.
The last point may sound a little strange, but what I mean is that the ObjTree's result is converted in such a way the the whole XMLRPC structure is converted into the objects and arrays as it was sent. Remember the format from before:
fetching the (property)name: methodResponse.params.param.value.struct.member[0].name (='first_name')
fetching the (property)value: methodResponse.params.param.value.struct.member[0].value (='Bill')
Suppose the property name be 'first_name' with a value of 'Bill'. After conversion, this would then be fetched using:
data.first_name, which would then evaulate to 'Bill'

So after a little while, I came up with two libraries, which do EXACTLY what I want AND are extremely easy to use.
The two libraries are:

For me, the goal has been achieved: Create AJAX requests to an XMLRPC based webservice AND doing it with the least possible lines of code!
Now how does this all work? Just read on and you will see that usage of these libraries is as easy as abc...
Then decide for yourself whether this works for you!

Source

The source is thoroughly documented, so you should be able to understand it rather easily...
Here are the libraries:
XOAP_Request.js
XMLRPC-ObjTree_converter.js

How to use XOAP_Request

Create XOAP_Request

Using XOAP_Request is very simple and straightforward. One goal for this utility was to get it as simple as possible. To set up the XOAP_Request, you need to know the following information:

Once you know this, you create the XOAP_Request :
var xoap = new XOAP_Request(url,method,parameters);
You do not need to set this with the constructor, but it is recommended you do so (at least for the first two).
This allows for changing xoap.params (or xoap.url or xoap.requestMethod) to call the XMLRPC server method with:
var xoap = new XOAP_Request('http://www.you.domain/XMLRPC/server.php','myserver.getStatus');
xoap.params = ['April', 'Neil']; //easy way of setting arrays
Note that while the params are purely randomly chosen, this does show the way to set parameters.

Assign Callback functions

Though you could use the xoap-instance as it is now, there's still something missing. You might wonder where or how returned data will now be dealt with...
There's a simple answer for this: Nowhere/Not at all.
The xoap-instance we've just created only has the data neccesary to perform the internal Ajax Request (which is based on prototype.js),
but has NO clue about what to do with the data. This is why we need to create the callback functions for it and then assign them to the XOAP_Request.

The callback functions you can assign are, since the internal Ajax Request is that of prototype.js, the same as prototype's Ajax.Request.
Assignable callbacks are: onException, onFailure, onXYZ (e.g. on404, on403 etc), onInteractive, onLoading, onLoaded, onUninitialized, onSuccess and onComplete
For details on these callbacks, you should refer to prototype's documentation.

//add callbacks to xoap
xoap.addCallback( 'onSuccess', xoap_success );
xoap.addCallback( 'onException', function(request,e){alert('Exception: '+e);} );
You probably wonder now: ok, but what the heck is xoap_success? Well, this is a callback function that we still need to define.
Referencing prototype's documentation, we find that the function that;s called back is always accompanied with with an argument.
This argument is the originalRequest, and can be used to fetch result data from.
//xoap_success callback function
function xoap_success(originalRequest) {
	//do stuff here...
}
Here, I shortly conclude the part on the XOAP_Request, for there's really nothing more to it.
I will continue with the response converter: XMLRPC_ObjTree_converter.
Don't worry, we'll get to the body of the xoap_success function later, after discussing the converter.

The XMLRPC_ObjTree_converter

Create the XMLRPC_ObjTree_converter

Creating the XMLRPC_ObjTree_converter is as simple as abc:

//create the converter
var xoc = new XMLRPC_ObjTree();
When using the converter, be aware that for each call an Exception could be generated.
A try...catch structure is needed for everything to work fine in case something goes wrong.
Furthermore, IF an exception is generated, the converter will internally catch it, set an internal error and then re-throw the exception message.
This way, it's very easy to get the error message (it is actually a rather useless function, and it's mainly there for my convenience :))

Using the XMLRPC_ObjTree_converter

After creating the converter, it is ready for use.
There are not many things you can do with it, for it's main goal is to convert the XML response from the Ajax Request,
which is already converted to an object by ObjTree, BACK to it's 'original' form. I specifically state 'original',
because an associative array in the original response will be converted to a structure in XMLRPC, therefore becoming an object
(though in javascript still available through an array, i guess).
Since XML/ObjTree provides a way of forcing an element to an array, we can use this to parse our member entities in the XMLRPC call (members are children of a struct entity);

//set forcing arrays for xoc
xoc.forceArray = ["member"]; //Not really needed, for if not set, DEFAULTS to 'member'
//now convert response
var rpcdata = xoc.convertRequest(originalRequest);
Well, that's pretty much it. Once you know what data is sent, it's incredibly easy to get back.
Let's put it all together in one script...

Putting things together

The following script is the final script, as was used for parts of this example document:
var URL = "http://my.domain.com";
var method = "rpcserver.hello";
try {
	//create main XOAP_Request
	var xoap = new XOAP_Request(URL,method);
	//add callbacks to the request
	xoap.addCallback( 'onSuccess', xoap_success );
	xoap.addCallback( 'onException', function(request,e){alert('Exception: '+e);} );
}
catch (e) { alert(xoap.error); }

//define xoap_success callback function
function xoap_success(originalRequest) {
	try {
		//create converter
		var xoc = new XMLRPC_ObjTree();
		//set forcing arrays for xoc
		xoc.forceArray = ["member"]; //Not really needed, for if not set, DEFAULTS to 'member'
		//now convert response
		var rpcdata = xoc.convertRequest(originalRequest);
		//handle data, in this case, only a single combined string:
		$('debug').innerHTML = rpcdata;
	}
	catch (e) {
		$('debug2').innerHTML = xoc.error;
	}
}

//now test call the request...
xoap.params = ["Steven Hawkins"];
xoap.do_request();
In our case, the script will output the following text:
Hello, Steven Hawkins, how are you today?
 
 

Advanced

So far, we have only seen a very simple example where just a sinngle value is returned...
But, what if we send objects, arrays (associative or not) and so on?
Very easy! Just keep in mind how your objects are returned. Suppose the following server-code (I'll only stick to the result code for now on a PHP basis... it's up to you to do another implement)

$result = array();
$result['persons'] = array(
        array (
'person'=>new person('Jan','Keijzers'), 'status'=>'online' ),
        array (
'person'=>new person('Jan','Smit'), 'status'=>'offline' ),
        array (
'person'=>new person('I ','Rule'), 'status'=>'online' ),
    );
$result['messages'] = array(
        array (
'person'=>new person('Jan','Keijzers'), 'time'=>'[ xx:xx:xx ]', 'msg'=>'Hello' ),
        array (
'person'=>new person('Jan','Smit'), 'time'=>'[ xx:xx:xx ]', 'msg'=>'Hello back' ),
        array (
'person'=>new person('Jan','Keijzers'), 'time'=>'[ xx:xx:xx ]', 'msg'=>'How r U today' ),
        array (
'person'=>new person('I ','Rule'), 'time'=>'[ xx:xx:xx ]', 'msg'=>'So, what do you think of this?' ),
    );
return
$result;

class
person{
    public
$fname='Jan';
    public
$lname = "Smit";
    function
__construct($f, $l) {
        
$this->fname=$f;
        
$this->lname=$l;
    }
}

Here, I'll stick to just the success function, as the rest is as straightforward as above...
The javascript handler will be:
var xoap = new XOAP_Request(RPC_SERVER,'advance',null);
xoap.addCallback( 'onSuccess', xoap_success2 );

//define xoap_success callback function
function xoap_success2(originalRequest) {
	try {
		//create converter
		var xoc = new XMLRPC_ObjTree();
		//set forcing arrays for xoc
		xoc.forceArray = ["member"]; //Not really needed, for if not set, DEFAULTS to 'member'
		//now convert response
		var rpcdata = xoc.convertRequest(originalRequest);
		//handle data, in this case, a 'complicated' structure reflecting your original response...:
		var str = '';
		for (var i=0; i<rpcdata.persons.length; i++) {
			var p = rpcdata.persons[i];
			str += ('Name: '+p.person.fname+' '+p.person.lname+' ('+p.status+')');
		}
		str += '<br/><br/>';
		for (var i=0; i<rpcdata.messages.length; i++) {
			var m = rpcdata.messages[i];
			str += (m.person.fname+' '+m.person.lname+' '+m.time+' :: '+m.msg);
		}
		$('debug').innerHTML = str;
	}
	catch (e) {
		$('debug2').innerHTML = xoc.error;
	}
}
 
 

If you wish to see the XML response that was returned by the call to the XMLRPC server, click here.
As you can see, the XML is rather extensive. You could, off course, parse the whole result yourself, but why should you? Using XOAP handles it all for you!

Periodical Requests

You might wonder by now how to implement periodical requests. Off course, you might already have thought of using javascipts setTimeout, but he... there's a better way.
Since we use the prototype library, there's already a good way of updating your data. This is a polling method and yes, I am aware of other packages that do not poll but update you data realtime. On the internet, there's loads of web2.0 buzzwords for this rather ancient technology: Comet, Forever Frame, streaming AJAX. Some say it's state of the art technology, but really, it's OLD... In short, HTTP is stateless, though by having a request SLEEP on the server in a loop which is checking for updated data or a condition, you can have a request 'thread' on the server. As soon as the loop is broken, the request is sent to the browser and the client can update the page through DOM scripting.

Anyway, lets' stick to the subject. Prototype provides the PeriodicalExecuter object to enable a polling mechanism.
Suppose we want to poll the server to update our GUI every second, simply write the following code:

//wrap request based on parameter...
function updateGUI() {
	var rand = Math.round(Math.random()*100);
	var name = "Steven Hawkins"+rand;
	xoap.params = [name];
	xoap.do_request();
}

//create PeriodicalExecuter
new PeriodicalExecuter(updateGUI, 1);

Reference

For further background reading, you should navigate to the following pages
The prototype.js homepage
XML/ObjTree homepage
webtoolkit's Base64, used in XMLRPC_ObjTree_converter


© 2007, Ing. R. J. van Dongen