Remote Procedure Call (RPC)

Dojo provides low level I/O out of the box, but as applications grow in complexity it's natural to want something less one-off-ish. Dojo's Remote Procedure Calls (RPC) module aims to make this process less error prone, easier, and require less code.

Remote Procedure Calls allows you to invoke a method on a remote host. Dojo provides a basic RPC client class that has been extended to provide access to JSON-RPC services and Yahoo services. It was designed so that it is easy to implement custom RPC services.

For example, you have a little application that you want to use to make some server calls. For simplicity, the methods you want the server to do are add(x,y) and subtract(x,y). Without using anything special, like an RPC client, you could do something like this:

/* GeSHi (C) 2004 - 2007 Nigel McNie (http://qbnz.com/highlighter) */ .geshifilter {font-family: monospace;} .geshifilter .imp {font-weight: bold; color: red;} .geshifilter .kw1 {color: #000066; font-weight: bold;} .geshifilter .kw2 {color: #003366; font-weight: bold;} .geshifilter .kw3 {color: #000066;} .geshifilter .co1 {color: #009900; font-style: italic;} .geshifilter .coMULTI {color: #009900; font-style: italic;} .geshifilter .es0 {color: #000099; font-weight: bold;} .geshifilter .br0 {color: #66cc66;} .geshifilter .st0 {color: #3366CC;} .geshifilter .nu0 {color: #CC0000;} .geshifilter .me1 {color: #006600;} .geshifilter .re0 {color: #0066FF;}
add = function(x,y) {
        request = {x: x, y: y};
        dojo.xhrGet({
            url: "add.php",
            load: onAddResults,
            handleAs: "text",
                content: request
        });
}
subtract = function(x,y) {
        request = {x: x, y: y};
       
        dojo.xhrGet({
            url: "subtract",
            load: onSubtractResults,
            handleAs: "text"
                content: request
        });
}

This isn't particularly difficult but it is repetitive and error prone. Dojo's RPC clients simplify this process by taking a simple definition of the remote methods and application needs and generating client side functions to call these methods. You need only write this definition, and initialize an RPC client object and then all of these remote methods are available for you to use as normal.

The definition file, called a Simple Method Description (SMD) file, is a simple JSON string that defines a URL to process the RPC requests, any methods available at that URL, and the parameters those methods take. The definition for the example above might look like this:

/* GeSHi (C) 2004 - 2007 Nigel McNie (http://qbnz.com/highlighter) */ .geshifilter {font-family: monospace;} .geshifilter .imp {font-weight: bold; color: red;} .geshifilter .kw1 {color: #000066; font-weight: bold;} .geshifilter .kw2 {color: #003366; font-weight: bold;} .geshifilter .kw3 {color: #000066;} .geshifilter .co1 {color: #009900; font-style: italic;} .geshifilter .coMULTI {color: #009900; font-style: italic;} .geshifilter .es0 {color: #000099; font-weight: bold;} .geshifilter .br0 {color: #66cc66;} .geshifilter .st0 {color: #3366CC;} .geshifilter .nu0 {color: #CC0000;} .geshifilter .me1 {color: #006600;} .geshifilter .re0 {color: #0066FF;}
{
        "serviceType": "JSON-RPC",
        "serviceURL": "rpcProcessor.php",
        "methods":[
                {
                        "name": "add",
                        "parameters":[
                                {"name": "x"},
                                {"name": "y"}   
                        ]
                },
                {
                        "name": "subtract",
                        "parameters":[
                                {"name": "x"},
                                {"name": "y"}   
                        ]
                }
        ]
}

Once the definition has been created, the code is pretty simple. The definition can be supplied either as a URL to retrieve it, a JSON string, or a JavaScript object, as shown in the following example.

/* GeSHi (C) 2004 - 2007 Nigel McNie (http://qbnz.com/highlighter) */ .geshifilter {font-family: monospace;} .geshifilter .imp {font-weight: bold; color: red;} .geshifilter .kw1 {color: #000066; font-weight: bold;} .geshifilter .kw2 {color: #003366; font-weight: bold;} .geshifilter .kw3 {color: #000066;} .geshifilter .co1 {color: #009900; font-style: italic;} .geshifilter .coMULTI {color: #009900; font-style: italic;} .geshifilter .es0 {color: #000099; font-weight: bold;} .geshifilter .br0 {color: #66cc66;} .geshifilter .st0 {color: #3366CC;} .geshifilter .nu0 {color: #CC0000;} .geshifilter .me1 {color: #006600;} .geshifilter .re0 {color: #0066FF;}
var myObject = new dojo.rpc.JsonService("http://localhost/definition.smd");
var myObject = new dojo.rpc.JsonService({smdStr: definitionJSON});
var myObject = new dojo.rpc.JsonService(definitionObj);

Thats it! Now all that's left is to call the method as shown in the following example.

/* GeSHi (C) 2004 - 2007 Nigel McNie (http://qbnz.com/highlighter) */ .javascript .imp {font-weight: bold; color: red;} .javascript .kw1 {color: #000066; font-weight: bold;} .javascript .kw2 {color: #003366; font-weight: bold;} .javascript .kw3 {color: #000066;} .javascript .co1 {color: #009900; font-style: italic;} .javascript .coMULTI {color: #009900; font-style: italic;} .javascript .es0 {color: #000099; font-weight: bold;} .javascript .br0 {color: #66cc66;} .javascript .st0 {color: #3366CC;} .javascript .nu0 {color: #CC0000;} .javascript .me1 {color: #006600;} .javascript .re0 {color: #0066FF;} myObject.add(3,5);

Of course this just calls the method and doesn't address what is returned from the call or how to get at the results. Just like the rest of the I/O system in dojo 0.9+, the RPC system returns a deferred to which you can attach callbacks.

/* GeSHi (C) 2004 - 2007 Nigel McNie (http://qbnz.com/highlighter) */ .geshifilter {font-family: monospace;} .geshifilter .imp {font-weight: bold; color: red;} .geshifilter .kw1 {color: #000066; font-weight: bold;} .geshifilter .kw2 {color: #003366; font-weight: bold;} .geshifilter .kw3 {color: #000066;} .geshifilter .co1 {color: #009900; font-style: italic;} .geshifilter .coMULTI {color: #009900; font-style: italic;} .geshifilter .es0 {color: #000099; font-weight: bold;} .geshifilter .br0 {color: #66cc66;} .geshifilter .st0 {color: #3366CC;} .geshifilter .nu0 {color: #CC0000;} .geshifilter .me1 {color: #006600;} .geshifilter .re0 {color: #0066FF;}
var myDeferred = myObject.add(3,5);
myDeferred.addCallback(myCallbackMethod);

Or, it could be more succinctly done this way:

/* GeSHi (C) 2004 - 2007 Nigel McNie (http://qbnz.com/highlighter) */ .geshifilter {font-family: monospace;} .geshifilter .imp {font-weight: bold; color: red;} .geshifilter .kw1 {color: #000066; font-weight: bold;} .geshifilter .kw2 {color: #003366; font-weight: bold;} .geshifilter .kw3 {color: #000066;} .geshifilter .co1 {color: #009900; font-style: italic;} .geshifilter .coMULTI {color: #009900; font-style: italic;} .geshifilter .es0 {color: #000099; font-weight: bold;} .geshifilter .br0 {color: #66cc66;} .geshifilter .st0 {color: #3366CC;} .geshifilter .nu0 {color: #CC0000;} .geshifilter .me1 {color: #006600;} .geshifilter .re0 {color: #0066FF;}
var myDeferred = myObject.add(3,5).addCallback(myCallbackMethod);

You just add myCallbackMethod as a callback for the Deferred returned from myObject.add(). In this case myCallbackMethod is called with a parameter with a value of 8. Likewise, you can attach an errback method to the deferred object to process any errors returned from the server. You can add as many callbacks and errbacks to your deferred object as you want and they will be called in the order that they were connected to the deferred object.

In addition to JsonService, Dojo offers a JsonpService client, which is used for services such as Yahoo which provide JSON-P style service. Most services of this style can be represented in an SMD and passed to the JsonpService and used in the same fashion as above in a crossplatform manner. This makes mashups a cinch. Take a look in the dojox project at the Yahoo.smd example provide. Pass that url to JsonPService and all of Yahoo's services are at your finger tips and others are easy to add.

/* GeSHi (C) 2004 - 2007 Nigel McNie (http://qbnz.com/highlighter) */ .geshifilter {font-family: monospace;} .geshifilter .imp {font-weight: bold; color: red;} .geshifilter .kw1 {color: #000066; font-weight: bold;} .geshifilter .kw2 {color: #003366; font-weight: bold;} .geshifilter .kw3 {color: #000066;} .geshifilter .co1 {color: #009900; font-style: italic;} .geshifilter .coMULTI {color: #009900; font-style: italic;} .geshifilter .es0 {color: #000099; font-weight: bold;} .geshifilter .br0 {color: #66cc66;} .geshifilter .st0 {color: #3366CC;} .geshifilter .nu0 {color: #CC0000;} .geshifilter .me1 {color: #006600;} .geshifilter .re0 {color: #0066FF;}
var svc = new dojo.rpc.JsonpService(dojo.moduleUrl("dojox.rpc", "yahoo.smd"), {appid: "dojoApp"});

The resulting Yahoo Service object allows you to perform web searches, do term extraction, and search all sorts of local an contextual data. See the SMD file for details on the available methods, parameters, and documentation pointers.

While Dojo is currently limited to these two RPC clients, the design of the base classes allows you to easily customize and extend dojo.rpc.RpcService to create services that meet your specific needs. Have a look at the source for RpcService.js and JsonService.js to see just how simple it is. If anyone is interested in contributing their own SMD files for their pet service or their company's services, please let us know!