Thursday 21 January 2010

AsyncTokenWrapper

I have to confess that I lied ever so slightly in that last post. I said that the service code was exactly the same as it was without using the Command metadatatag.

This isn’t strictly true – I had to write a wrapper for AsyncToken to handle the xml translation. I was hoping that I would be able to use an interceptor to intercept the result event, translate the xml and then re-dispatch the event. Jens said that this wasn’t possible though so I needed a neat and tidy way to translate the results of the async call before it made it back to the model.

I did this by wrapping the async token that I get from whatever async operation I am performing. I can then pass this token to parsley so my code can do what it likes to the result before parsley routes it to it’s destination.

AsyncTokenWrapper does just that, it extends AsyncToken and wraps another AsyncToken. When it gets a result or a fault from the original token it applies an optional function to the result or fault before passing this new ResultEvent or FaultEvent onto it’s responders.

Code Below:

package com.pricklythistle.common.service
{
import mx.core.mx_internal;
import mx.messaging.messages.IMessage;
import mx.rpc.AsyncToken;
import mx.rpc.Fault;
import mx.rpc.IResponder;
import mx.rpc.Responder;
import mx.rpc.events.FaultEvent;
import mx.rpc.events.ResultEvent;

use namespace mx_internal

public class AsyncTokenWrapper extends AsyncToken
{

//---------------------------------
//
// Constructor
//
//---------------------------------

public function AsyncTokenWrapper(
token : AsyncToken,
resultProcessingFunction : Function = null,
faultProcessingFunction : Function = null )
{
super();

_token = token;

addResponders();

this.resultProcessingFunction = resultProcessingFunction;
this.faultProcessingFunction = faultProcessingFunction;
}

//---------------------------------
//
// Private Variables
//
//---------------------------------
private var _token : AsyncToken;

//---------------------------------
//
// Properties
//
//---------------------------------

public var resultProcessingFunction : Function;
public var faultProcessingFunction : Function;

//---------------------------------
//
// Public Methods
//
//---------------------------------
private function onResult( data : Object ) : void
{
var originalResultEvent : ResultEvent = ResultEvent( data );
var result : Object = originalResultEvent.result;
if( resultProcessingFunction != null )
{
result = resultProcessingFunction( result );
}

var newResultEvent : ResultEvent = new ResultEvent(
ResultEvent.RESULT,
originalResultEvent.bubbles,
originalResultEvent.cancelable,
result,
this,
originalResultEvent.message );

applyResult( newResultEvent );

}

private function onFault( info : Object ) : void
{
var originalFaultEvent : FaultEvent = FaultEvent( info );
var fault : Fault = originalFaultEvent.fault;
if( faultProcessingFunction != null )
{
fault = faultProcessingFunction( fault ) as Fault;
}

var newFaultEvent : FaultEvent = new FaultEvent(
FaultEvent.FAULT,
originalFaultEvent.bubbles,
originalFaultEvent.cancelable,
fault,
this,
originalFaultEvent.message
);

applyFault( newFaultEvent );
}

//---------------------------------
//
// Private Functions
//
//---------------------------------
private function addResponders() : void
{
_token.addResponder(
new Responder( onResult, onFault ) );
}
}
}

Sorry for the way the code gets mashed up there but you can click on the little button top right to get it in a new window.

4 comments:

  1. One enhancement i'd recommend is putting the resultProcessingFunction call within a try/catch statement so that custom processors can throw errors or faults and the asyncTokenWrapper can dispatch them as FaultEvents through the onFault method

    ReplyDelete
  2. Yup, that's a nice enhancement. I've actually moved on from this implementation now as I didn't really like having to extend AsyncToken in this way. I have a neater implementation which I'll blog about at some point but your point is still valid with the new way I'm doing things.

    Thanks for the comment.

    ReplyDelete
  3. Or, if you're using an HTTPService, you can subclass the SerializationFilter class, which is quite a clean way of doing this - I'm currently using this for a REST service that needs to format not just the response, but the request also.

    ReplyDelete
  4. I've just started playing with commands and started thinking about how to achieve exactly this. Thanks for writing it up.

    I'd be quite interested to see your neater implementation also; is it some point yet? :)

    ReplyDelete