QueTwo's Blog

thouoghts on telecommunications, programming, education and technology

Connecting your Flex application to BlazeDS or LiveCycle DS

If you have ever attended one of my presentations on BlazeDS, LiveCycle DS, or Flex/ColdFusion, you have heard me talk about how bad the data-connectivity wizards are in ALL of the IDEs available for the Flex SDK.  Even the new Flash Builder 4.5 dosen’t address the white-hot mess they call the data connectivity wizards that they started including in 4.0 (Side note:  I love most of the features in Flash Builder — I use it every day, but some of the wizards they included to make life easier really don’t).

Even including the services-config.xml document as a configuration option in your application will often lead you to troubles in the long-run.  This is included for you when you tell Flash Builder that you want to use ColdFusion or Java as your server model.  When you do this, the compiler inspects your services-config.xml configuration document on your server, and builds a table of the channels (endpoints) and destinations that are configured on the server.  You can then call the destination by name in theortags and not have to worry about how your client actually connects back to the server…

… untill you need to move your application…

… or you need connect your AIR application to your server…

… or you have a mobile or television application that needs resources on your server…

… or your server’s configuration changes.

In order to get around the above issues, you may need to code your communication methods in ActionScript.  By encapsulating your communications methods in ActionScript, you make your application a lot more portable, more configurable, and you can head off a lot of the issues that tend to develop by using the built-in wizards and the code that they generate.  If you code them by hand, you won’t be able to use the data-services bindings, or the ability to drag/drop stuff on to the stage to connect it to your back-end server.  But what you will end up with is a lot more flexible and portable among different projects.

Start off by creating a new ActionScript class.  I usually have a base class for each communications method that I’m working with — Remote Object or Messaging, and I extend that base class to match the methods that I need for my particular project.  Your new ActionScript class should at least extend the EventDispatcher class so you can throw events.  The example below, we will be creating a stub class that will be communicating with a ColdFusion server via RemoteObjects (AMF). 

With your ActionScript class created, you will need to declare 6 protected variables — a ChannelSet, an AMFChannel, a RemoteObject (not the mxml kind), and three Strings to represent the server, destination and source we are working with.  Our class should look like this to begin :

public class AMFManager extends EventDispatcher  
{   
   protected var _cs:ChannelSet;
   protected var _channel:AMFChannel;
   protected var _ro:RemoteObject;

   protected var _server:String;
   protected var _destination:String;
   protected var _cfcSource:String;

   public function AMFManager(server:String, destination:String, cfcSource:String)
   {
    _server = server;
    _destination = destination;
    _cfcSource = cfcSource;
    super(null);
   }
  }

Now, we will want to set up a new function that will setup our AMF connection.  This involves the instantiation of a channel, adding it to a channel set, and wiring up the RemoteObject.  We know that we will always be talking to a ColdFusion server, so we can assume we will be talking to the /flex2gateway/ endpoint (although, to allow more flexibility, we could make this modular as well), we will be talking over http, and we that the channel name is my-cfamf.  All this information is given to the AMFChannel that we instantiate. When working with ColdFusion 9 and above, we need to make sure we turn off smallMessages, because it needs the full header information in order for the ColdFusionAdapter to properly process the messages.  We then pass the AMFChannel to the ChannelSet — we will not be using a backup channel for this particular setup, although adding a second channel to the ChannelSet is trivial.  Finally, we setup the RemoteObject instance, and pass the ChannelSet, the source, and the destination that we wish to connect to the remote object. 

  protected function createAMFConnection():void
   {
    _channel = new AMFChannel("my-cfamf","http://" + _server + "/flex2gateway/");
    _channel.enableSmallMessages = false;

    _cs = new ChannelSet();
    _cs.addChannel(_channel);

    _ro = new RemoteObject();
    _ro.destination = _destination;
    _ro.source = _cfcSource;
    _ro.channelSet = _cs;
   }

I also make a habit of providing a general fault handler, so that if there is an error passed back by BlazeDS/LCDS/ColdFusion, that our app knows about it.  This general fault handler will only catch faults that are not caught by more specific fault handelers (for example, if you set one up on a method you are calling).  I add this handler in the createAMFConnection() function.   Don’t forget to call the createAMFConnection() during the constructor.

From this point, I would usually extend this main class and add in functions that are specific to my project or end-device.  For example, after I extend the class, if my application needs to work with the getDirList() method within my ColdFusion CFC, I could add the following functions :

  public function getFileList():void
   {
    _ro.getDirList.addEventListener(ResultEvent.RESULT, gotFileList);
    _ro.getDirList();
   }

   protected function gotFileList(event:ResultEvent):void
   {
    _ro.getDirList.removeEventListener(ResultEvent.RESULT, gotFileList);
    var e:FileListEvent = new FileListEvent(FILE_LIST_EVENT);
    e.fileList = event.message.body as ArrayCollection;
    dispatchEvent(e);
   }

All my controller would need to do is instantiate the class, call the function and listen for the event to come back — very similar to the way that the <mx:RemoteObject> tag operates.  But we do get the advantage of being able to control which sever, destinations and sources we connect to at runtime and on the client (without re-compiling!).

A completed class might look like the following.  I included the getFileList() function in this listing (this would normally be in an extended class) so you can see how things tie together.

package org.quetwo.common.managers
{
 import flash.events.Event;
 import flash.events.EventDispatcher;
 import flash.events.IEventDispatcher;

 import mx.collections.ArrayCollection;
 import mx.controls.Alert;
 import mx.messaging.ChannelSet;
 import mx.messaging.channels.AMFChannel;
 import mx.rpc.events.FaultEvent;
 import mx.rpc.events.ResultEvent;
 import mx.rpc.remoting.RemoteObject;

 import org.quetwo.common.events.FileListEvent;
 import org.quetwo.common.events.AMFErrorEvent;

 [Event(name="gotFileListEvent", type="org.quetwo.common.events.FileListEvent")]
 [Event(name="gotAMFErrorEvent", type="org.quetwo.common.events.AMFErrorEvent")]

 public class AMFManager extends EventDispatcher
 {
  public  static FILE_LIST_EVENT:String = "gotFileListEvent";
  public  static AMF_FAIL_EVENT:String = "gotAMFErrorEvent";
  private static _displayFailMessage:Boolean = false;  
  protected var _cs:ChannelSet;
  protected var _channel:AMFChannel;
  protected var _ro:RemoteObject;

  protected var _server:String;
  protected var _destination:String;
  protected var _cfcSource:String;

  public function AMFManager(server:String, destination:String, cfcSource:String)
  {   
   _server = server;
   _destination = destination;
   _cfcSource = cfcSource;
   super(null);

   createAMFConnection();
  }

  protected function gotFail(event:FaultEvent):void
  {
   if (_displayFailMessage)
   {
    mx.controls.Alert.show(event.fault.faultString,"Error Connecting to ColdFusion");  
   }
   var e:AMFFailEvent = new AMFFailEvent(AMF_FAIL_EVENT);
   e.fault = event.fault;
   dispatchEvent(e);
  }

  protected function createAMFConnection():void
  {
   _channel = new AMFChannel("my-cfamf","http://" + _server + "/flex2gateway/");
   _channel.enableSmallMessages = false; 

   _cs = new ChannelSet();
   _cs.addChannel(_channel);

   _ro = new RemoteObject();
   _ro.destination = _destination;
   _ro.source = _cfcSource;
   _ro.channelSet = _cs;
   _ro.addEventListener(FaultEvent.FAULT, gotFail);
  }

  public function getFileList():void
  {
   _ro.getDirList.addEventListener(ResultEvent.RESULT, gotFileList);
   _ro.getDirList();
  }

  protected function gotFileList(event:ResultEvent):void
  {
   var e:FileListEvent = new FileListEvent(FILE_LIST_EVENT);
   e.fileList = event.message.body as ArrayCollection;
   dispatchEvent(e);
  }
}
Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: