Chapter 26. Integrating Flex Applications with ColdFusion

Flash Remoting, the technology that allows Flash-based documents to communicate with Web-based resources over a high-speed, binary protocol, was first introduced with ColdFusion MX (also known as ColdFusion version 6). In the early days of the technology, before the introduction of Flex, applications built in Flash MX and subsequent releases had the ability to make remote procedure calls to functions of ColdFusion components (CFCs) over a standard Web connection.

When Flex 1.0 was released, Flash Remoting was adapted for use with Javabased application servers that hosted Java-based classes. Flex client applications could make calls to Java-based methods just as easily as with ColdFusion using the feature first known as Remote Object Services, now known as the Remoting Service.

Note

The Java-based Remoting Service is described in Chapter 24.

Adobe ColdFusion 8 continues to offer built-in support for Flash Remoting with Flex-based and Flash-based client applications, and it adds the ability to integrate tightly with features that are unique to LiveCycle Data Services or BlazeDS. When ColdFusion is integrated with these Adobe products, you can build and deploy Flex applications that share messages in real time or near real time with ColdFusion-based resources using the Message Service, and you can use LiveCycle Data Services' Data Management Service to create applications that synchronize data between multiple connected clients and servers.

Tip

All the currently available ColdFusion features that support integration with Flex client applications were first introduced in ColdFusion version 7.02. With the exception of the event gateway used to integrate Flex applications with the Message Service, all the features described in this chapter work equally well with ColdFusion 7.02 or ColdFusion 8.

In this chapter, I describe how to use Flash Remoting to call ColdFusion component functions from Flex applications. In Chapter 27, I describe how to use the ColdFusion Extensions for Eclipse to interact with ColdFusion during development and generate server-side ColdFusion components for use with Flex/ColdFusion applications.

Note

To use the sample code for this chapter, download chapter26.zip from the Web site. Follow the instructions later in this chapter to create a Flex project for use with ColdFusion and install the various project components to the client and server.

Understanding Flash Remoting and ColdFusion 8

The feature known as the Remoting Service in LiveCycle Data Services and BlazeDS is known as Flash Remoting in ColdFusion. Flash Remoting allows you to directly make calls to functions of ColdFusion components using the Flex framework's RemoteObject component.

Calls from a Flex client application are sent directly to the ColdFusion server as HTTP requests encrypted in Action Message Format (AMF), and responses are returned from ColdFusion to the Flex application without any intermediate proxy or additional software. As shown in Figure 26.1, when using the RemoteObject component to call CFC methods, you don't need to install or integrate LiveCycle Data Services or BlazeDS.

Flash Remoting requests and responses travel directly from the Flex application to ColdFusion and back again.

Figure 26.1. Flash Remoting requests and responses travel directly from the Flex application to ColdFusion and back again.

To call CFC functions from a Flex application, follow these steps:

  1. Install Adobe ColdFusion or obtain access to a ColdFusion server. You can use either the Standard or Enterprise edition or, if you don't have a license, you can install ColdFusion locally as the Developer edition. This edition is free for development and testing and has some limitations, including allowing connections from only two browser clients during any particular session.

  2. If using Flex Builder, optionally install the ColdFusion Extensions for Flex Builder.

  3. Create ColdFusion components on the server with code you can call from a Flex application.

  4. If using Flex Builder, create a Flex project that's integrated with your ColdFusion server installation.

  5. Create and test Flex client code to call the CFC functions.

Note

The sample code and instructions in this chapter assume that ColdFusion 8 has been installed on a Windows-based development system using the development Web server running on port 8500. If your ColdFusion installation differs, adapt the instructions as needed.

Creating a Flex project for use with ColdFusion

When you create a new Flex project, you can add project properties that allow you to easily test your Flex application with the ColdFusion server. Follow these steps to create a new Flex project:

  1. Select File → New → Flex Project from the Flex Builder menu.

  2. On the first screen, set these properties, as shown in Figure 26.2:

    • Project name: chapter26

    • Use default location: selected

    • Application type: Web application

    • Application server type: ColdFusion

    • Use remote object access service: selected

    • ColdFusion Flash Remoting: selected

  3. Click Next.

  4. On the Configure ColdFusion Server screen, set the ColdFusion installation type and location. If you installed the server configuration to the default location in Windows, use these settings:

    • ColdFusion installation type: Standalone

    • Use default location for local ColdFusion server: selected

    • Use built-in ColdFusion web server: selected

    Creating a new Flex project

    Figure 26.2. Creating a new Flex project

  5. Click Validate Configuration to verify that your ColdFusion configuration settings are accurate.

    Note

    ColdFusion must be running in order to validate the configuration at this point. Flex Builder sends a test request to the ColdFusion server's Root URL to ensure that the server is reachable.

  6. Set the Output folder to a location under the ColdFusion Root URL. This is the folder where the application's debug output files will be generated during the compilation process and from which you retrieve documents in the browser during testing. The default setting is a subfolder under the ColdFusion Web root whose name starts with the project name and ends with "-debug".

  7. Click Next.

  8. Accept the Main application filename of chapter26.mxml.

  9. Click Finish to create the project and application.

  10. Run the application.

You should see that the application is retrieved from the folder on the ColdFusion server using the server's root URL and the subfolder in which the output files are generated.

Note

If you want to use the sample applications for this chapter from the Web site, follow these instructions: Extract the contents of chapter26.zip to the new project's root folder, locate chapter26CFFiles.zip in the project files, and extract those files to the ColdFusion server's Web root folder, such as C:ColdFusion8wwwroot. This creates a new subfolder named flex3bible/chapter26 under the ColdFusion Web root.

Configuring Flash Remoting on the server

Just as with the Remoting Service on LiveCycle Data Services or BlazeDS, to use Flash Remoting from a Flex application, it must be configured on the server. When you install ColdFusion, a folder named WEB-INF/flex is created that contains a default set of configuration files.

Tip

The WEB-INF folder is located under the ColdFusion Web root, as defined during the ColdFusion installation process. The actual location differs depending on ColdFusion's configuration.

The "server" configuration that includes a limited JRun server places WEB-INF under the installation folder's wwwroot subfolder. When the server configuration of ColdFusion is installed on Windows with the default location, the Flex configuration files are stored in C:ColdFusion8wwwrootWEB-INFflex. For the J2EE or multi-server configurations, WEB-INF is located under the ColdFusion "context root" folder. For example, when installed with the default location on Windows, the Flex configuration files are stored in C:JRun4serverscfusioncfusion-earcfusion-warWEB-INFflex.

Flash Remoting access is configured in one of two files in the WEB-INF/flex folder:

  • If you install ColdFusion with the integrated LiveCycle Data Services, the primary services configuration file, services-config.xml, includes a file named remotingconfig.xml that declares Flash Remoting configuration options.

  • If you install ColdFusion without the integrated LiveCycle Data Services, a file named service-config.xml contains all Flex server configuration options, including Flash Remoting destination definitions.

ColdFusion includes a predefined Flash Remoting destination definition with an id of ColdFusion in either services-config.xml or remoting-config.xml that looks like this:

<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
        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>

These key destination properties are worth a detailed description:

  • The <channel> is set to my-cfamf. This channel is defined in services.config.xml, and uses the AMFChannel class on the server to serialize and deserialize AMF-formatted messages between client and server. The services configuration file also defines a channel named my-cfamf-secure that can be used for encrypted communications over SSL.

  • The <source> element is set to a wildcard value of *, meaning that the predefined ColdFusion destination can be used to call any CFC on the server. This is in contrast to the use of the Java-based Remoting Service with LCDS and BlazeDS, where each Java class must be configured with its own unique destination.

  • The <access> element determines which functions can be called from the Flex application. The default setting of remote means that only functions whose access attribute is set to remote can be called from Flex.

  • The settings in the <property-case> element determine whether property names are forced to lowercase as they're returned from ColdFusion to the Flex client application. Because ColdFusion is mostly case-insensitive, the names of CFC properties, structure properties, and query columns are automatically returned in uppercase. The code works fine, but it looks odd to the eye of a developer who's accustomed to object-oriented coding conventions, where property names always start with initial lowercase characters. If you want to change the default behavior, switch the value of the case properties to true to force lowercase names:

    <property-case>
      <force-cfc-lowercase>true</force-cfc-lowercase>
      <force-query-lowercase>true</force-query-lowercase>
      <force-struct-lowercase>true</force-struct-lowercase>
    </property-case>

The default ColdFusion destination is designed to be usable with its initial property settings. In most cases, you can start calling CFC functions from a Flex client application without changing any of the server-side configuration options.

Creating ColdFusion Components for Flex

The rules for creating ColdFusion components for use with a Flex application are very similar to those CFCs used as SOAP-based Web services:

  • CFCs should be placed in a folder under the Web root. With additional configuration, you also can place CFCs in folders that are mapped through the ColdFusion administrator.

  • CFC functions should have their access attribute set to remote. With additional configuration, you also can expose functions with their access set to public.

  • Functions should return values with data types that are compatible with Flex applications.

Note

A complete description of how to create and deploy ColdFusion components is beyond the scope of this chapter. For a good starting tutorial on this subject, see Ben Forta's article on the Adobe Developer Center Web site at:

www.adobe.com/devnet/coldfusion/articles/intro_cfcs.html

Table 26.1 describes the data types that can be returned from a CFC to a Flex application and how each value is translated into ActionScript variables when it's returned from a CFC function to a Flex client application.

Table 26.1. Data Conversion from ColdFusion to ActionScript

ColdFusion Data Type

ActionScript Data Type

String

String

Array

Arrya

Query

ArrayCollection

Struct

Object

CFC instance

Strongly typed value object

Date

Date

Numeric

Number

XML Object

XML Object

The returned data type is determined in a ColdFusion function by its returntype property. The CFC in Listing 26.1 shows a ColdFusion component with a helloWorld() function that declares a returntype of string:

Example 26.1. A simple ColdFusion component

<cfcomponent name="HelloService" output="false"
     hint="A ColdFusion Component for use in Flash Remoting">
  <cffunction name="helloWorld" returntype="string" access="remote">
    <cfreturn "Hello from a ColdFusion Component!"/>
  </cffunction>
</cfcomponent>

Note

The code in Listing 26.1 is available in the Web site files as HelloService.cfc in the ColdFusion files of the chapter26 project.

Tip

Unlike the worlds of Java and ActionScript, ColdFusion Markup Language is mostly case-insensitive. As a result, returntype values of string and String mean the same thing.

The returntype attribute is also used by ColdFusion to verify that data being returned by a function is of the correct type. CFML (ColdFusion Markup Language) is a very loosely typed language, where simple values are generally stored as String values until being cast appropriately at runtime (a process sometimes known as "lazy evaluation"). But at runtime, ColdFusion can detect discrepancies between a declared returntype and the actual value being returned. This function, for example, would generate a server-side runtime error, because the value being returned can't be parsed as a number:

<cffunction name="getNumber" returntype="numeric"
   access="remote">
  <cfreturn "This is not a numeric value"/>
</cffunction>

This resulting server-side error would be exposed in the Flex client application as a fault event dispatched by the RemoteObject that made the remote call to the function.

Using CFCs with the RemoteObject Component

To call a CFC function from a Flex client application, you start by creating an instance of the RemoteObject component. This is the same RPC component that's used to communicate with ColdFusion is almost exactly the same. If the source of the component is set to a wildcard in the server-side destination, you set the component's source property in the client-side RemoteObject declaration.

Setting the source property

The CFC is known to the Flex client application by its fully qualified name and location, declared with dot notation. This String value is passed to the RemoteObject component's source property to determine which component will be called on the server.

ColdFusion uses a naming pattern whereby CFCs are known by the name of the file in which the component is defined (without the file extension), prefixed with the names of the folders in which it's stored, starting at the Web root folder. Folder and component names are separated with dot characters, just like packages in Java. So, for example, a CFC that's defined in a file named MyComponent.cfc and stored in a subfolder under the ColdFusion Web root named flex3bible/cfc would be referred to from Flex as:

flex3bible.cfc.MyComponent

If you're working on a development server that has RDS, you can generate a CFC's documentation by navigating to the component from a Web browser. The documentation includes the exact string you need to set the component's source accurately in Flex. For example, you can browse to the HelloService.cfc file stored in the Web root folder's flex3bible/chapter26 folder with this URL:

http://localhost:8500/flex3bible/chapter26/HelloService.cfc

Tip

If you have RDS security turned on in the ColdFusion administrator, you'll need to enter your RDS password to view the CFC's documentation.

Figure 26.3 shows the resulting CFC documentation. The string value you use as the source attribute in Flex is displayed twice: once at the top of the documentation page and again in the hierarchy section.

Creating a RemoteObject instance

You can create an instance of the RemoteObject component that works with the ColdFusion destination in either MXML or ActionScript. In addition to the object's unique id, you set object's destination to the id of the destination on the server, named by default ColdFusion. The object's source attribute is set to the fully qualified name and location of the CFC, as described in the preceding section.

The code to create a RemoteObject and set its required properties in MXML looks like this:

<mx:RemoteObject id="helloService"
  destination="ColdFusion"
  source="flex3bible.chapter26.HelloService"/>
Automatically generated CFC documentation, including the component's fully qualified name and location

Figure 26.3. Automatically generated CFC documentation, including the component's fully qualified name and location

The code to create the same object in ActionScript looks like this:

import mx.rpc.remoting.RemoteObject;
var helloService:RemoteObject = new RemoteObject("ColdFusion");
helloService.source = "flex3bible.chapter26.HelloService";

After you've declared the RemoteObject and set its source and destination properties, you're ready to make runtime calls to remote CFC functions.

Calling CFC functions

You call CFC functions as though they were local methods of the RemoteObject. For example, the CFC in Listing 26.1 has a public method named helloWorld() that returns a simple String. As with local functions, you can call the remote method upon any application event. For example, this code calls the server-side helloWorld() method upon a Button component's click event:

<mx:Button label="Click to say hello"
  click="helloService.helloWorld()"/>

You also can call a CFC function by calling the RemoteObject component's getOperation() method to create an instance of the Operation class. The following code creates the Operation object and then calls its send() method to call the remote method:

import mx.rpc.remoting.mxml.Operation;
private function callIt():void
{
  var op:Operation = helloService.getOperation("helloWorld")
    as Operation;
  op.send();
}

This technique allows you to determine which remote method will be called at runtime, instead of having to hard code the method name.

Handling CFC Function Results

Calls to remote CFC functions are made asynchronously in Flex, so a call to a CFC function doesn't return data directly. Instead, as with the other RPC components, you handle the response with binding expressions or event handlers. Binding expressions require less code and are easy to create, while event handlers offer much more power and flexibility in how you receive, process, and save data to application memory.

Using binding expressions

A binding expression used to pass returned data to application components consists of three parts, separated with dots:

  • The RemoteObject instance's id

  • The CFC function name

  • The lastResult property

At runtime, the method is created as an Operation object that's a member of the RemoteObject instance. The Operation object's lastResult property is populated with data when it's received from the server.

The lastResult property is explicitly typed as an ActionScript Object, but at runtime its native type is determined by the type of data that's returned from the server. A String returned from ColdFusion is translated into an ActionScript String value, so a binding expression that handles the value returned from the simple helloWorld() method can be used to pass the returned value to a Label or other text display control.

The application in Listing 26.2 calls the remote helloWorld() CFC function and displays its returned data in a Label control with a binding expression in its text property.

Example 26.2. Handling returned data with a binding expression

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml"
  backgroundColor="#EEEEEE">

  <mx:RemoteObject id="helloService"
    destination="ColdFusion"
    source="flex3bible.chapter26.HelloService"/>

  <mx:Button label="Hello World" click="helloService.helloWorld()"/>

  <mx:Label text="{helloService.helloWorld.lastResult}"
    fontSize="12"/>

</mx:Application>

Note

The code in Listing 26.2 is available in the Web site files as ROWithBinding.mxml in the src folder of the chapter26 project.

Using the result event

As with other RPC components, you can handle results of a call to a CFC function with the RemoteObject component's result event. This event dispatches an event object typed as mx.rpc.events.ResultEvent, the same event object that's used by the other RPC components HTTPService and RemoteObject. The event object's result property references the returned data.

To handle and save data using the result event, follow these steps:

  1. Declare a bindable variable outside of any functions that acts as a persistent reference to the returned data. Cast the variable's type depending on what you expect to be returned by the remote method. For example, if the data returned by the CFC function is typed as a Query, the RemoteObject component casts the returned data as an ArrayCollection. This code declares a bindable ArrayCollection variable:

    import mx.collections.ArrayCollection;
    [Bindable]
    private var myData:ArrayCollection
  2. Create an event handler function that will be called when the event is dispatched. The function should receive a single event argument typed as ResultEvent and return void:

    private function resultHandler(event:ResultEvent):void
    {
    }
  3. Within the event handler function, use the event.result expression to refer to the data that's returned from the server. Just as with the WebService component, ResultEvent.result is typed as an Object. Because the expression's native type differs depending on what's returned by the CFC function, you typically have to explicitly cast the returned data. This code expects the CFC function to return an ArrayCollection:

    myData = event.result as ArrayCollection;

You can listen for the result event with either an MXML attribute-based event listener or a call to the ActionScript addEventListener() method. The attribute-based event listener looks like this:

<mx:RemoteObject id="myService" destination="helloClass"
  source="flex3bible.chapter26.ContactService"
  result="resultHandler(event)"/>

When using addEventListener() to create an event listener, you can designate the event name with the String value result, or with the ResultEvent class's RESULT static constant:

var contactService:RemoteObject = new RemoteObject("ColdFusion");
contactService.source =
   source="flex3bible.chapter26.ContactService";
contactService.addEventListener(ResultEvent.RESULT,
   resultHandler);
contactService.getAllContacts();

Listing 26.3 uses a result event handler function to capture and present data that's been returned from a CFC function.

Example 26.3. Handling results from a CFC function with a result event handler

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml"
  backgroundColor="#EEEEEE" creationComplete="initApp()"
  xmlns:view="view.*">
  <mx:Script>
    <![CDATA[
      import mx.collections.ArrayCollection;
      import mx.rpc.events.ResultEvent;
      import mx.rpc.remoting.RemoteObject;
      [Bindable]
      private var contactData:ArrayCollection;
      private var contactService:RemoteObject;
      private function initApp():void
      {
contactService = new RemoteObject("ColdFusion");
        contactService.source = "flex3bible.chapter26.ContactService";
        contactService.addEventListener(ResultEvent.RESULT,
   resultHandler);
      }
      private function resultHandler(event:ResultEvent):void
      {
       contactData = event.result as ArrayCollection;
      }
    ]]>
  </mx:Script>
  <mx:Button label="Hello World"
   click="contactService.getAllContacts()"/>
  <view:ContactsGrid dataProvider="{contactData}"/>
</mx:Application>

Note

The code in Listing 26.3 is available in the Web site files as ROResultHandler.mxml in the src folder of the chapter26 project. The custom DataGrid component that displays data in this and other applications in this chapter is defined as a custom MXML component named ContactsGrid.mxml in the project's src/view folder.

Listing 26.4 shows the code for the CFC that's called by this and other applications in this section. Notice that the Query object is created manually in ColdFusion code, rather than being generated with a <cfquery> command. As a result, the case of the column names is controlled by this call to the ColdFusion QueryNew() function, rather than being derived from a database query's metadata:

<cfset var
   qContacts=queryNew('contactId,firstname,lastname,city')>

Example 26.4. A CFC returning a Query object

<cfcomponent name="ContactService"
  hint="Delivers Contact data from an XML file to a Flex application">
  <cfapplication name="flex3BibleChapter26" sessionManagement="true">
  <!--- Initialize data set in memory if it doesn't already exist --->
  <cfif not structKeyExists(session, "qContacts")>
    <cfset createDataSet()>
  </cfif>

  <!--- Returns all data from a query object --->
  <cffunction name="getAllContacts" returntype="query" access="remote">
    <cfreturn session.qContacts>
  </cffunction>
  <!--- Returns filtered data using query of query --->
<cffunction name="getFilteredContacts" returnType="query"
 access="remote">
  <cfargument name="firstname" type="string" required="true">
  <cfargument name="lastname" type="string" required="true">
  <cfset var qFiltered="">
  <cfquery dbtype="query" name="qFiltered">
    SELECT * FROM session.qContacts
    WHERE 0=0
  <cfif len(trim(firstname))>
    AND firstname LIKE '%#trim(arguments.firstname)#%'
  </cfif>
  <cfif len(trim(lastname))>
    AND lastname LIKE '%#trim(arguments.lastname)#%'
  </cfif>
 </cfquery>
 <cfreturn qFiltered>
</cffunction>
<!--- Returns the total count of Contacts --->
<cffunction name="getContactCount" returntype="numeric"
 access="remote">
  <cfreturn session.qContacts.recordCount>
</cffunction>
<!--- Called to create a query object from an XML file --->
<cffunction name="createDataSet" returntype="void" access="private">
  <cfset var strContacts="">
  <cfset var xContacts="">
  <cfset var i="">
  <cfset var qContacts=queryNew('contactId,firstname,lastname,city')>
  <cffile action="read" file="#expandPath('data/contacts.xml')#"
    variable="strContacts">
  <cfset xContacts=xmlParse(strContacts)>
  <cfloop from="1" to="#arrayLen(xContacts.contacts.row)#" index="i">
    <cfset QueryAddRow(qContacts)>
    <cfset qContacts.firstname[i]=xContacts.contacts.row[i]
      .firstname.xmltext>
    <cfset qContacts.lastname[i]=xContacts.contacts.row[i].</p>
      lastname.xmltext>
    <cfset qContacts.contactId[i]=xContacts.contacts.row[i].</p>
      contactId.xmltext>
    <cfset qContacts.city[i]=xContacts.contacts.row[i].city.xmltext>
  </cfloop>
  <cfset session.qContacts=qContacts>
 </cffunction>
</cfcomponent>

Note

The code in Listing 26.4 is available in the Web site files as ContactService.cfc in the ColdFusion files of the chapter26 project.

Handling results from multiple CFC functions

When you need to call more than one function from a CFC, you have to distinguish which event handler function should be called for each of them. You do this in MXML with the <mx:method > compiler tag, which is nested within a <mx:RemoteObject> tag set. Each <mx:method > tag represents a CFC function and can declare its own distinct result and fault event handlers.

The CFC in Listing 26.4, for example, has a function named getContactCount() that returns a numeric value and a function named getAllContacts() that returns a Query object (translated to an ArrayCollection in the Flex application). To handle each function's result event with its own distinct event handler function, you create the functions and then declare the <mx:method> tags as follows:

<mx:RemoteObject id="contactService"
  destination="ColdFusion"
  source="flex3bible.chapter26.ContactService">
  <mx:method name="getContactCount"
   result="countHandler(event)"/>
  <mx:method name="getAllContacts" result="dataHandler(event)"/>
</mx:RemoteObject>

The application in Listing 26.5 calls getContactCount() upon application startup to inform the user how many data items are available on the server. The call to getAllContacts() to actually retrieve the data is made only when the user clicks Get Contact Data.

Example 26.5. Handling result events from multiple CFC functions

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml"
  xmlns:view="view.*" backgroundColor="#EEEEEE"
  creationComplete="contactService.getContactCount()">
  <mx:Script>
    <![CDATA[
      import mx.collections.ArrayCollection;
      import mx.rpc.events.ResultEvent;
      [Bindable]
      private var recordCount:Number;
      [Bindable]
      private var contactData:ArrayCollection;
      private function countHandler(event:ResultEvent):void
      {
        recordCount = event.result as Number;
      }
      private function dataHandler(event:ResultEvent):void
      {
        contactData = event.result as ArrayCollection;
}
    ]]>
  </mx:Script>
  <mx:RemoteObject id="contactService"
    destination="ColdFusion"
    source="flex3bible.chapter26.ContactService">
    <mx:method name="getContactCount" result="countHandler(event)"/>
    <mx:method name="getAllContacts" result="dataHandler(event)"/>
  </mx:RemoteObject>
  <mx:Label
    text="There are {recordCount} Contacts available on the server"/>
  <mx:Button label="Get Contact Data"
    click="contactService.getAllContacts()"/>
  <view:ContactsGrid dataProvider="{contactData}"/>
</mx:Application>

Note

The code in Listing 26.5 is available in the Web site files as ROMultipleFunctions.mxml in the src folder of the chapter26 project.

Figure 26.4 shows the resulting application. The Label at the top of the application displays the getContactCount() results immediately upon application startup, while the actual data is displayed only when the user requests it from the server.

An application using two different functions of a single CFC

Figure 26.4. An application using two different functions of a single CFC

Tip

As with other RPC components, you can also handle result and fault events using the ItemResponder and AsyncToken classes. For a description of this pattern and sample code, see Chapter 21.

Passing Arguments to CFC Functions

You can pass arguments to CFC functions in three different ways:

  • Explicit arguments are passed in the order in which they're declared in the CFC function

  • Bound arguments are declared with MXML code and bound to data sources in the Flex application

  • Named arguments are wrapped in an ActionScript Object and are passed as though the CFC function expects only a single argument.

Using explicit arguments

Explicit arguments are passed in the same order in which they're declared in the CFC function. The CFC's getFilteredContacts() function declares two required arguments:

<cffunction name="getFilteredContacts" returnType="query"
    access="remote">
  <cfargument name="firstname" type="string" required="true">
  <cfargument name="lastname" type="string" required="true">
  ... function body ...
</cffunction>

Using explicit arguments, you pass values that match the expected data types. This code sends the data in the same order in which they're declared:

contactService.getFilteredContacts(fnameInput.text,
   lnameInput.text);

Tip

It's also possible to pass arguments explicitly with the Operation class's send() method. The following syntax allows you to pass remote CFC function names as strings or variables, passed into the RemoteObject component's getOperation() method:

contactService.getOperation("getFilteredContacts").send(
  fnameInput.text, lnameInput.text);

This calling syntax also works with the WebService component.

Using bound arguments

Bound arguments are declared in MXML and then passed through a call to the RemoteObject component's send() method. Just as when using the RemoteObject component to call Java-based methods in classes hosted by BlazeDS, you use bound argument notation with XML elements for each argument wrapped in an <mx:arguments> tag set. This code binds the getFilteredContacts() method's two arguments to values gathered from TextInput controls:

<mx:RemoteObject id="contactService" destination="ColdFusion"
  source="flex3bible.chapter26.ContactService">
  <mx:method name="getFilteredContacts">
    <mx:arguments>
      <firstname>{fnameInput.text}</firstname >
      <lastname>{lnameInput.text}</lastname>
    </mx:arguments>
  </mx:method>
</mx:RemoteObject>

To call the method with the bound arguments, call the operation's send() method without any explicit arguments:

contactService.getFilteredContacts.send()

Tip

When using bound arguments with CFC functions, the arguments are matched by name, not by the order of declaration in the client-side code. This behavior is different from Java-based methods and arguments, where bound arguments are sent in the order in which they're declared, and the XML element names are ignored.

Using named arguments

You can match CFC function arguments by name by wrapping them in an ActionScript Object. Each property has a name and a value; the name of the Object property must match the name of the CFC function argument.

This code creates an Object and attaches argument values by name:

var args:Object = new Object();
args.firstname = fnameInput.text;
args.lastname = lnameInput.text;
contactService.getFilteredContacts(args);

You also can write the same code using shorthand Object notation:

contactService.getFilteredContacts(
  {firstname:fnameInput.text, lastname:lnameInput.text});

This behavior is sometimes confusing to ColdFusion developers who first encounter it, because it means that you can't pass an anonymous Object as an argument to a CFC function and expect it to arrive intact as a ColdFusion structure. The Object is always broken down into its named properties, and then the individual properties are passed to the CFC function. If you need to pass a structure argument to a CFC function, you have to wrap it in another Object and pass it by name:

myService.myFunction({myArgumentName:myObject});

The named arguments behavior in Flex is very similar to ColdFusion's own argumentCollection attribute, which can be used to pass a structure of named arguments to functions from CFML code. This CFML code calls a CFC function and passes a structure in very much the same way:

<cfset stArgs=structNew()>
<cfset stArgs.firstname="firstnameValue">
<cfset stArgs.lastname="lastnameValue">
<cfinvoke component="flex3bible.chapter26.ContactService"
  argumentCollection="#stArgs#" returnVariable="qContacts"/>

The application in Listing 26.6 uses named arguments wrapped in an ActionScript Object.

Example 26.6. An application passing named arguments to a CFC function

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml"
  backgroundColor="#EEEEEE" xmlns:view="view.*">
  <mx:Script>
    <![CDATA[
      private function getContacts():void
      {
        var args:Object = new Object();
        args.firstname = fnameInput.text;
         args.lastname = lnameInput.text;
contactService.getFilteredContacts(args);
}
]]>
</mx:Script>
<mx:RemoteObject id="contactService" destination="ColdFusion"
     source="flex3bible.chapter26.ContactService"/>
<mx:Form>
     <mx:FormItem label="First Name:">
         <mx:TextInput id="fnameInput"/>
     </mx:FormItem>
     <mx:FormItem label="Last Name:">
       <mx:TextInput id="lnameInput"/>
       <mx:Button label="Get Filtered Data" click="getContacts()"/>
    </mx:FormItem>
  </mx:Form>
<view:ContactsGrid
     dataProvider="{contactService.getFilteredContacts.lastResult}"/>
</mx:Application>

Note

The code in Listing 26.6 is available in the Web site files as RONamedArgs.mxml in the src folder of the chapter26 project. Examples of the same application using explicit and bound arguments are in ROExplicitArgs.mxml and ROBoundArgs.mxml, respectively.

Using Value Object Classes

When passing data between a Flex client application and ColdFusion, you can build both clientside and server-side data objects using the Value Object design pattern. The Flex version is built as an ActionScript class, while the ColdFusion version is built as a CFC. At runtime, you can pass strongly typed value objects between the client and server tiers of your application, and the Flex application and the Flash Remoting gateway automatically transfer data between the objects based on a mapping that you provide in the code.

Tip

The Value Object design pattern is also known in various industry documentation sources as the Transfer Object and Data Transfer Object pattern. The different names are all used to refer to the same pattern: a class that contains data for a single instance of a data entity.

Creating a ColdFusion value object

The ColdFusion version of a value object is written as a simple CFC. The <cfcomponent> start tag requires an alias attribute that's set to the fully qualified name and location of the CFC:

<cfcomponent output="false" alias="flex3Bible.chapter26.Contact">
     ... component body ...
</cfcomponent>

Each named property is declared after the <cfcomponent> start tag using the <cfproperty> tag. Each property has a name and a type to indicate how it will be exchanged with Flex. The following <cfproperty> tags declare one numeric and two string properties:

<cfproperty name="contactId" type="numeric" default="0">
<cfproperty name="firstname" type="string" default="">
<cfproperty name="lastname" type="string" default="">

Tip

The <cfproperty> tag, which in conventional ColdFusion code is used only to generate CFC documentation, controls the name and case of the property name when it's exchanged with the Flex application at runtime. This overrides the settings in the configuration files that control the case of property names.

The <cfproperty> tag's default attribute is used to generate CFC documentation and doesn't actually set default values when the CFC is instantiated in ColdFusion. Instead, you typically add code outside any function definitions that set the properties' default values upon instantiation:

<cfscript>

     this.contactId = 0;
     this.firstname = "";
     this.lastname = "";
</cfscript>

The CFC in Listing 26.7 is a value object that declares four properties of a Contact value object and sets their initial values upon instantiation.

Example 26.7. A simple value object CFC

<cfcomponent output="false" alias="flex3Bible.chapter26.Contact">
     <cfproperty name="contactId" type="numeric" default="0">
     <cfproperty name="firstname" type="string" default="">
     <cfproperty name="lastname" type="string" default="">
     <cfproperty name="city" type="string" default="">
     <cfscript>
          this.contactId = 0;
          this.firstname = "";
          this.lastname = "";
          this.city = ""
   </cfscript>
</cfcomponent>

Note

The code in Listing 26.7 is available in the Web site files as Contact.cfm in the ColdFusion files of the chapter26 project.

Creating an ActionScript value object

The Flex client application uses an ActionScript version of the value object built as an ActionScript class. The class requires a [RemoteClass] metadata tag with an alias attribute that describes the fully qualified name and location of the matching CFC:

[RemoteClass(alias="flex3Bible.chapter26.Contact")]

This is a two-way mapping: When an ActionScript version of the object is sent to ColdFusion, the Flash Remoting gateway creates an instance of the CFC and passes the received object's property values to the server-side version. Similarly, if a CFC function returns instances of the server-side version, client-side versions are created automatically and their property values set to the values received from the server.

Caution

The alias attributes of the [RemoteClass] metadata tag on the client and the <cfcomponent> tag on the server must match exactly and are case-sensitive.

The ActionScript class in Listing 26.8 declares the same set of values as public properties and maps itself to the server's version with the [RemoteClass] metadata tag.

Example 26.8. An ActionScript value object class

package vo
{
  [Bindable]
  [RemoteClass(alias="flex3Bible.chapter26.Contact")]
  public class Contact
  {
    public var contactId:int;
    public var firstname:String;
    public var lastname:String;
    public var city:String;
    public function Contact()
    {
    }
  }
}

Note

The code in Listing 26.8 is available in the Web site files as Contact.as in the src/vo folder in the chapter26 project.

Returning value objects from ColdFusion to Flex

After you've built versions of the value object in both ColdFusion and ActionScript and provided the appropriate mappings, a CFC function has the ability to return either individual value object instances or collections of value objects wrapped into arrays. This CFC function creates an instance of the Contact value object and returns it to Flex:

<cffunction name="getContactVO" access="remote"
     returntype="flex3Bible.chapter26.Contact">
     <cfset contact=createObject("component",
          "flex3Bible.chapter26.Contact")>
     <cfset contact.contactId = 1 >
     <cfset contact.firstname = "David">
     <cfset contact.lastname = "Gassner">
     <cfset contact.city = "Seattle">
     <cfreturn contact>
</cffunction>

Notice that the function's returntype attribute is set to the fully qualified name and location of the value object CFC.

The CFC in Listing 26.9 extends the original CFC and uses its data, but transforms the query object into an array of strongly typed value objects before returning it to Flex. Notice that this time the CFC function's returntype attribute is set to an array of CFC instances, rather than the usual generic array notation.

Example 26.9. A CFC function returning an array of value objects

<cfcomponent name="ContactServiceWithVO"
    hint="Delivers Contact data as an array of value objects"
    extends="ContactService">
    <cfapplication name="flex3BibleChapter26"
                                          sessionManagement="true">
    <cfif not structKeyExists(session, "qContacts")>
    <cfset createDataSet()>
    </cfif>
    <!--- Returns all data as an array of structures --->
    <cffunction name="getContactsAsArray" access="remote"
    returntype="flex3Bible.chapter26.Contact[]">
    <cfset var contact="">
    <cfset var arReturn=arrayNew(1)>
    <cfloop query="session.qContacts">
      <cfset contact=createObject("component",
         "flex3Bible.chapter26.Contact")>
    <cfset contact.contactId = session.qContacts.contactId>
    <cfset contact.firstname = session.qContacts.firstname>
    <cfset contact.lastname = session.qContacts.lastname>
    <cfset contact.city = session.qContacts.city>
    <cfset arrayAppend(arReturn, contact)>
     </cfloop>
     <cfreturn arReturn>
   </cffunction>
</cfcomponent>

Note

The code in Listing 26.9 is available in the Web site files as ContactServiceWithVO.cfc in the ColdFusion files of the chapter26 project.

Receiving value objects from ColdFusion

In order to receive value objects from a CFC function and have them automatically transformed into instances of the ActionScript value object, the Flex application must contain at least one reference to the ActionScript class. The reference can be a declared instance of the class or a call to any static properties or methods. This ensures that the class, which contains the [RemoteClass] metadata tag, is compiled into the application and is available at runtime.

Caution

It isn't enough to just import the value object class in the Flex application; you have to declare at least one instance. The purpose of importing a class is to inform the Flex compiler of the class's existence, but the compiler doesn't include the class definition in the binary version of the application unless at least one instance of the class is declared, or there's a reference to one of its static members.

The Flex application in Listing 26.10 receives the Array of value objects and processes them in an event handler. Notice the MXML <vo:Contact/> declaration that ensures the mapping of the value object classes is included in the compiled application.

Tip

When a CFC function returns a ColdFusion array, it's received in Flex as an ActionScript Array, not an ArrayCollection. In this application, the result event handler expects an Array and assigns it to the source of the already-instantiated ArrayCollection:

contactData.source = event.result as Array;

Example 26.10. Receiving an Array of strongly typed value objects

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml"
    backgroundColor="#EEEEEE" creationComplete="initApp()"
    xmlns:view="view.*" xmlns:vo="vo.*">
    <mx:Script>
    <![CDATA[
    import mx.controls.Alert;
    import mx.collections.ArrayCollection;
    import mx.rpc.events.ResultEvent;
    import mx.rpc.remoting.RemoteObject;
    [Bindable]
    private var contactData:ArrayCollection = new ArrayCollection();
    private var contactService:RemoteObject;
    private function initApp():void
    {
    contactService = new RemoteObject("ColdFusion");
    contactService.source =
      "flex3bible.chapter26.ContactServiceWithVO";
    contactService.addEventListener(ResultEvent.RESULT,
      resultHandler);
    }
    private function resultHandler(event:ResultEvent):void
    {
    contactData.source = event.result as Array;
    }
    ]]>
    </mx:Script>
    <vo:Contact/>
    <mx:Button label="Get Contacts"
    click="contactService.getContactsAsArray()"/>
    <view:ContactsGrid dataProvider="{contactData}"/>
</mx:Application>

Note

The code in Listing 26.10 is available in the Web site files as ROReceiveValueObjects.mxml in the src folder of the chapter26 project.

Passing value object arguments to CFC functions

Value objects also can be passed from a Flex client application to a CFC function. The CFC function should have declared an argument typed as the ColdFusion version of the value object. This CFC function receives an instance of the Contact value object CFC and returns a concatenated string built from its properties:

<cffunction name="parseContact" access="remote">'
    <cfargument name="contactVO"
      type="flex3Bible.chapter26.Contact"
    required="true">
    <cfreturn "Contact received: " + contactVO.firstname +
    " " + contactVO.lastname>
</cffunction>

To pass a value object argument to the CFC function from Flex, create an instance of the ActionScript version of the object and set its properties. Then pass the value object to the function using any of the argument-passing strategies described previously.

The application in Listing 26.11 passes an instance of the ActionScript value object to a CFC function that extracts its properties and returns a concatenated string.

Example 26.11. Passing a value object to a CFC function

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml"
    backgroundColor="#EEEEEE">
    <mx:Script>
    <![CDATA[
      import vo.Contact;
      private function passArgument():void
      {
        var newContact:Contact = new Contact();
        newContact.firstname = fnameInput.text;
        newContact.lastname = lnameInput.text;
        contactService.parseContact(newContact);
      }
    ]]>
    </mx:Script>
    <mx:RemoteObject id="contactService" destination="ColdFusion"
    source="flex3bible.chapter26.ContactServiceWithVO"/>
    <mx:Panel title="Enter Contact Information">
<mx:Form>
      <mx:FormItem label="First Name:">
        <mx:TextInput id="fnameInput"/>
      </mx:FormItem>
      <mx:FormItem label="Last Name:">
        <mx:TextInput id="lnameInput"/>
        <mx:Button label="Pass Argument" click="passArgument()"/>
      </mx:FormItem>
    </mx:Form>
    </mx:Panel>
    <mx:Label text="{contactService.parseContact.lastResult}"
    fontSize="12"/>
</mx:Application>

Note

The code in Listing 26.11 is available in the Web site files as ROPassVOArg.mxml in the src folder of the chapter26 project.

Working with RemoteObject Faults

When an exception occurs during a call to a CFC function, the RemoteObject dispatches a fault event. The event object is typed as mx.rpc.events.FaultEvent and contains a fault property typed as mx.rpc.Fault. This object in turn has String properties named faultCode, faultString, and faultDetail. The values of these properties differ depending on the nature of the error, and in the case of faultDetail they sometimes don't contain useful information.

Handling the fault event

As with all events, you can create an event listener with either MXML or ActionScript code. The MXML attribute-based event listener looks like this:

<mx:RemoteObject id="contactService" destination="ColdFusion"
       source="flex3bible.chapter26.ContactServiceWithVO"
       fault="faultHandler(event)"/>

To create an event listener in ActionScript code, call the RemoteObject component's addEventListener() method and declare the event name using the FaultEvent.FAULT constant:

faultService.addEventListener(FaultEvent.FAULT, faultHandler);

When the event handler function receives the event object, you can handle it in any way you like. Minimally, you might display the fault information to the user with a pop-up dialog box generated by the Alert class:

private function faultHandler(event:FaultEvent):void
     {
       Alert.show(event.fault.faultString, event.fault.faultCode);
}

Tip

In ColdFusion 8, the value of the error message always has a prefix of "Unable to invoke CFC -". If you don't want to display this prefix, you have to parse the original error message from the value of the faultString property:

var errorMessage:String = event.fault.faultString;
errorMessage = errorMessage.substring(22,
      errorMessage.length);
Alert.show(errorMessage, event.fault.faultCode);

Generating custom exceptions from a CFC function

On the server, you can generate your own faults from a CFC function by calling the ColdFusion <cfthrow> command. These <cfthrow> commands' attributes are exposed in the Flex application's <FaultEvent.fault> object:

  • The message attribute appears in the fault object's faultString property.

  • The errorcode attribute appears in the fault object's faultCode property.

The CFC in Listing 26.12 implements a throwCFCFault() function that always generates a fault with message and errorcode attributes.

Example 26.12. A CFC function generating a server-side fault

<cfcomponent output="false">
    <cffunction name="throwCFCFault" returntype="String">
    <cfthrow message="An error message generated by a CFC function"
      errorcode="CFC Function Error">
    <cfreturn "A String">
    </cffunction>
</cfcomponent>

Note

The code in Listing 26.12 is available in the Web site files as FaultService.cfc in the ColdFusion files of the chapter26 project.

The Flex application in Listing 26.13 calls the CFC function to intentionally generate a fault and display its information in an Alert pop-up dialog box.

Example 26.13. A Flex application handling a server-side fault from a CFC function

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml"
    backgroundColor="#EEEEEE">
    <mx:Script>
    <![CDATA[
      import mx.controls.Alert;
      import mx.rpc.events.FaultEvent;
      import mx.rpc.events.ResultEvent;
      import mx.rpc.remoting.RemoteObject;
      [Bindable]
      private var returnString:String;
      private function resultHandler(event:ResultEvent):void
      {
       returnString = event.result as String;
      }
      private function faultHandler(event:FaultEvent):void
      {
        var errorMessage:String = event.fault.faultString;
        errorMessage = errorMessage.substring(22, errorMessage.length);
        Alert.show(errorMessage, event.fault.faultCode);      }
    ]]>
    </mx:Script>
    <mx:RemoteObject id="faultService" destination="ColdFusion"
    source="flex3bible.chapter26.FaultService"
    result="resultHandler(event)"
    fault="faultHandler(event)"/>
    <mx:Button label="Generate Fault"
      click="faultService.throwCFCFault()"/>
    <mx:Label text="{returnString}"/>
</mx:Application>

Note

The code in Listing 26.13 is available in the Web site files as ROFaultHandler.mxml in the src folder of the chapter26 project.

Summary

In this chapter, I described how to integrate Flex client applications with Adobe ColdFusion 8 using Flash Remoting and the Flex framework's RemoteObject component. You learned the following:

  • Flash Remoting was originally introduced with ColdFusion MX and was adapted for use in LiveCycle Data Services and Blaze as the Remoting Service.

  • Flash Remoting allows you to call functions of ColdFusion components (CFCs) from a ColdFusion server.

  • Remote function calls and responses are encrypted in AMF, a binary message format that's significantly smaller and faster than XML.

  • Data can be exchanged between the Flex client and a CFC function based on documented data type mappings.

  • Calls to CFC functions are asynchronous.

  • CFC function results can be handled with binding expressions or by handling the RemoteObject component's result event.

  • Arguments can be passed to CFC functions using explicit, named, or bound argument syntax.

  • Strongly typed value objects can be created in both ActionScript and ColdFusion and exchanged automatically between client and server at runtime.

  • Exceptions are handled as Flex application faults using the RemoteObject component's fault event.

  • Custom exceptions can be generated in ColdFusion and handled in a Flex client application.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.128.198.60