Communicating with the server
qooxdoo uses the Remote Procedure Call (RPC) mechanism to call the APIs that are exposed on the server. qooxdoo RPC is based on JSON-RPC as the serialization and method call protocol. qooxdoo provides all the necessary classes in the qx.io.remote
package. So, it is pretty simple to communicate with the server.
Setting up an RPC connection
To make remote calls, you need to create an instance of the Rpc
class:
var rpc = new qx.io.remote.Rpc( "http://localhost:8080/qooxdoo/.qxrpc", "qooxdoo.test" );
The first parameter is the URL of the server and the second is the name of the service you want to call. Class name is the fully qualified name and is case sensitive.
Making a call
When you have the Rpc
instance, you can make synchronous and asynchronous calls based on your need:
// synchronous call try { var result = rpc.callSync("echo", "Test"); alert("Result of sync call: " + result); } catch (exc) { alert("Exception during sync call: " + exc); }
Synchronous calls typically block the browser UI until the response comes back from the server. A user cannot perform any action until the response comes back from the server. So, try to avoid them as much as possible or use them sparingly. In synchronous calls, the method name is the first parameter followed by the parameters of the server method as mentioned in the preceding code.
The following code demonstrates asyncronous call:
// asynchronous call var handler = function(result, exc) { if (exc == null) { alert("Result of async call: " + result); } else { alert("Exception during async call: " + exc); } }; rpc.callAsync(handler, "echo", "Test");
An asynchronous call does not block the browser UI until the response comes back from the server. Instead, it takes an additional first parameter that specifies a handler function that is invoked when the result of the method call is available or when an exception occurs. You can also use qooxdoo event listeners for asynchronous calls. To use qooxdoo event listeners, just use callAsyncListeners
instead of callAsync
.
A sample which uses an asynchronous call via event listeners is as follows:
var rpc = new qx.io.remote.Rpc(); var methodName = "testMethod"; rpc.addListener("completed", function(e) { var result = e.getData(); // do something.......... }, this ); rpc.addListener("failed", function(e) { // do something on failure this.warn("Method call failed "+e); }, this ); rpc.addListener("timeout", function(e) { // do something on timeout. this.warn("Method call timed out "+e); }, this); var result = rpc.callAsyncListeners(false, methodName);
Finally, to summarize, one can communicate with the server by issuing an RPC call in three different ways:
- Synchronous (
qx.io.remote.Rpc.callSync
): It is dangerous as it blocks the whole browser and it is not recommended to use it - Asynchronous (
qx.io.remote.Rpc.callAsync
): It returns the results via a call back function - Asynchronous (
qx.io.remote.Rpc.callAsyncListeners
): It returns results via event listeners
RPC request contains a service which maps to the remote service. It also contains the method to be invoked in that remote service, ID of the request, and the parameters to pass as arguments to the remote service method.
{ "service":"qooxdoo.test", "method":"echo", "id":1, "params":["Hello to Qooxdoo World!"] }
Aborting a call
If the user of the system changes his or her mind, the user can abort an asynchronous call while it's still being performed. See the following code snippet for aborting the asynchronous call:
// Rpc instantiation and handler function left out for brevity var callref = rpc.callAsync(handler, "echo", "Test"); // ... rpc.abort(callref); // the handler will be called with an abort exception
Tip
Downloading the example code
You can download the example code files for all Packt books you have purchased from your account at http://www.PacktPub.com. If you purchased this book elsewhere, you can visit http://www.PacktPub.com/support and register to have the files e-mailed directly to you.
Error handling
When you make a synchronous call, you can catch an exception to handle the errors. The exception object contains rpcdetails
that describes the error in detail. The same details are also available in the second parameter in an asynchronous handler function, as well as in the events fired by callAsyncListeners
. The following code snippet demonstrates error handling:
//creation of the Rpc instance left out for brevity //error handling for sync calls try { var result = rpc.callSync("echo", "Test"); } catch (exc) { showDetails(exc.rpcdetails); } // error handling for async calls var handler = function(result, exc) { if (exc != null) { showDetails(exc); } }; rpc.callAsync(handler, "echo", "Test"); //method to display error details var showDetails = function(details) { alert( "origin: " + details.origin + "; code: " + details.code + "; message: " + details.message ); };
The exception origin can be one of the following four:
qx.io.remote.Rpc.origin.server:
This occurs on the server (for example, when a non-existing method is called)qx.io.remote.Rpc.origin.application:
This occurs inside the server application (for example, during a method call in a non-qooxdoo code)qx.io.remote.Rpc.origin.transport:
This occurs in the communication layer (for example, when theRpc
instance was constructed with a URL where no backend is deployed, resulting in an HTTP 404 error)qx.io.remote.Rpc.origin.local:
This occurs locally (for example, when the call timed out or when it was aborted)
The exception code depends on the origin. For the server and application origins, the possible code is defined by the backend implementation. For transport errors, it's the HTTP status code. For local errors, the following code is defined:
qx.io.remote.Rpc.localError.timeout:
A time-out occurredqx.io.remote.Rpc.localError.abort:
The call was aborted
Cross-domain calls
Using the qooxdoo RPC implementation, you can also make calls across domain boundaries. On the client side, all you have to do is specify the correct destination URL in the Rpc
constructor and set the setcrossDomain
property to true
. See the following code snippet:
var rpc = new qx.io.remote.Rpc("http://targetdomain.com/appname/.qxrpc"); rpc.setCrossDomain(true);
On the server side, you need to configure the backend to accept cross-domain calls (Refer the documentation of the various backend implementations).
The origin is determined through a combination of protocol, host, and port. The localhost
and 127.0.0.1
are treated as two different hosts. Therefore, if you access the application using http//localhost:8080/teamtwitter
, then to access the server without enabling the cross-domain, you will have to use the following URL for the RPC calls:
http://localhost:8080/teamtwitter/.qxrpc
Similarly, if you access the application using http//127.0.0.1:8080/teamtwitter
, then to access the server without enabling the cross-domain, you will have to use the following URL for the RPC calls:
http://127.0.0.1:8080/teamtwitter/.qxrpc
It is advisable to get the protocol, host, and port from the URL typed in the browser and construct the URL for the RPC calls that are sent to the same server.
If you really want to make cross-domain calls, you will have to set the cross-domain flag in the RPC call from the client and make sure the server running on the different host accepts the cross-domain calls.
Parameter and result conversion
All method parameters and result values are automatically converted between JavaScript and the server implementation language. During the data transfer between the client and server, the data is transferred in JSON format. So, the data gets converted from JavaScript to JSON and then to Java or any other server implementation language and vice versa. Using the Java RPC Server, you can even have overloaded methods. The correct one will be picked on basis of the provided parameters. The following table lists the JavaScript types and the corresponding JSON types and Java types at the server.
All primitive data types are self explanatory. JavaScript has the Date
object, which is translated to the java.util.Date
object. JavaScript supports the Array
object, which is translated to the java.util.Array
object. Other than Number, Boolean, String, Date
, and Array
type are objects in JavaScript. If a JavaScript object is passed for the java.util.Map
parameter in the server side, all the member variables of the object are converted to the key-value pair of the map. Similarly, when a java.util.Map
is returned from the server side, all key-value pairs are translated to member variables of the JavaScript object.
JavaBeans are converted in a similar way. The properties of JavaBeans become JavaScript properties and vice versa. If a JavaScript object contains properties for which no corresponding setters exist in the JavaBeans, they are ignored. For performance reasons, recursive conversion of JavaBean
and Map
objects is performed without checking for cycles! If there's a reference cycle somewhere, you end up with a StackOverflowException
.
The same is true when you try to send a JavaScript object to the server. If it (indirectly) references itself, you get a recursion error in the browser. Besides the fully-automatic conversions, there's also a class hinting mechanism. You can use it in case you need to send a specific sub-class to the server. However, it can't be used to instantiate classes without a default constructor yet. Future qooxdoo versions may provide more extensive class hinting support.
What just happened?
We got an understanding of how to communicate with the server from the qooxdoo client applications. We have learnt how to make synchronous and asynchronous calls, handle the error, abort the asynchronous call, and so on. We have also understood the data type mapping between the JavaScript, JSON, and the server implementation language.