qooxdoo Beginner's Guide
上QQ阅读APP看书,第一时间看更新

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

Request

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!"]
}

Response

RPC response contains the ID to map the request call and the result of the request.

{
"id":"1",
"result":"Client said: 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 the Rpc 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 occurred
  • qx.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.