Home 


Simple Object and Data Access (SODA) Remote
Procedure Call (RPC)


What is SODA-RPC?

SODA-RPC (or SODA for short) stands for Simple Object and Data Access. It's a protocol that provides an easy way for web applications to communicate with a web server. The transport uses XML to send and receive data to and from the server.  No XML parser is required since SODA-RPC uses a very simple layout to convert data variables into an XML document and back.

Click here to see a live Calculator Application using SODA-RPC. An ASP compatible web server is  required.
 

Why should I use SODA-RPC?

XML is a widely used data format for exchanging data on the web, but it requires the overhead of an XML parser and data often times will have to be converted to the desired data type for use with the application.  

A SOAP-RPC Web Service provides another way for programmers to send and receive information to and from a web server, but it too requires the web server and browser to have special software installed to in order to communicate.

SODA-RPC might not be as powerful as SOAP, but it’s powerful enough to get the job done right

Key Features:

The SODA protocol makes it very easy for a programmer to send and receive JavaScript objects to and from the server without having to worry about how the object will be handled on the server. With each transmission the programmer will be able interact directly with objects and variables in the language they’re using on the server-side.
 

How easy is it to get started with SODA-RPC?

It's very simple! All you have to do is download DynAPI 2.9 and include the following inside the <head> tag of your html document:

<script language="JavaScript" src="../src/dynapi.js"></script>
<script language="Javascript">
    dynapi.library.setPath('../src/');
    dynapi.library.include('dynapi.api');
    dynapi.library.include('dynapi.library');
    dynapi.library.include('dynapi.util.IOElement');
    dynapi.library.include('dynapi.util.IOElementSoda');
</script>

The first line must point to the location of your dynapi.js file:

<script language="JavaScript" src="../src/dynapi.js"></script>

Secondly you want to make sure you set the path to the dynapi library  files (it's usually the same path as that of the dynapi.js file):

dynapi.library.setPath('../src/');


Now that we've included the necessary library files into our html document, it's now time to explore create a connection to the server.

The first thing to do is to create a new IOElement object.

var io = new IOElement(1); // create only one asynchronous thread

Next we'll connect to the service using the createWebService method. The createWebService has the following arguments:

createWebService(name,url,fn,uid,pwd,method)
    name    - client-side name used to identify the service
    url     - URL path to the web sercvice
    fn      - Callback function
    useSync - Used to activate Synchronous transfers.
    uid     - User ID
    pwd     - User Password
    method  - GET or POST method during calls. POST is used by default

The name argument makes it possible for you to connect to more than one service using one IOElement object

example:

// io.createWebService(bksrv,"bkman.asp",initWS,false);
// io.createWebService(hrms,"hrms.asp",initWS,false);

// create a synchronous connection
io.createWebService(calc,"calc.asp",initWS,true); 
function initWS(calc,s,errorText){
   if(s!=true) {
      alert('An Error occured while creating web service: '+errorText);
      return;
   }

   // access the calc service
   calc.call("multiply", [5,6],fn);
}

Once we've established a synchronous connection with the service we can now import methods located on the server by using the importWebMethods() function. This function will only work with synchronous calls. You can NOT import web methods when using asynchronous calls!

var rt=io.calc.importWebMethods();
if (!rt) alert('Unable to import web method');

So far we've manage to include the necessary libraries, create a connection and import web methods exposed by the service. Our next step is to place both asynchronous and synchronous calls to the service.

To place a asynchronous call to the server we'll use the call method.

var id = io.calc.call('multiply', [4,5],fn);
// do some work here
function fn(e,s){
    var o = e.getSource();
    var r = o.getResponse(); // get Response object
    var f=document.forms["calculator"];
    if(!r.error) f.txtdisplay.value = r.value;
    else {
        var er=r.error.text+'\n\nTo retry the request click the OK button';
        if (confirm(er)) o.retry();
        else o.cancel();
    }
}

The above will call the multiply() method on the server and pass it the arguments 4 and 5. The fn argument is used as the callback function that's called when the server has issued a response. The value returned from the call() function can be used later to cancel the call or to perform a retry operation if the call had failed.

To place a synchronous call to the service we can do it using one of two methods. The first is to use the call() method with the callback function set to false. The second is to use the imported web methods:

1) Using the call function.

var response = io.calc.call('subtract',[9,19],false); // note: no callback function is used.
if (response.error) alert(response.error.text);
else {
    alert(response.value);
}

2) Using imported web methods

var response = io.calc.subtract(4,5);

Note that an object is returned after a synchronous call is made. This object is called the Response Object. It contains the returned value and an error object if there was an error during the call.

The call() function can also be used to place multiple calls to the server in either async/sync modes. 

Let's say we want to perform the following calculation via our web service:

23+(((5*7)-6)/2)  which is equal to 37.5

With conventional tools you would have to make several calls to the server or create a function on the server that can perform the above calculation. But what it you're not able to create the function on the server? Then you'd just have to stick with with what you have. SODA-RPC solves the above problem by allowing you to not only make multiple calls but to also pass arguments from one method to the next in a single call to the server!

Here's how it's done using a simple object:

var dta={
    multiply:[5,7],
    subtract:['@RESULT#0',6],
    divide:['@RESULT#1',2],
    add:[23,'@RESULT#2']
}
var response = io.calc.call(dta,null,false);
if (response.error) alert(response.error.text);
else {
    alert(response.value[3]); // displays 37.5
}

Or we could be it this way using arrays:

var mns='multiply,subtract,divide,add';
var args=[
    [5,7],
    ['@RESULT#0',6],
    ['@RESULT#1',2],
    [23,'@RESULT#2']
];
var response = io.calc.call(mns,args,false);
if (response.error) alert(response.error.text);
else {
    alert(response.value[3]);
}

Using the later makes it possible for you to use a method more than once. For example:

((23*4)+12)*3 can be represented as:

var mns='multiply,add,multiply';
var args=[
    [23,4],
    ['@RESULT#0',12],
    ['@RESULT#1',3]
];
var response = io.calc.call(mns,args,false)
if (response.error) alert(response.error.text);
else {
    alert(response.value[2]);
} 

The value return in the response object is an array containing the results of all the methods called on the server.

Note: Using Synchronous transfers will block the executing script will it waits for a response from the server. In my opinion it's best to use asynchronous transfers as it allows for more control.


SODA-RPC Data Types

The soda protocol has 8 types of data. All values have one of these types:

Undefined/Null
The Undefined or Null type has one value only, null.
Boolean
The Boolean type represents the two logical values, true and false.
String
Strings are delineated by single or double quotation marks and can contain zero or more characters. An empty string has zero characters and length.
Integer
The integer type represents positive or negative numbers without a decimal point. 
Float 
The float type represents positive or negative numbers with a decimal point. A floating-point literal must have at least one digit and either a decimal point.
Date
The Date type stores a valid date entry in the format mm/dd/yyy hh:nn:ss
Array
An Array is a series of data elements of mixed data types.
Object
The Object type is represents an associative (hash) array used for storing name and value pairs.

 

Data Types  JavaScript  VBScript  Perl
 Date  Date  Date  scalar ($)
 String  String  String  scalar ($)
 Integer  Integer  Long Integer  scalar ($)
 Float  Float  Double  scalar ($)
 Boolean  Boolean  Boolean  scalar ($)
 Array  Array []  Array  Array []
 Associative Array  Object {}  Dictionary Object  Hashes %

 The server or client libraries can be easily ported to other languages (such as php, python, java, tcl) 

Server-Side Libraries:

You first need to include the following set of files:

ioelmsrv.*
ioelmsrv.soda.*

Where * is used to represent the language and extension of the file. For example * would represent jscript.asp which would result in ioelmsrv.jscript.asp and ioelmsrv.soda.jscript.asp

Client-Side Libraries:

Note: If you'd like to create a library file for another scripting language you can use the soda-tester example page to test the functions of the library.


Creating a SODA-RPC web service

The first thing you'll be needing is the IOElement Server and SODA-RPC library files for the server-side script that you're using. There are currently three server-side scripts avaiable: 

PHP
ioelmsrv.php
ioelmsrv.soda.php
 
Perl
ioelmsrv.pl
ioelmsrv.soda.pl
 
JScript (ASP) 
ioelmsrv.jscript.asp
ioelmsrv.soda.jscript.asp
 
VBScript (ASP)
ioelmsrv.vbscript.asp
ioelmsrv.soda.vbscript.asp

The above libraries can be easily ported to other languages.

Server-Side Methods:

wsAddDescription(name,text) - Add a description to a method or its parameters. When online help is enabled users will be able to see the description of the methods or parameters.

wsAddDescription("aboutMe","Company Information - Name, Email, etc")
wsAddDescription("add","Add two numeric values and returns the result")
wsAddDescription("add:a","Numeric value")
wsAddDescription("add:b","Numeric value")

wsAddMethod(name,params,rtype) - Used for adding a methods to the service. Methods added will be made public. 

wsAddMethod('search',['fname:string','age:integer:0'],'array');
wsAddMethod('clearcache',null,'boolean'); // method does not accept arguments

The params argument can be a string or and array of string parameters. Each parameter is defined as:

 Name of argument:Data Type:Default Value

wsAddErrorCode(code,text) - Used for adding error codes to the service. These codes can be triggered by using the wsRaiseError() method

wsAddErrorCode("D2","Test Error");
// some code here
wsRaiseError("D2"); // later raise error D2

wsAddVariable(name,value) - Used for adding JavaScript variables which will be returned to the client current client

wsCaptureEvent(evnt,fn) - Used for capturing Dispatch and Login events triggered by the service. 

wsCaptureEvent("login",login);
wsCaptureEvent("logout",logout);
wsCaptureEvent("dispatch",dispatch);

wsDispatch();

function login(uid, pwd, sid) {
   if (uid=="myname" && pwd=="mypassword") {
      Session.Contents(sid)="ok";
      return true; //returns true if successful
   }
};

function logout(sid) {
   Session.Contents(sid)="";
   return true; //returns true if successful
};

function dispatch() {
   var sid;
   sid=wsGetSessionId();
    if (Session.Contents(sid)!="ok");
    return false; //returns false to prevent dispatch
};
 

wsDispatch() - Dispatches methods and variables if IOResponse is set to html (default-asynchronous) otherwise only methods are dispatched

wsDispatchVariables() - Dispatches JavaScript variables to client. Should only be used with Asynchronous calls. A SODA-RPC library can be used on the server-side to dispatch variables to the client when a call is made via IOElement's get(), post() or upload() methods.

wsAddVariable('count',2);
wsAddVariable('name','Mary Jane');

wsDispatchVariables(); // returns JavaScript variables to client;

wsEnableOnlineHelp(b) - Used to enable or disable online help/debug. When set the false users will only be able to see the main splash screen. 

wsEnableOnlineHelp(true)

wsGetRequest(name) - Returns form data submitted by client

var fname = wsGetRequest("firstname"); // client submitted "firstname"

wsGetRequestMethod() - Returns the method (GET, POST or UPLOAD) used to make the call

wsGetSessionId() - Returns the current caller's session id

wsIsHelpMode() - Returns true if in helpmode or false if not in help mode.

wsRaiseError(ecode,etext) - Returns an error to the client. This method can also be used to help debug server-side code: 

function setDate(dt){
   if(!dt) wsRaiseError(null,'You've entered:'+dt);
   else {
      // code to set date
   }
}

wsSetComment(strComment) - Sets a comment or description for the service 

wsSetName(strName) -  Sets a name for the service

 

The SODA-RPC protocol makes it possible for any web server to act as a web service because it uses either GET or POST to send data to the server. There are two main variables used when submiting data, they are as follows:

IORepsonse -  XML or HTML (default)  - Sets the returned data format from the server

IOEnvelope - SODA-RPC XML document 

example:

http://localhost/myserver.asp?IOResponse=XML&IOEnvelope=<soda.env> <sid>12232-33334-23243354-2332</sid> <method>Test</method> <body> <so.da></so.da> </body> </soda.env>

 The XML/HTML formats make it possible for a SODA-RPC client to exist within a browser or within a desktop application


A SODA-RPC Application Example using Asynchronous Calls

Let’s say we have a web service app call Book Manager that was written in JavaScript and DHTML on the client-side and VBScript on the server-side.

First I’m going to have to create a way for me to send and receive data to and from the server. Next I need a way to send data in the background.

The first thing that normally comes to mind is to use a <form> with hidden fields to send data to the server, but that would then force me to reload my html page. Ok, let’s use an IFrame! Sure, but that would also require me to parse the data returned from the server!

This is how SODA-RPC makes it very easy to communicate with server-side apps:

 IOElement SODA-RPC functions:

 Constructor IOElement(hidden, useXFrames)

hidden – total number or threads or ports;
useXFrames – use eXternal Frames for post request in NS4

 createWebService(name, url, fn, useSync, method, uid, pwd);

name – local name used to identify the service
url – SODA web service url
fn - Callback function
useSync - Activate Synchronous transfers
method – method used for request; POST or GET
uid - user id - for use with server-side login event when
a connection is created
pwd - user password - for use with server-side login
event when a connection is created

 getResponse();
            Returns a response object containing the returned value from the server and and error object

For more information please see the IOElement Quick Reference  Guide

Web Service Object Methods 

call(name, params, fn); - calls a remote method on the server
            name – name of method(s) on the server
            params – parameters to pass to the method; multiple params are passed as an array
            fn – (optional) callback or returned function. When set to false an synchronous call is made.

importWebMethods();  - Import web methods available on the server. Returns true if successful.

Example: 

Let’s first create an IOElement with a single thread or port

var io = new IOElement(1); // for NS4 to support post request use IOElement(1,true);

Next we'll connect to the soda web service on the server

io.createWebService(‘book’, ‘bookmanager.asp’, initWS, false);
function initWS(ws,s,et){
   if(s!=false) {
      alert('Error while connecting');
      return;
   }
   // some code here
};

Request books from server

io.book.call(“getBooks”, null, fetchBooks);
function fetchBooks(e){
    var o=e.getSource();
   var result=o.getResult();
   if (result.error) alert(result.error.text);
    else {
        var books=result.value;
        for( var i=0;i<books.length;i++){
            // code here to display books
        }
   }
}

Search for a book by the name of 'The JavaScript Bible'

var sortby=2; //author
var query={};
query.name  = ‘The JavaScript Bible’;
query.author = ‘*’;
query.year = ‘*’;
io.book.call(‘searchBooks’,[query, sortby], fetchBooks);

The getBooks and searchBooks methods on the server-side can be written in any program.

Example of server-side code:

<!-- #include file=“soda.vbscript.asp”  -->
<%
Call wsAddMethod(“searchBooks”, Array(“name:string:*”, “sortby:int:0”),“object”)
Call wsAddMethod(“getBooks”,null,“array”)
Call wsDispatch()
Functions GetBooks()
   Dim books()
   ‘get data from database and populate books() array
   GetBooks=books         
End Function
Function SearchBooks(query, sortby)
   ‘ Note: In VBScript the dictionary object is used to
    ‘ represent an associative array
    Dim results
   // perform search
    SearchBooks = results
End Function
%>

For more information please see:


An example of a SODA-RPC document

<envelope>
    <sid>3435-453546-FG34-5464634</sid>
    <uid>Mary</uid>
    <pwd>password</pwd>
    <err>E1|System Error</err>
    <method>GetUsers</method>
    <body>
        <soda>
            <a0>
                <s1>This is a string</s1>
                <r0/>
                <s1>Testing  A</s1>
                <r0/>
                <f1>34.5</f1>
                <r0/>
                <i1>100</i1>
                <r0/>
                <f1>0.15</f1>
                <r0/>
                <a1>
                    <s2>A</s2>
                    <r1/>
                    <s2>B</s2>
                    <r1/>
                    <s2>C</s2>
                    <r1/>
                    <d2>3/20/2002 22:23:38</d2>
                </a1>
                <r0/>
                <s1>More string</s1>
                <r0/>
                <a1>
                    <b2>true</b2>
                    <r1/>
                    <b2>false</b2>
                    <r1/>
                    <i2>0</i2>
                    <r1/>
                    <u2>0</u2>
                    <r1/>
                    <s2>Boolean</s2>
                </a1>
                <r0/>
                <o1>
                    <a2>
                        <s3>name|address|city|country|phone|email|fn</s3>
                        <r2/>
                        <a3>
                            <s4>Mary Jane</s4>
                            <r3/>
                            <s4>34 More Lane</s4>
                            <r3/>
                            <s4>FL</s4>
                            <r3/>
                            <s4>USA</s4>
                            <r3/>
                            <u4>0</u4>
                            <r3/>
                            <s4>email@mail.com</s4>
                            <r3/>
                            <u4>0</u4>
                        </a3>
                    </a2>
                </o1>
            </a0>
        </soda>
    </body>
</envelope>