Friday, November 02, 2007

Coping with Flex Asynchronous Remote Calls - Part III

The fact that flex web service calls are asynchronous have implications on how you design your code. This part III of my series of posts on the subject, which shows how to handle concurrent, asynchronous calls to the same web service method.

In part II of this post series, I showed how to use callbacks to handle the asynchronity of the calls. But the solution presented there, does not handle concurrent calls to the same web service method. To handle concurrent calls, you need to use the call object itself, and assign tokens on it. In the solution I show here, I set the callback as the token.
package com.blogspot.techpolesen {

import mx.rpc.soap.WebService;
import mx.rpc.events.ResultEvent;

public class RemoteService {
private var remoteService : WebService = new WebService();

function RemoteService() : void {
remoteService = new WebService();
remoteService.wsdl = "/services/RemoteService?wsdl";
remoteService.loadWSDL();
remoteService.addEventListener(ResultEvent.RESULT, handleNextInSequenceResult);
}

public function callNextInSequence(parameterPassedToServer : String, callback : Function) : void {
var call : Object = remoteService.nextInSequence.send(parameterPassedToServer);
call.callbackFunction = callback;
}

private function handleNextInSequenceResult(event : ResultEvent) : void {
var token : Object = event.token;
token.callbackFunction(event.result);
}
}
}
Heres is a bit of explanation on the above code:
  • The as3 class above is a wrapper around the remote web service calls.
  • The constructor instantiates the web service client object, parses the wsdl and adds a listener for the result event
  • The callNextInSequence method is the one, that can call my remote web service method (called nextInSequence, which accepts a string and appends a sequence number after it and returns it)
  • IMPORTANT: Note how the callNextInSequence method, uses the send() method of the operation instance to call the web service. The return value of send() is a call object, on which you can put tokens.
  • Tokens, which are put on a call, will appear on the result event, when the result of that particular remote call returns with a result.
  • In this implementation, I use the token to remember who I need to callback on, with the result of the call.
  • Left is only to explain the handleNextInSequenceResult listener, which is quite simple. It simply obtains the token from the event (which is the function callback) and calls it with the result of the remote call.
Here is Main.mxml content, which uses this class:
<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml">
<mx:Script>
<![CDATA[
import com.blogspot.techpolesen.RemoteService;

private var remoteServiceProxy : RemoteService = new RemoteService();

private function callItConcurrently() : void {
results.text = ""; // clear result area

// do some concurrent, asynchronous calls
remoteServiceProxy.callNextInSequence("value 1", handleResult);
remoteServiceProxy.callNextInSequence("value 2", handleResult);
remoteServiceProxy.callNextInSequence("value 3", handleResult);
}

private function handleResult(result : String) : void {
results.text += "Result: result = " + result + "\n";
}
]]>
</mx:Script>

<mx:Button label="Do concurrent, asynchronous remote calls" click="callItConcurrently()"/>
<mx:TextArea id="results" width="80%" height="50%"/>
</mx:Application>

Downloading the source
I have zipped up the sources for you. It can be downloaded from here. Ready to be build with maven.

This is a multi-module maven build. There are two directories:
  • client : Contains the flex source and a pom to build it
  • server : Contains the web service and a pom to build it
The war artifact output from the server module have in it the flash output from the client module and a index.html which loads it.

To start the server after a build, you simply jump into the server directory and do a "mvn jetty:run-exploded".

Other Small Flex Tutorials
This was lesson 9 in my series of posts on what I learn about developing filthy rich flash apps using flex2. If you want to read more, the previous lessons can be found here:

6 comments:

Patrick said...

Thanks Per, very helpful for a flex noob like me.

Patrick

Al Holden said...

Using your example, how would one set the concurrency policy on your remoteService web service object?
The line remoteService.concurrency = 'last'; does not work for me.

Tech Per said...

@al holden:

Sorry, but I do not know :-( I would like to know, if you find out, though :-)

The "concurrency" property seems to only be accessible through the mxml element (mx.rpc.soap.mxml.WebService as opposed to mx.rpc.soap.WebService), and I cannot find it elsewhere in the flex apidocs.

And the flex2 sources are missing just about everything under mx.rpc :-(

Add News to Your Website said...

Great example. Thanks.

Free News Website

Anonymous said...

how would you modify this to also handle Faults in the same manner and not just Results?

Tech Per said...

To handle faults too, I would:

1. Add generic fault handler

In the RemoteService constructor, add a line like this:

remoteService.addEventListener(FaultEvent.FAULT, handleNextInSequenceFault);

2. Add fault handler method

Also, in RemoteService, add this method:

private function handleNextInSequenceFault(event : FaultEvent) : void {
var token : Object = event.token;
token.faultCallbackFunction(event.fault);
}

3. Extend the callNextInSequence:

And, also in RemoteService, change the paramters of the method to include a fault-callback:

public function callNextInSequence(parameterPassedToServer : String, callback : Function, faultCallback : Function) : void {
var call : Object = remoteService.nextInSequence.send(parameterPassedToServer);
call.callbackFunction = callback;
call.faultCallbackFunction = faultCallback;
}

4. The call

And then of course make the call to remoteService.callNextInSequence with an additional callback function as last parameter.


NOTE: I have not tried this myself! My faulthandler is very generic, tracing the error and leading to login-form. The errors that can occur at my site are not recoverable by the application, so they can be handled the same, pretty much all of them.