Using BlazeDS from a Flash or AS3-only project

Some weeks ago I had to develop a light version of an existing Flex application. The Flex application consumed lot of Java logic through services exposed by BlazeDS’s RemoteObjects.

The goal was to be able to consume, from the new application, the exact same Java Services that the Flex application was consuming through RemoteObjects without modifying the Java code or the server gateway at all.

This scenario could be a very common scenario in the near future when porting, adapting or extending existing applications to different devices targetting Flash Player 10.1.

BlazeDS uses some kind of handshake between the client and the server as well as control messages that were not obvious to replicate in a custom implementation using a plain NetConnection object. Also I wanted to make use of the several more complicated features that Flex rpc implementation brings.

Basically the problem became to “How could I use the entire component set from rpc.swc distributed with Flex SDK in a Flash or AS3-only project?”

This excercise is also applicable to WebService, HttpService, Consumer or Producer (messaging) classes.

Step 1.

Add rpc.swc from {FLEX_HOME}/framework/libs to your project or .fla classpath

Step 2

Compile some simple code using RemoteObjects:

const ENDPOINT : String = "http://your.domain/messagebroker/amf";
const DESTINATION : String = "workflowService";

remoteObject = new RemoteObject();
remoteObject.destination = DESTINATION;
remoteObject.endpoint = ENDPOINT;
remoteObject.addEventListener( ResultEvent.RESULT, resultHandler );
remoteObject.addEventListener( FaultEvent.FAULT, faultHandler );
remoteObject.findAll();

Compiling this code throws several different errors due to unsatisfied or missing dependencies:

mx.logging.ILogger
mx.rpc.IResponder

These 2 classes are in framework.swc Copying the swc from the FlexSDK into your classpath will fix the issues.

Step 3

After that you’ll get errors due to missing resource bundles like:
“Unable to resolve resource bundle ‘collections’ for locale ‘en_US’”

Copying to the classpath the required resource bundles from {FLEX_SDK}/frameworks/locale/ will fix the problem. You’ll only need to copy rpc_rb.swc and framework_rb.swc

You will receive this error only if compiling the project using the Flex compiler. You won’t get this error if you compile your application in Flash.

Step 4

When the application finally compiles you’ll always get FaultEvents with not too much information.

faultCode: “Client.Error.MessageSend”
faultDetail: “Channel.Connect.Failed error null url: ‘null’”

Using Charles I realized that the request was not even sent, something was going wrong.

Step 5

All RPC classes use the Flex logging API so enabling it in the Flash project could be helpful. To enable it we only need to add:

Log.addTarget( new TraceTarget());

Step 6

After a couple of hours of debugging I discovered that RPC library to properly work needs to be initialized in the following way:

import flash.display.LoaderInfo;
import mx.core.mx_internal;
import mx.messaging.config.LoaderConfig;

use namespace mx_internal;

LoaderConfig.mx_internal::_url = loaderInfo.url;
LoaderConfig.mx_internal::_parameters = loaderInfo.parameters;

Where loaderInfo is the loaderInfo object of the root Sprite of your application.

After adding the initialization code you will see the request going through in Charles.

Step 7

There’s still one last problem to solve:

“TypeError: Error #1034: Type Coercion failed: cannot convert Object@20612a79 to mx.messaging.messages.ErrorMessage.”

The error means that somewhere an ErrorMessage object was expected but instead an object of type Object is received. We need to register several types. This will happen with several different types of ojects. To solve it:

registerClassAlias("flex.messaging.messages.ErrorMessage", ErrorMessage);
registerClassAlias("flex.messaging.messages.CommandMessage", CommandMessage);
registerClassAlias("flex.messaging.messages.RemotingMessage", RemotingMessage);
registerClassAlias("flex.messaging.messages.AcknowledgeMessage ", AcknowledgeMessage);
registerClassAlias("DSC", CommandMessageExt);
registerClassAlias("DSK", AcknowledgeMessageExt);
registerClassAlias("flex.messaging.config.ConfigMap", ConfigMap);
registerClassAlias("flex.messaging.io.ArrayCollection", ArrayCollection);
registerClassAlias("flex.messaging.io.ObjectProxy", ObjectProxy);

After properly registering these classes everything should work as expected (the list of required registrations might not be complete, if you find a TypeCoercion error consider registering the object).

The entire code you need:

import flash.display.Sprite;
import flash.events.Event;
import flash.net.registerClassAlias;

import mx.collections.ArrayCollection;
import mx.core.mx_internal;
import mx.logging.Log;
import mx.logging.targets.TraceTarget;
import mx.messaging.config.ConfigMap;
import mx.messaging.config.LoaderConfig;
import mx.messaging.messages.AcknowledgeMessage;
import mx.messaging.messages.AcknowledgeMessageExt;
import mx.messaging.messages.CommandMessage;
import mx.messaging.messages.CommandMessageExt;
import mx.messaging.messages.ErrorMessage;
import mx.messaging.messages.RemotingMessage;
import mx.rpc.events.FaultEvent;
import mx.rpc.events.ResultEvent;
import mx.rpc.remoting.RemoteObject;
import mx.utils.ObjectProxy;

use namespace mx_internal;

const ENDPOINT : String = "http://your.domain/messagebroker/amf";
const DESTINATION : String = "workflowService";

var remoteObject : RemoteObject;

function registerObjects() : void
{
    registerClassAlias( "flex.messaging.messages.ErrorMessage", ErrorMessage );
    registerClassAlias( "flex.messaging.messages.CommandMessage", CommandMessage );
    registerClassAlias( "flex.messaging.messages.RemotingMessage", RemotingMessage );
    registerClassAlias( "flex.messaging.messages.AcknowledgeMessage ", AcknowledgeMessage );
    registerClassAlias( "DSC", CommandMessageExt );
    registerClassAlias( "DSK", AcknowledgeMessageExt );
    registerClassAlias( "flex.messaging.config.ConfigMap", ConfigMap );
    registerClassAlias( "flex.messaging.io.ArrayCollection", ArrayCollection );
    registerClassAlias( "flex.messaging.io.ObjectProxy", ObjectProxy );
}


function initializeRPC() : void
{
    LoaderConfig.mx_internal::_url = loaderInfo.url;
    LoaderConfig.mx_internal::_parameters = loaderInfo.parameters;
}


function initializeLogging() : void
{
    Log.addTarget( new TraceTarget());
}


function createRemoteObject() : void
{
    remoteObject = new RemoteObject();
    remoteObject.destination = DESTINATION;
    remoteObject.endpoint = ENDPOINT;
    remoteObject.addEventListener( ResultEvent.RESULT, resultHandler );
    remoteObject.addEventListener( FaultEvent.FAULT, faultHandler );
    remoteObject.findAll();
}


function resultHandler( event : Event ) : void
{
    trace( "invocation successful" );
}


function faultHandler( event : Event ) : void
{
    trace( "invocation failed" );
}

registerObjects();
initializeLogging()
initializeRPC();
createRemoteObject();

Pros and cons of the solution

  • ~100Kb footprint
  • You can focus on adding value and not building the infrastructure.
  • Faster time to market
  • Solid market solution

Improvement points

Using the above solution you’ll have to work with ArrayCollection instead of Arrays which in certain situations might not be necessary, might have a performance impact as well as increase the number of class dependencies and file size.

One possible way to get rid of ArrayCollection and all its dependencies (i.e. ListCollectionView, Cursor, SystemManager, etc.) would be to create your own map. Instead of using:

registerClassAlias( "flex.messaging.io.ArrayCollection", ArrayCollection );

You could use:

registerClassAlias( "flex.messaging.io.ArrayCollection", YourClass );

Where your class would implement IExternalizable. This class could be completely light and would mean a considerable improvement in terms of dependencies and lighter implementation.

17 Comments to “Using BlazeDS from a Flash or AS3-only project”

  1. Matt 27 March 2010 at 9:42 pm #

    Great post.

    I wonder if solutions like this one will become more widely popular as developers start to migrate towards AS3 only project for mobile. Additionally, I’d like to see something like this simplified for the new light weight component frameworks like reFlex.

    Thanks for posting this!

  2. Peter G 28 March 2010 at 12:36 am #

    So I’m curious as to how this works on the runtime side.

    Let’s imagine I write a simple Flash application that uses this methodology. Are the classes required for the remote communication compiled into my SWF? Shouldn’t that make the SWF much larger than 100k?

    If they aren’t compiled into the SWF, how does this actually work? Does the user of the Flash app have to have the Flex SWC files available on their system?

    I’m not a Flash/Actionscript/Flex person by background, so I’m not super familiar with the linking behavior of these applications.

  3. xbeumala 29 March 2010 at 11:10 am #

    Hi Peter,

    Required classes will be compiled to the swf yes. The simple test I built was 100K but this can be reduced if you do not link ArrayCollection and its dependencies as explained.

  4. Rémi.T 29 March 2010 at 2:40 pm #

    You can also call BlazeDS methods using the fp “native” flash.net.NetConnection class.

    With an 100 lines custom class you can recreate the basic RPC system without importing any of the flex framework classes.

    I use this for an air kiosk application where any useless code can create memory leak. And the flex framework is a leak generator. :)

  5. xbeumala 29 March 2010 at 2:47 pm #

    Hi Rémi,

    as explained in the post you can create a connector using a plain NetConnection object but it won’t work in lots of cases since you should implement the handshake and control messages.

    If you have any leak you should notify them in http://bugs.adobe.com

  6. Ankur Gautam 5 May 2010 at 10:14 pm #

    Hi Guys

    A nice blog , i am doing a strange activity i have compiled Blaze ds to JDK 1.4 compatible and when i am using those jars i am getting an error
    TypeError: Error #1034: Type Coercion failed: cannot convert Flex Message(flex.messaging.messages.AcknowledgeMessageExt)
    clientId = null
    correlationId = null
    destination = null
    messageId = AB0BB538-E949-0800-912A-7D44EFF626BC
    timestamp = 1273090468345
    timeToLive = 0
    body = null

    Can some of ur flex geeks through some light ….

    Ankur

  7. Ankur Gautam 5 May 2010 at 11:02 pm #

    I tried the code mentioned above getting a null pointer exception for the lines

    LoaderConfig.mx_internal::_url = loaderInfo.url;
    LoaderConfig.mx_internal::_parameters = loaderInfo.parameters;

    Am i missing something…….

  8. Mete Atamel 6 May 2010 at 4:51 pm #

    If all you want to do is remoting calls with NetConnection, you can do that with 2 lines of code, check this out:

    http://meteatamel.wordpress.com/2010/01/22/netconnection-and-remoting-in-blazedslcds/

  9. Olivier 28 May 2010 at 11:39 am #

    Thank you so much for the long time you took to debug things that I never have the courage to debug !

  10. Tucker Connelly 10 June 2010 at 9:54 pm #

    You made my day, man. I spent hours trying to figure out the error in Step 4. Thanks!

  11. Heath 24 September 2010 at 3:50 am #

    This is exactly what I have been looking for!

    Everything compiles fine in my .fla but I get the following output error:

    VerifyError: Error #1014: Class mx.rpc.remoting::RemoteObject could not be found.

    at global$init()

    Thanks,
    Heath

  12. hitesh 12 November 2010 at 4:10 pm #

    As iam new to flex environment
    i gone through blaze ds test drive i got one doubt i.e whenever we run the application in server the mxmls are recompiled…………which would be proble in testing and project staging phase…could u help me out in one time executionnnn please…..

  13. Aenkh 2 December 2010 at 11:20 am #

    First of all, thank you for this great post.

    I simply wanted to point out one thing about the ArrayCollection problem. You can easily have Java return AS3 Arrays instead of mx.collections.ArrayCollection by configuring the BlazeDS channel in services-config.xml.

    Based on the “Configuring AMF serialization on a channel” § on this page http://livedocs.adobe.com/blazeds/1/blazeds_devguide/help.html?content=serialize_data_3.html#300626 , you just need to add legacy-collection property to your channel-definition’s serialization properties.

    Simply modify your channel-definition and you’ll be done with it :

    true

    Hope this helps!

  14. Aenkh 2 December 2010 at 11:26 am #

    Oops … I didn’t know posting XML in comments won’t work …
    “channel-definition” should look something like this in your services-config.xml :

    channel-definition
    endpoint/
    properties
    serialization
    legacy-collection true /legacy-collection
    /serialization
    /properties
    /channel-definition

    I think you pretty much got it =)

  15. Durrab Jami Khan 25 December 2010 at 8:46 am #

    Hello Xavi Beumala,
    I am pretty successful whatever you have done here and its working fine.

    I can do remote calls and Consumers are also getting subscribed to there destination.

    There is one strange problem. When I try to use my-streaming-amf Channel, While subscribing to it.
    I get Error mx.message.messages.IMessage cannot be converted to Object. I have done this as well.

    registerClassAlias(“flex.messaging.messages.Message”, IMessage);

    Where Message is the Spring BlazeDS Message Interface.

    But still even if I do this. I am not getting the message pushed from clients under messageHandler(msg:IMessage)

    You help will really solve my problem.

    Looking forward:

    Durrab

  16. [...] Using BlazeDS from a Flash or AS3-only project | rialvalue.comSome weeks ago I had to develop a light version of an existing Flex application. The Flex application consumed lot of Java logic through services exposed by BlazeDS’s RemoteObjects. [...]

  17. Rodrigo 4 October 2011 at 6:26 am #

    Hi Xavi,

    Thank you for this post.
    I followed your post.
    I’m using Flex 4.5.1 and BlazeDS 4.
    I created a RemoteObject, Producer and Consumer.
    Everything works ok.
    The only problem is that BlazeDS can’t serialize my custom objects.
    I did everything that finding in web but without sucess.
    Some suggestion?

    ps. You can use now just the RpcClassAliasInitializer.registerClassAliases();


Leave a Reply