Important note: Effective with the release of Adobe LiveCycle ES, the Adobe Flex Data Services 2 server product has been rebranded as a Solution Component of LiveCycle ES. This article was written based on Flex Data Services but will likely work as is with LiveCycle Data Services ES. Any articles referring to or using ColdFusion and Flex Data Services are not compatible with LiveCycle Data Services ES. To learn about the new capabilities of LiveCycle Data Services ES, see the tutorials in the LiveCycle Developer Center and read about Adobe LiveCycle Data Services ES.
Data is what it's all about. You want to provide users with a rich, interactive experience and you want to create that experience using data. The pairing of Flex and ColdFusion is a natural choice when selecting technologies that can provide a rich user experience while making the job easier for developers.
In this article, you'll examine how to move data from ColdFusion CFCs to Flex 2 applications using the tried-and-true web services and—for those who want more secure and compact communications channels and protocols—RemoteObject with the cool, new capabilities of ColdFusion MX 7.0.2.
To make the most of this article, you need to install the following software and files:
Important note: Effective with the release of Adobe LiveCycle ES, the Adobe Flex Data Services 2 server product has been rebranded as a Solution Component of LiveCycle ES. This article was written based on Flex Data Services but will likely work as is with LiveCycle Data Services ES. Any articles referring to or using ColdFusion and Flex Data Services are not compatible with LiveCycle Data Services ES. To learn about the new capabilities of LiveCycle Data Services ES, see the tutorials in the LiveCycle Developer Center and read about Adobe LiveCycle Data Services ES.
C:\CFusionMX7\wwwrootA basic knowledge of ColdFusion CFCs.
To install ColdFusion MX 7.0.2 with the new Gateway services, refer to the ColdFusion installation instructions.
To install Flex Builder 2, refer to the Flex Builder 2 installation instructions.
In this article, I assume that you selected the default installation options.
In the next Wizard panel:
On the Source Path tab in the next Wizard panel:
These steps create a Flex project called HelloWorld_ro with bin and html-template subdirectories that contain two sample Flex applications: HelloWorld_ro.mxml and HelloWorld_ro_more_datatypes.mxml. The HelloWorld_ro.mxml file is set as the default application.
Now let's look at the CFC:
<cfcomponent name="HelloWorld">
<cffunction name="sayHelloString" access="remote" returnType="string">
<cfreturn "Hello World!">
</cffunction>
</cfcomponent>
Pretty simple, right?
The Flex application is also fairly straightforward:
<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="vertical" >
<mx:Script>
<![CDATA[
import mx.rpc.events.ResultEvent;
import mx.controls.Alert;
[Bindable]
public var sResult:String;
public function handleStringResult(event:ResultEvent):void{
sResult=event.result as String;
}
]]>
</mx:Script>
<mx:RemoteObject
id="myService"
destination="ColdFusion"
source="HelloWorld"
showBusyCursor="true">
<mx:method name="sayHelloString" result="handleStringResult(event)" fault="Alert.show(event.fault.message)"/>
</mx:RemoteObject>
<mx:Label id="lblStringResult" text="{sResult}"/>
<mx:Button label="get String via Remote Object" click="myService.sayHelloString()"/>
</mx:Application>
Let's continue by looking at the RemoteObject tag. The RemoteObject has an ID of myService by which it can be referenced. The HelloWorld source attribute refers to the CFC by its fully qualified path. (Because it sits directly beneath the ColdFusion webroot, the CFC has no parent-package folders in its path.)
The child tag method maps to a method of the CFC using the name attribute and sets a result handler function, handleStringResult, via the result attribute, passing an event parameter to the result handler which is handed to the RemoteObject by the result returned from the CFC. Similarly, a fault handler function is assigned in the method tag. In this case, the event's fault.message is passed to an Alert.show function, which is a class shipped with Flex and imported earlier in the <mx:Script> block of code.
If you look at the handleStringResult function in the <mx:Script> block of code, you'll see that it simply sets the result property of the passed event parameter to a globally scoped public variable named sResult (which is typed as a String). That result property might not be a String, so the as String part of the assignment casts event.result to a String.
Above the sResult variable declaration is a metadata tag denoted by the square brackets, []. The Bindable metadata keyword indicates that properties bound to this variable through the use of curly braces, {}, will be updated when the value of the variable changes.
If you look at the <mx:Label> component, you'll see that its text property is bound to the sResult variable. Notice that the type used in the result handler function, mx.rpc.events.ResultEvent, is imported at the top of the <mx:Script> block of code.
Finally, you can see that there's an <mx:Button> component that invokes the sayHelloString method when it's clicked.
But wait! What about the destination property of the RemoteObject? It has a value of ColdFusion. Where does that come from?
In the top MenuBar within Flex Builder, select Project > Properties. Next, click the Flex Compiler branch to the left of the Properties dialog box. Notice the Additional Compiler Arguments text-imput field. Do you see the part of the entry that contains: --services=C:\CFusionMX7\wwwroot\WEB-INF\flex\services-config.xml? That compiler switch references a configuration file that provides information about services to be used during compilation:
<destination id="ColdFusion">
<channels>
<channel ref="my-cfamf"/>
</channels>
<properties>
<source>*</source>
<!-- define the resolution rules and access level of the cfc being invoked -->
<access>
<!-- Use the ColdFusion mappings to find CFCs, by default only CFC files under your webroot can be found. -->
<use-mappings>false</use-mappings>
<!-- allow "public and remote" or just "remote" methods to be invoked -->
<method-access-level>remote</method-access-level>
</access>
<property-case>
<!-- cfc property names -->
<force-cfc-lowercase>false</force-cfc-lowercase>
<!-- Query column names -->
<force-query-lowercase>false</force-query-lowercase>
<!-- struct keys -->
<force-struct-lowercase>false</force-struct-lowercase>
</property-case>
</properties>
</destination>
Looking at that configuration file, you can see a destination with id="ColdFusion", which is referenced in the destination attribute of the RemoteObject tag. This destination configures several properties: source, access and property-case (used to manage case-sensitivity issues with ColdFusion). These properties are well commented in the configuration file.
The destination also refers to a channel, which appears later in the configuration file and looks like this:
<channel-definition id="my-cfamf" class="mx.messaging.channels.AMFChannel">
<endpoint uri="http://{server.name}:{server.port}{context.root}/flex2gateway/" class="flex.messaging.endpoints.AMFEndpoint"/>
<properties>
<polling-enabled>false</polling-enabled>
<serialization>
<instantiate-types>false</instantiate-types>
</serialization>
</properties>
</channel-definition>
You can find more information about configuring channels and destinations in the Flex documentation.
Now that you've looked at the code and some of its inner workings, let's run the sample.
Make sure that your ColdFusion server is running. Then, within Flex Builder, click the green Run arrow in the top ToolBar. A browser opens and you see a simple Flex application containing a single Button called "get String via Remote Object." When you click the Button, a Label named "Hello World!" appears.
In the next wizard panel:
In the next wizard panel, accept the default values and click Finish.
These steps create a Flex project called HelloWorld_ws with bin and html-template subdirectories that contain two sample Flex applications: HelloWorld_ws.mxml and HelloWorld_ws_more_datatypes.mxml. The HelloWorld_ws.mxml file is set as the default application.
This application is almost identical to the RemoteObject example:
<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="vertical" >
<mx:Script>
<![CDATA[
import mx.rpc.events.ResultEvent;
import mx.controls.Alert;
[Bindable]
public var sResult:String;
public function handleStringResult(event:ResultEvent):void{
sResult=event.result as String;
}
]]>
</mx:Script>
<mx:WebService id="myService"
useProxy="false"
wsdl="http://localhost:8500/HelloWorld.cfc?wsdl">
<mx:operation name="sayHelloString" result="handleStringResult(event)" fault="Alert.show(event.fault.message)"/>
</mx:WebService>
<mx:Label id="lblStringResult" text="{sResult}"/>
<mx:Button label="get String via WebService" click="myService.sayHelloString.send()"/>
</mx:Application>
The major difference in this code is the <mx:WebService> tag, which replaces the <mx:RemoteObject> tag. Notice that the child tag of <mx:WebService> looks the same as the one for <mx:RemoteObject>, except that it is called <mx:operation> rather than <mx:method>.
Also, invoking the web service operation is slightly different as seen in the Button's click attribute. The useProxy attribute of the WebService tag is set to false because this example does not use a Flex server (Flex Data Services 2) for a proxy, but accesses the web service directly. So, if the web service is not hosted from the same domain that hosts this application SWF, then a cross-domain.xml file will be necessary. (A sample cross-domain.xml file is included in the ZIP file that accompanies this article.) The wsdl attribute of the WebService tag points to the HelloWorld.cfc. The rest of the code is identical to the RemoteObject example.
Now, let's run the sample. Again, make sure that your ColdFusion server is running. Then, within Flex Builder, click the green Run arrow in the top ToolBar. A browser opens and you see a familiar-looking Flex application that contains a single Button. This time, the Button is called "get String via Webservice". Click the Button and a Label called "Hello World!" appears.
These two samples look very similar, produce similar results, and use the same CFC on the back end. Why would you choose one over the other? Well, Webservices is a standards-based protocol whereas RemoteObject uses AMF, a binary protocol. Webservices returns data in a text-based, human-readable format and you can get information about a web service by requesting the web service wsdl.
So, if you are not the owner of the CFC and it is exposed for remote access, then it might make sense to use it as a web service. Alternatively, if you are the owner of the CFC, using the AMF protocol with RemoteObject will provide better performance.
If you take a look at the HelloWorld.cfc provided with the sample code, you'll see that there are additional methods beyond the one that was used for the RemoteObject and Webservices examples.
In addition to the method that returns the String, "Hello World!", there are methods that return an array, struct, and query recordset of the phrase "Hello World!" with translations into French, Spanish, and Latin.
There is also a method that takes a String parameter with acceptable values of string, array, struct, or query whose value determines the corresponding datatype to return. The HelloWorld_ro_more_datatypes.mxml and HelloWorld_ws_more_datatypes.mxml Flex applications (included in the sample ZIP file that accompanies this article) demonstrate these other methods.