Supercharge Your Flex App with ColdFusion Power

Tweet

There are quite a few tutorials here at SitePoint that can help you grasp some of the key principles of creating Rich Internet Applications (RIAs) using Flex and AIR. You’ll find that most development undertakings in Flex will involve a back-end application to interact with the Flex client.

Let’s discuss some of the theories and principles behind what makes up a Flex application, and then put those principles into practice with a ColdFusion app. We’ll assume here that you have some experience already with ColdFusion development.

Pay attention, because there’s a quiz at the end. The first 100 people to complete the quiz will win themselves a copy of Getting Started With Flex 3, thanks to our sponsor, Adobe. Take the quiz!

Understanding a Rich Internet Application’s Architecture

From a high level point of view, the common systems architecture of a web application usually comprises three layers.

The bottom tier consists of a data storage layer, usually a relational database system such as Microsoft’s SQL Server, Oracle, or MySQL. Such a layer provides a relational table model that can be used to store and retrieve application data.

The layer above the data storage layer is known as the application server, or middleware. Commonly used technologies in this playground are Adobe’s ColdFusion, Java, PHP, Ruby on Rails, or .NET. Those platforms are used to develop business and data access logic.

On top of that, or perhaps even embedded in the middleware, we’ll find a layer responsible for HTTP delivery – that is, web servers like IIS or Apache. In Rich Internet Applications, architects sometimes have to deal with other protocols besides HTTP: technologies such as Flash Media Server, for example, support the real-time streaming protocol RTMP.

In my earlier tutorial, I showed how a Flex application could communicate with other applications and data on the client side. This time, we’ll now communicate with our business and data layers on the server side.

How Flex Communicates

Flex can access remote data mainly by three different approaches:

  • HTTP calls to flat files, XML files or dynamic URLs that deliver data Flex can work with
  • SOAP-based web service calls
  • Action Message Format (AMF) remote object calls.

Each of these methods is represented by different ActionScript classes and MXML tags. It’s fair to say that the MXML tag syntax is probably easier to use when coming from a ColdFusion background, because you’re comfortable being able to use a similar syntax to ColdFusion’s CFML syntax.

HTTPService: Retrieving Data Over HTTP

Let’s think about the tag mx:HTTPService. Unsurprisingly, this is an HTTP-based service, which we can use to grab some data from elsewhere at runtime. In MXML, such a service would be declared as below:

<mx:HTTPService id="dataService" 
url="http://www.example.com/xmlfile.xml" />

The id attribute provides a reference to the service object. The url attribute points to a static XML file that’s accessible via HTTP, but could also point to a local file, like so:

<mx:HTTPService id="dataService" url="xmlfile.xml" />

In this case the Flex application would expect to find the xml file in the same path as itself.

Naturally, it’s possible to have ColdFusion generate some XML for us. Here’s a very simple way of doing this:

<cfsavecontent variable="xmlContent"><data> 
   <result>            
       <item>Taxes</item>
       <amount>2000</amount>
   </result>
   ...
</data></cfsavecontent>

<cfcontent reset="true" type="text/xml"><cfoutput>#xmlContent#</cfoutput>

The tag stores created static or dynamic content in a variable, xmlContent. We then use a cfcontent tag to reset any output we may have created earlier and specify a text/xml content-type. We then deliver the content of the variable xmlContent using a simple cfoutput tag. Voilà, out comes some XML.

On the next page, we’ll look at a Flex application that would consume that XML.

A Flex application to cater for such a service call would look like:

<?xml version="1.0" encoding="utf-8"?>  
<mx:Application  
 xmlns:mx="http://www.adobe.com/2006/mxml"  
 layout="horizontal"  
 creationComplete="cfmFile.send()">  
 
   <mx:Script>  
       <![CDATA[  
           import mx.controls.Alert;  
           import mx.rpc.events.ResultEvent;  
           import mx.rpc.events.FaultEvent;  
           import mx.collections.ArrayCollection;  
 
           [Bindable]  
           private var budgetData:ArrayCollection = new ArrayCollection();  
 
           private function cfmFileRH(e:ResultEvent):void  
           {  
               budgetData = e.result.data.result as ArrayCollection;  
           }  
 
           private function cfmFileFH(e:FaultEvent):void  
           {  
               mx.controls.Alert.show(e.fault.faultString,"Error when loading XML");  
           }  
 
 
       ]]>  
   </mx:Script>  
 
   <mx:HTTPService  
   id="cfmFile"  
   url="http://example.com/budgetXML.cfm"  
   result="cfmFileRH(event)"  
   fault="cfmFileFH(event)"  
   />  
 
   <mx:DataGrid dataProvider="{budgetData}" >  
   </mx:DataGrid>  
 
</mx:Application>

Here’s what’s happening: we’re using an mx:HTTPService to grab some data, and storing the result from the service in a variable named budgetData. The creationComplete event handler on the tag sends the HTTPService request when the application is created. Finally, a mx:DataGrid object displays the data as a table.

Looking closely at the mx:HTTPService we can see that it includes two attributes: result and fault. In Flex, any data service request is dealt with asynchronously, meaning that the Flex application will immediately broadcast a result event instead of waiting for a response. All service tags offer the attributes result and fault to deal with either of these outcomes. In those attributes you’d just specify the name of the method you’d like to be called whenever a result (or a fault) comes back from the service call.

Practically speaking, an instance of mx:HTTPService is limited to GET and POST requests, although in principle other requests can be issued – but these are unavailable unless the HTTP requests are being routed via a proxy server, such as Adobe’s BlazeDS or LiveCycle Data Services. The reason for that limitation is the security model of the Flash Player, which only supports direct GET or POST calls via HTTP. That’s where another concept enters the game: a cross-domain policy file.

Normally, a Flash-based application using an mx:HTTPService or a SOAP-based web service may only retrieve data from a service or file stored on the same server as the .swf file. To determine whether the data is being pulled from the same domain, Flash Player compares the domain names used for the Flash file and the remote data source. This means that an application loaded from http://localhost/test.swf is unable to access HTTP data from http://127.0.0.1/data/xmlfile.xml – even though they are the same server. One solution is to use a cross-domain policy file, named crossdomain.xml, placed in the web root of the server that’s meant to provide the data. You can learn more about how to use a cross-domain policy file in Adobe’s documentation.

Next, let’s find out about sending request parameters within Flex.

Sending Request Parameters

Perhaps you need to send parameters to your service. The easiest way to pass in parameters is by adding an mx:request tag as a nested tag of the HTTP service. Let’s imagine we need to send two parameters, dataMethod and userType, to a ColdFusion script. We’d use the mx:request element like so:

<mx:HTTPService id="dataService" url="http://example.com/sendDataRequest.cfm" method="POST">   
  <mx:request>  
      <dataMethod>getAll</dataMethod>  
      <userType>administrator</userType>  
   </mx:request>  
</mx:HTTPService>

Since we’re using the HTTP POST method, all the request variables we’re creating here will become ColdFusion variables in ColdFusion’s FORM scope. On the ColdFusion side, we’re therefore going to end up with FORM.dataMethod as well as FORM.userType on the ColdFusion template. If you’d chosen HTTP GET (by specifying method="GET"), your request data would become URL variables: URL.userType and URL.dataMethod in this case.

So far we’ve just looked into returning XML data via HTTP services from ColdFusion templates. Although that’s a very common way to interact with HTTP services and ColdFusion, there are some other, alternative return formats for HTTP services that in some occasions might be more appropriate to use:

  • object: response data is XML and will be converted into a tree of ActionScript objects (ArrayCollection, ObjectProxy)
  • xml: response data is XML and will be converted into an ActionScript object of type XMLnode – this is a legacy format which is only here for compatibility; it’s best to avoid using it.
  • e4x: response data is XML and will be converted into an ActionScript XML object
  • flashvars: response data is a chain of key/value-pairs: name1=value1&'name2=value2, and so on
  • text: response data is text, no conversion happens.
resultFormat="object" is the default, and is quite often the right way to go. If you want to work with XML instead of ArrayCollections and ObjectProxy, e4x (ECMAScript for XML) is the preferred result format - xml makes use of a deprecated set of API classes that's part of Flex for compatibility reasons only.

Providing a Service with ColdFusion

So: we've spent a good amount of this article talking about HTTP services and the general structure of a service. Now that we understand how these work, discussing web services and remote objects becomes much easier.

Let's take the following ColdFusion component that offers one simple echo method to the outside world, and try to build a Flex client that hooks into that CFC:

<cfcomponent>x   
   <cffunction  
     name="echo"  
     returntype="string"  
     access="remote"  
     output="false"  
     hint="I echo whatever I'm being passed">  
       <cfargument name="input" type="string" required="true">  
       <cfreturn "Echoing..." & arguments.input>  
   </cffunction>  
</cfcomponent>

ColdFusion offers two ways of exposing the functionality of that component's method to a caller: as a SOAP-based web service or as a remote object. Let's try each.

SOAP

Starting with the SOAP web service, we've made the echo method a web service method by setting the attribute access="remote" in the cffunction tag. At the same time, this setting will enable us to call the method as a remote object. We'd like to retrieve the data in the WSDL format, which can be accomplished simply by adding ?WSDL to the end of the URL.

To grab that WSDL in Flex, we start off using a mx:WebService, which is structured in much the same manner as a mx:HTTPService:

<mx:WebService    
 id="dataWS"    
 wsdl="http://example.com/service.cfc?WSDL"    
 result="onDataServiceResult(event)"    
 fault="onDataServiceFault(event)"    
/>

With web services you'll quite often find that a service offers multiple methods, and that you'd prefer to specify a result or fault handler method per service. That's possible when using mx:operation:

<mx:WebService    
     id="dataWS"    
     wsdl="http://example.com/service.cfc?WSDL"    
     fault="onDataServiceFault(event)"    
   >    
       <mx:operation name="echo" result="onResultEcho(event)" />    
   </mx:WebService>

Providing parameters to web service calls works in a similar way to providing those to HTTP service calls - via mx:request - but may also be done by using a syntax familiar to developers coming from languages like Java or C++:

dataWS.echo("Hello")
Action Message Format

Now, let's try this with the Action Message Format (AMF). AMF is a protocol that's been around for quite a while, and AMF3's specification was publicly released by Adobe in December 2007. On the server side you'd need an AMF3-enabled service - luckily, as a ColdFusion developer you're ready to provide this type of service right out of the box.

In Flex we can use the mx:RemoteObject tag to communicate with remote objects:

<mx:RemoteObject    
 id="dataRS"    
 destination="ColdFusion"    
 source="example.service">    
   <mx:method    
     name="echo"    
     result="onResultEcho(event)"    
     fault="onFaultEcho(event)"    
 />    
</mx:RemoteObject>

Comparing this code with the equivalent web service declaration in Flex, there are a few small differences: we're dealing with a destination named "ColdFusion" now, and mx:operation has changed to mx:method.

When one first sets up a Flex project for ColdFusion in Flex Builder, the setup wizard will ask a bunch of questions regarding the location of the ColdFusion server, its port, URL, and context root. That information provided will then be used at the compile time of the Flex application to provide Flex with the details of your AMF URL. This works smoothly and easily for simple data types such as strings or Booleans, or even built-in ColdFusion types such as arrays and structures.

You may even wish to deal with entire objects from your application's business domain, and transfer them back and forth. There's a design pattern called data transfer object or value object that describes such a process, and it's fairly simple to use this method with ColdFusion components. Your service configuration in Flex will mostly remain the same, but instead of sending or expecting simple types from your remote object call, it will be a domain object: an object that represents a customer, shopping cart, employee, or whatever it is you plan to deal with. To allow Flex to deal with such complex object types, ColdFusion has to know about them as well, and there would have to be an equivalent component in ColdFusion. Here's a CFC that describes a user:

<cfcomponent displayname="User" output="false">    
  <cfproperty name="user_id" type="numeric" />    
  <cfproperty name="user_name" type="string" />    
   
  <cffunction name="init" access="public" returntype="example.User" output="false">    
     <cfargument name="user_id" type="numeric" required="false"  />    
     <cfargument name="user_name" type="string" required="false"  />    
   
     <cfset this['user_id'] = arguments.user_id />    
     <cfset this['user_name'] = arguments.user_name />    
   
     <cfreturn this />    
  </cffunction>    
</cfcomponent>    

In the following minimal ActionScript 3 class, we also describe what a User object ought to contain:

[Bindable]    
[RemoteClass(alias="example.User")]    
public class User    
{    
       public var user_id:int=0;    
       public var user_name:String="";    
}

The metadata tag [Bindable] allows User to be used in Flex data bindings. [RemoteClass(alias="example.User")] maps the CFC type example.User on the server end to the User class here in ActionScript.

Now What?

Where do we go from here? If you're only using ColdFusion, my guess is that you'll find it's easier to just use RemoteObjects for these kinds of projects. If you have a mixed environment comprising multiple technologies, it might be worth looking into web services and HTTP service calls to load XML. Either way, you're now equipped to deal with either situation: time to have fun!

Think you're ready to kick butt with ColdFusion? Why not test your newfound knowledge with our quiz - just five easy questions, and all the answers were right here in this article. The first 100 people to take our quiz will win a copy of Getting Started with Flex delivered straight to your door absolutely free. Grab yours!

Free book: Jump Start HTML5 Basics

Grab a free copy of one our latest ebooks! Packed with hints and tips on HTML5's most powerful new features.

No Reader comments