Requirements
     
User level
     
All      
       

 

The Java EE Platform is the leading enterprise web server. The Adobe Flash Platform is the leader in the rich Internet application space. Using both, developers can deliver compelling, data-centric applications that leverage the benefits of an enterprise back-end solution and a great user experience.

In this article, you learn about the architecture of applications built using Flex and Java including:

  • An overview of the client/server architecture.
  • The different ways the client and server can communicate.
  • An introduction to Flash Remoting and why and how you use it.
  • How to integrate a Flex application with your security framework.
  • An overview of how to build Flex applications using events, states, MXML components, and modules.
  • An introduction to developing a Flex application with real-time server data push.
  • How to boost productivity developing data-intensive applications using the Data Management service in LiveCycle Data Services.
  • An overview of model driven development using Flash Builder and LiveCycle Data Services to generate client and server-side code.
  • How to deploy a Flex application on a portal server.

Be sure to also watch the video Introduction to Flex 4 and Java integration.

To learn more about the technologies used to build these applications, read The technologies for building Flex and Java applications article.

 

Client/server architecture

 

Flex and Java applications use a multi-tier architecture where the presentation tier is the Flex application, the business or application tier is the Java EE server and code, and the data tier is the database. You can write the back-end code just as you normally would for a Java application, modeling your objects, defining your database, using an object-relational framework such as Hibernate or EJB 3, and writing the business logic to query and manipulate these objects. The business tier must be exposed for access via HTTP from the Flex application and will be used to move the data between the presentation and data tiers.

Typical HTML applications consist of multiple pages and as a user navigates between them, the application data must be passed along so the application itself (the collection of pages and functionality it consists of) can maintain state. In contrast, Flex applications, by nature, are stateful. A Flex application is embedded in a single HTML page that the user does not leave and is rendered by Flash Player. The Flex application can dynamically change views and send and retrieve data asynchronously to the server in the background, updating but never leaving the single application interface (see Figure 1) (similar to the functionality provided by the XMLHttpRequest API with JavaScript.)

 

The client/server architecture.
Figure 1. The client/server architecture.
 
Client/server communication

 

Flex applications can communicate with back-end servers using either direct socket connections or more commonly, through HTTP. The Flex framework has three remote procedure call APIs that communicate with a server over HTTP: HTTPService, WebService, and RemoteObject. All three wrap Flash Player's HTTP connectivity, which in turn, uses the browser's HTTP library. Flex applications cannot connect directly to a remote database.

You use HTTPService to make HTTP requests to JSP or XML files, to RESTful web services, or to other server files that return text over HTTP. You specify the endpoint URL, listener functions (the callback functions to be invoked when the HTTPService request returns a successful or unsuccessful response), and a data type for the returned data (what type of data structure it should be translated into once received in the Flex application). You can specify the data to be handled as raw text and assigned to a String variable or converted to XML, E4X, or plain old ActionScript objects. If you get back JSON, you can use the Adobe Flex corelib package of classes to deserialize the JSON objects into ActionScript objects. To make calls to SOAP based web services, you can use the HTTPService API or the more specialized WebService API, which automatically handles the serialization and deserialization of SOAP formatted text to ActionScript data types and vice versa.

The third option for making remote procedure calls is to use the RemoteObject API. It makes a Flash Remoting request to a method of a server-side Java class that returns binary Action Message Format over HTTP. When possible, use Flash Remoting whose binary data transfer format enables applications to load data up to 10 times faster than with the more verbose, text-based formats such as XML, JSON, or SOAP (see Figure 2). To see a comparison of AMF to other text-based serialization technologies, see James Ward's Census RIA Benchmark application.

 

Methods for connecting Flex and Java.
Figure 2. Methods for connecting Flex and Java.

 

Flash Remoting

 

Flash Remoting is a combination of client and server-side functionality that together provides a call-and-response model for accessing server-side objects from Flash Platform applications as if they were local objects. It provides transparent data transfer between ActionScript and server-side data types, handling the serialization into Action Message Format (AMF), deserialization, and data marshaling between the client and the server.

Flash Remoting uses client-side functionality built in to Flash Player and server-side functionality that is built in to some servers (like ColdFusion and Zend) but must be installed on other servers (as BlazeDS or LiveCycle Data Services on Java EE servers, WebORB or FluorineFX on .NET servers, the Zend framework or amfphp on PHP servers, and more). See the technologies for building Flex and Java applications article for more details about BlazeDS and LiveCycle Data Services.

BlazeDS and LiveCycle Data Services use a message-based framework to send data back and forth between the client and server. They provide Remoting, Proxying, and Messaging services, and for LiveCycle, an additional Data Management service. The Flex application sends a request to the server and the request is routed to an endpoint on the server. From the endpoint, the request is passed to the MessageBroker, the BlazeDS and LiveCycle Data Services engine that handles all the requests and routes them through a chain of Java objects to the destination, the Java class with the method to invoke (see Figure 3).

Flash Remoting architecture.
Figure 3. Flash Remoting architecture.

 

AMF

 

AMF is a binary format used to serialize ActionScript objects and facilitate data exchange between Flash Platform applications and remote services over the Internet. Adobe publishes this protocol; the latest is AMF 3 Specification for ActionScript 3. You can find tables listing the data type mappings when converting from ActionScript to Java and Java to ActionScript here.

For custom or strongly typed objects, public properties (including those defined with get and set methods) are serialized and sent from the Flex application to the server or from the server to the Flex application as properties of a general 0bject. To enable mapping between the corresponding client and server-side objects, you use the same property names in the Java and ActionScript classes and then in the ActionScript class, you use the [RemoteClass] metadata tag to create an ActionScript object that maps directly to the Java object.

Here is an example Employee ActionScript class that maps to a server-side Employee Java DTO located in the services package on the server.

 

package valueobjects.Employee{ [Bindable] [RemoteClass(alias="services.Employee")] public class Employee { public var id:int; public var firstName:String; public var lastName:String; (...) } }

 

Installing BlazeDS or LiveCycle Data Services

 

To use Flash Remoting with BlazeDS or LiveCycle Data Services, you need to install and configure the necessary server-side files. For BlazeDS, you can download it as a WAR file which you deploy as a web application or as a turnkey solution. The turnkey download contains a ready-to-use version of Tomcat in which the the BlazeDS WAR file has already been deployed and configured along with a variety of sample applications. Similarly, for LiveCycle Data Services, the installer lets you choose to install LiveCycle with an integrated Tomcat server or as a LiveCycle Data Services web application.

In either scenario a web application called blazeds or lcds (usually appended by a version number) is created. You can modify and build out this application with your Java code, or more typically, you can copy the JAR files and configuration files the blazeds or lcds web application contains and add them to an existing Java web application on the server (see Figure 4).

 

The required BlazeDS or LiveCycle Data Services files.
Figure 4. The required BlazeDS or LiveCycle Data Services files.

 

Modifying web.xml

 

If copying the files to a different web application, you also need to modify the web.xml file to define a session listener for HttpFlexSession and a servlet mapping for MessageBroker, which handles all the requests and passes them off to the correct server-side Java endpoints. You can copy and paste these from the original blazeds or lcds web application web.xml file.

 

<!-- Http Flex Session attribute and binding listener support --> <listener> <listener-class>flex.messaging.HttpFlexSession</listener-class> </listener> <!-- MessageBroker Servlet --> <servlet> <servlet-name>MessageBrokerServlet</servlet-name> <display-name>MessageBrokerServlet</display-name> <servlet-class>flex.messaging.MessageBrokerServlet</servlet-class> <init-param> <param-name>services.configuration.file</param-name> <param-value>/WEB-INF/flex/services-config.xml</param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>MessageBrokerServlet</servlet-name> <url-pattern>/messagebroker/*</url-pattern> </servlet-mapping>

 

Optionally, you may also want to copy and paste (and uncomment) the mapping for RDSDispatchServlet, which is used for RDS (Remote Data Service) access with the data service creation feature in Flash Builder 4 that introspects a server-side service and generates corresponding client-side code. See the model driven development section for more details.

 

<servlet> <servlet-name>RDSDispatchServlet</servlet-name> <display-name>RDSDispatchServlet</display-name> <servlet-class>flex.rds.server.servlet.FrontEndServlet</servlet-class> <init-param> <param-name>useAppserverSecurity</param-name> <param-value>false</param-value> </init-param> <load-on-startup>10</load-on-startup> </servlet> <servlet-mapping id="RDS_DISPATCH_MAPPING"> <servlet-name>RDSDispatchServlet</servlet-name> <url-pattern>/CFIDE/main/ide.cfm</url-pattern> </servlet-mapping>

 

Reviewing services-config.xml

 

For Flash Remoting, the client sends a request to the server to be processed and the server returns a response to the client containing the results. You configure these requests by modifying the services-config.xml and remoting-config.xml files located in the /WEB-INF/flex/ folder for the web application.

The services-config.xml file defines different channels that can be used when making a request. Each channel definition specifies the network protocol and the message format to be used for a request and the endpoint to deliver the messages to on the server. The Java-based endpoints unmarshal the messages in a protocol-specific manner and then pass the messages in Java form to the MessageBroker which sends them to the appropriate service destination (you'll see how to define these next).

 

<channels> <channel-definition id="my-amf" class="mx.messaging.channels.AMFChannel"> <endpoint url="http://{server.name}:{server.port}/{context.root}/messagebroker/amf" class="flex.messaging.endpoints.AMFEndpoint"/> </channel-definition> <channel-definition id="my-secure-amf" class="mx.messaging.channels.SecureAMFChannel"> <endpoint url="https://{server.name}:{server.port}/{context.root}/messagebroker/amfsecure" class="flex.messaging.endpoints.SecureAMFEndpoint"/> </channel-definition> (...) </channels>

 

Defining destinations

 

In the remoting-config.xml file, you define the destinations (named mappings to Java classes) to which the MessageBroker passes the messages. You set the source property to the fully qualified class name of a Java POJO with a no argument constructor that is located in a source path, usually achieved by placing it in the web application's /WEB‑INF/classes/ directory or in a JAR file in the /WEB‑INF/lib/ directory. You can access EJBs and other objects stored in the Java Naming and Directory Interface (JNDI) by calling methods on a destination that is a service facade class that looks up an object in JNDI and calls its methods.

You can access stateless or stateful Java objects by setting the scope property to application, session, or request (the default). The instantiation and management of the server-side objects referenced is handled by BlazeDS or LiveCycle Data Services.

 

<service id="remoting-service" class="flex.messaging.services.RemotingService"> <adapters> <adapter-definition id="java-object" class="flex.messaging.services.remoting.adapters.JavaAdapter" default="true"/> </adapters> <default-channels> <channel ref="my-amf"/> </default-channels> <destination id="employeeService"> <properties> <source>services.EmployeeService</source> <scope>application</scope> </properties> </destination> </service>

 

You can also specify channels for individual destinations.

 

<destination id="employeeService " channels="my-secure-amf">

 

Lastly, you use these destinations when defining RemoteObject instances in a Flex application.

 

<s:RemoteObject id="employeeSvc" destination="employeeService"/>

 

Security

 

In many applications, access to some or all server-side resources must be restricted to certain users. Many Java EE applications use container managed security in which user authentication (validating a user) and user authorization (determining what the user has access to—which is often role based) are performed against the Realm, an existing store of usernames, passwords, and user roles. The Realm is configured on your Java EE server to be a relational database, an LDAP directory server, an XML document, or to use a specific authentication and authorization framework.

To integrate a Flex application with the Java EE security framework so that access to server-side resources is appropriately restricted, you add security information to the BlazeDS or LiveCycle Data Services configuration files (details follow below) and then typically in the Flex application, create a form to obtain login credentials from the user which are passed to the server to be authenticated. The user credentials are then passed to the server automatically with all subsequent requests.

 

Modifying services-config.xml

 

In the BlazeDS or LiveCycle Data Services services-config.xml file, you need to specify the "login command" for your application server in the <security> tag. BlazeDS and LiveCycle Data Services supply the following login commands: TomcatLoginCommand (for both Tomcat and JBoss), JRunLoginCommand, WeblogicLoginCommand, WebSphereLoginCommand, OracleLoginCommand. These are all defined in the XML file and you just need to uncomment the appropriate one.

You also need to define a security constraint that you specify to use either basic or custom authentication and if desired, one or more roles. To do custom authentication with Tomat or JBoss, you also need to add some extra classes to the web application for integrating with the security framework used by the Jave EE application server and modify a couple of configuration files. Mode details can be found here.

 

<services-config> <security> <login-command class="flex.messaging.security.TomcatLoginCommand" server="Tomcat"> <per-client-authentication>false</per-client-authentication> </login-command> <security-constraint id="trusted"> <auth-method>Custom</auth-method> <roles> <role>employees</role> <role>managers</role> </roles> </security-constraint> </security> ... </services-config>

 

Modifying remoting-config.xml

Next, in your destination definition, you need to reference the security constraint:

 

<destination id="employeeService"> <properties> <source>services.EmployeeService</source> </properties> <security> <security-constraint ref="trusted"/> </security> </destination>

 

You can also define default security constraints for all destinations and/or restrict access to only specific methods that can use different security constraints.

The default channel, my-amf, uses HTTP. You can change one or more of the destinations to use the my-secure-amf channel that uses HTTPS:

 

<destination id="employeeService"> <channels> <channel ref="my-secure-amf"/> </channels> ... </destination>

 

where my-secure-amf is defined in the services-config.xml file:

 

<!-- Non-polling secure AMF --> <channel-definition id="my-secure-amf" class="mx.messaging.channels.SecureAMFChannel"> <endpoint url="https://{server.name}:{server.port}/{context.root}/messagebroker/amfsecure" class="flex.messaging.endpoints.SecureAMFEndpoint"/> </channel-definition>

 

Adding code to the Flex application

 

That covers the server-side setup. Now, if you are using custom authentication, you need to create a form in the Flex application to retrieve a username and password from the user and then pass these credentials to the server by calling the ChannelSet.login() method and then listening for its result and fault events. A result event indicates that the login (the authentication) occurred successfully, and a fault event indicates the login failed. The credentials are applied to all services connected over the same ChannelSet. For basic authentication, you don’t have to add anything to your Flex application. The browser opens a login dialog box when the application first attempts to connect to a destination.

Your application can now make Flash Remoting requests to server destinations just as before, but now the user credentials are automatically sent with every request (for both custom and basic authentication). If the destination or methods of the destination have authorization roles specified which are not met by the logged in user, the call will return a fault event. To remove the credentials and log out the user, you use the ChannelSet.logout() method.

 

Flex application architecture

 

Now that you've learned to set up Flash Remoting on the server-side and define a RemoteObject instance in Flex, let's take a look at how you build an application to use this object.

 

Using events

 

A typical Flex application consists of MXML code to define the user interface and ActionScript code for the logic. Just as for JavaScript and the browser DOM objects, the two are wired together using events and event handlers. To use a RemoteObject in an application, you define the instance, invoke a method of the server-side remoting destination, specify callback functions for the result and fault events, and inside those, do something with the data returned from the server.

Here is a simple application where employee data is retrieved from a database and displayed in a Flex DataGrid component. After the application is initialized, the getEmployees() method of the employeeService destination defined in the remoting-config.xml file on the server is called, and if data is successfully returned from the server, the variable employees is populated and if the request fails for any reason, a message is displayed in an Alert box. Data binding is used to bind the employees variable to the dataProvider property of the DataGrid.

 

<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009" xmlns:s="library://ns.adobe.com/flex/spark" xmlns:mx="library://ns.adobe.com/flex/mx" initialize="employeeSvc.getEmployees()"> <fx:Script> <![CDATA[ import mx.collections.ArrayCollection; import mx.controls.Alert; import mx.rpc.events.FaultEvent; import mx.rpc.events.ResultEvent; [Bindable]private var employees:ArrayCollection; private function onResult(e:ResultEvent):void{ employees=e.result as ArrayCollection; } private function onFault(e:FaultEvent):void{ Alert.show("Error retrieving data.","Error"); } ]]> </fx:Script> <fx:Declarations> <s:RemoteObject id="employeeSvc" destination="employeeService" result="onResult(event)" fault="onFault(event)" /> </fx:Declarations> <mx:DataGrid dataProvider="{employees}"/> </s:Application>

 

When using a RemoteObject, you can define result and fault handlers on the service level:

 

<s:RemoteObject id="employeeSvc" destination="employeeService" result="onResult(event)" fault="onFault(event)"/>

 

on the method level:

 

<s:RemoteObject id="employeeSvc" destination="employeeService"> <s:method name="getEmployees" result="onResult(event)" fault="onFault(event)"/> <s:method name="getDepartments" result="onResult2(event)" fault="onFault2(event)"/> </RemoteObject>

 

or on a per-call basis:

 

<s:Application xmlns:fx=http://ns.adobe.com/mxml/2009" xmlns:s="library://ns.adobe.com/flex/spark" xmlns:mx="library://ns.adobe.com/flex/mx" initialize="getEmployeesResult.token=employeeSvc.getEmployees()"> <fx:Declarations> <s:RemoteObject id="employeeSvc" destination="employeeService"/> <s:CallResponder id="getEmployeesResult" result="onResult(event)" fault="onFault(event)"/> </fx:Declarations>

 

Using data binding

 

Data binding is a powerful part of the Flex framework that lets you update the user interface when data changes without you having to explicitly register and write the event listeners to do this. In the previous application code, the [Bindable] tag in front of the employees variable definition is a compiler directive; when the file is compiled, ActionScript code is automatically generated so that an event is broadcast whenever the employees variable changes.

 

[Bindable]private var employees:ArrayCollection;

 

The curly braces in the assignment of the DataGrid's dataProvider property actually generates the code to listen for changes to the employees variable and when it changes, to update the DataGrid view accordingly.

<mx:DataGrid dataProvider="{employees}"/>

 

In this application, employees is initially null and no data is displayed in the DataGrid but as soon as the data is successfully retrieved form the server and employees is populated, the DataGrid is updated to display the employee data.

 

Using view states

 

To make more extreme changes to the user interface dynamically at runtime, for instance to add, remove, move, or modify components, you use Flex view states. For every Flex view or component, you can define multiple states and then for every object in that view, you can define what state(s) it should be included in and what it should look like and how it should behave in that state. You switch between states by setting the component's currentState property to the name of one of the defined states.

 

<s:states> <s:State name="employees"/> <s:State name="departments"/> </s:states> <mx:DataGrid dataProvider="{employees}" includeIn="employees"/> <s:Button label.employees="Switch to departments" label.departments="Switch to employees" click.employees="currentState='departments'" click.departments="currentState='employees'"/>

 

Using MXML components

 

As your application gets larger, you need to break up your logic into packages of ActionScript classes and your views into separate MXML files (called MXML components). Each MXML component extends an existing component and can only be included in an application, but not run on its own. To use a component in MXML, you instantiate an instance of that component (its class name is the same as its file name) and include the proper namespace so the compiler can locate it.

Here is the code for a MXML component, Masterview, saved as MasterView.mxml in the com.adobe.samples.views package.

<s:Group xmlns:fx="http://ns.adobe.com/mxml/2009" xmlns:s="library://ns.adobe.com/flex/spark" > <fx:Metadata> [Event(name="masterDataChange",type="flash.events.Event")] </fx:Metadata> <fx:Script> <![CDATA[ import mx.collections.ArrayList; [Bindable]private var masterData:ArrayList=new ArrayList(["data1", "data2", "data3"]); public var selectedData:String; private function onChange(e:Event):void{ selectedData=dataList.selectedItem; this.dispatchEvent(new Event("masterDataChange")); } ]]> </fx:Script> <s:DropDownList id="dataList" dataProvider="{masterData}" change="onChange(event)"/> </s:Group>

 

Here is the code for an application that instantiates and uses that custom MasterView component.

 

<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009" xmlns:s="library://ns.adobe.com/flex/spark" xmlns:views="com.adobe.samples.views.*"> <fx:Script> <![CDATA[ import mx.controls.Alert; private function onMasterDataChange(e:Event):void{ Alert.show(e.currentTarget.selectedData,"Master data changed"); } ]]> </fx:Script> <views:MasterView masterDataChange="onMasterDataChange(event)"/> </s:Application>

 

Broadcasting events

 

In order to build loosely-coupled components, you need to define a public API for the component (its public members) and/or define and broadcast custom events as shown in the MasterView code example above. The [Event] metadata tag is used to define the event as part of the component's API and specify what type of event object it broadcasts.

 

<fx:Metadata> [Event(name="masterDataChange",type="flash.events.Event")] </fx:Metadata>

 

When some event occurs in the component (in this example, a DropDownList change event), the component creates an instance of the type of event object specified in the metadata and broadcasts it.

 

this.dispatchEvent(new Event("masterDataChange"));

 

The code that instantiates this custom component can now register to listen for this custom event and register and event handler.

 

<views:MasterView masterDataChange="onMasterDataChange(event)"/>

 

Loosely-coupled components like this that define and broadcast custom events are the core building blocks for Flex applications. In fact, this is how the components in the Flex framework itself are built. For more information on broadcasting custom events, watch the video, Learn how to define and broadcast events.

 

Creating modules

 

By default, all your code gets compiled into one SWF file. If your SWF file gets very large or contains functionality that only specific users may use, you can use modules to break your application into multiple SWF files that can be loaded and unloaded dynamically by the main application at runtime. To create a module, you create a class (ActionScript of MXML) extending the Module class and then compile it. To load the module dynamically at runtime into an application, you use the <mx:ModuleLoader> tag or methods of the ModuleLoader class.

 

Using a microarchitecture

 

That covers the basics for building an application, but as your application gets larger, you are going to want to use some methodology to organize its files, centralize the application data and data services, and handle communication between all the components. To do this, you can build your Flex application using all the design patterns that have proven useful over the years in enterprise application development. In fact, many Flex specific microarchitectures have been and continue to be developed. The oldest and most established is Cairngorm, an open source microarchitecture that uses commands and delegates, front controllers, a singleton data model, a singleton service store, and an event dispatcher. Other popular frameworks include Pure MVC, Mate, Parsley, Swiz, and Spring ActionScript. For more information about these and other frameworks, see Flex Architecture on the Adobe Developer Center.

 

Messaging

 

To this point, the article has focused on creating applications that use a call-response model to make asynchronous calls to Java classes on the server. Using BlazeDS or LiveCycle Data Services, you can also build applications that use a publish-subscribe model to send messages between multiple Flex clients (through the server), push messages from the server to clients, and/or send messages to other JMS enabled messaging clients. A Flex application can send messages to a destination on the server and any other clients subscribed to that same destination will receive those messages.

A simple application using messaging is instant messaging where text is exchanged between clients. Messaging can also be used to create rich collaborative data applications where data changes made in one client are "instantly" seen by other clients viewing the same data. Server sending notifications to clients, clients receiving sport score updates, auction sites having access to real-time bids, applications for trading stocks, foreign exchange etc. are all examples of applications that can be developed using the messaging infrastructure.

 

Defining destinations

 

Similar to how you configure remoting, you configure messaging by defining destinations in a server-side configuration file, in this case, messaging-config.xml. A messaging destination can be as simple as this:

 

<destination id="chat">

 

in which case it uses the default adapter and channel defined in the messaging-config.xml file:

 

<adapters> <adapter-definition id="actionscript" class="flex.messaging.services.messaging.adapters.ActionScriptAdapter" default="true" /> <adapter-definition id="jms" class="flex.messaging.services.messaging.adapters.JMSAdapter"/> </adapters> <default-channels> <channel ref="my-rtmp"/> <channel ref="my-streaming-amf"/> </default-channels>

 

The first adapter defined, actionscript, is the default adapter and is used to exchange messages between Flex clients. The jms adapter can be used instead to bridge to JMS destinations. The default channel is my-rtmp, a real-time streaming channel with failover to a streaming AMF channel (both defined in the services-config.xml file). Channels are discussed in more detail in the next section, Selecting a channel.

You can also specify additional properties when defining a destination, including network and server properties. In the following destination, the chat destination is configured to use the my-polling-amf channel, users are never unsubscribed even with no activity, messages are kept on the server indefinitely until there are 1000 messages at which time the oldest is replaced, and only clients that have been authenticated and authorized against the trusted security constraint defined in the services-config.xml file (see the Security section) can publish or receive messages.

 

<destination id="chat"> <properties> <channels> <channel ref="my-polling-amf"/> </channels> <network> <session-timeout>0</session-timeout> </network> <server> <max-cache-size>1000</max-cache-size> <message-time-to-live>0</message-time-to-live> <durable>false</durable> <send-security-constraint ref="trusted"/> <subscribe-security-constraint ref="trusted"/> </server> </properties> </destination>

 

Selecting a channel

 

When defining a destination, you specify the channel to be used for the communication between the client and server including the protocol, the port, and the endpoint. Channels are defined in the services-config.xml file. For remoting, you usually use the my-amf or my-secure-amf channel. For messaging, there is larger number of channels to select from, including those that use polling or streaming, servlets or sockets, and HTTP or RTMP.

Polling channels support polling the server on some interval or on some event. The my-polling-amf channel polls the server every 8 seconds for new messages.

 

<channel-definition id="my-polling-amf" class="mx.messaging.channels.AMFChannel"> <endpoint url="http://{server.name}:{server.port}/{context.root}/messagebroker/amfpolling" class="flex.messaging.endpoints.AMFEndpoint"/> <properties> <polling-enabled>true</polling-enabled> <polling-interval-seconds>8</polling-interval-seconds> </properties> </channel-definition>

 

To more closely mimic a real-time connection, you can use long polling. The my-amf-longpoll channel is configured for long polling.

 

<channel-definition id="my-amf-longpoll" class="mx.messaging.channels.AMFChannel"> <endpoint url="http://{server.name}:{server.port}/{context.root}/messagebroker/myamflongpoll" class="flex.messaging.endpoints.AMFEndpoint"/> <properties> <polling-enabled>true</polling-enabled> <polling-interval-seconds>0</polling-interval-seconds> <wait-interval-seconds>60</wait-interval-seconds> <client-wait-interval-seconds>3</client-wait-interval-seconds> <max-waiting-poll-requests>100</max-waiting-poll-requests > </properties> </channel-definition>

 

When this channel is used, the client polls the server; the server poll response thread waits 60 seconds for new messages to arrive if there are no new messages on the server and then returns to the client; after receiving the poll response, the client polls again after 3 seconds; the process is repeated. The server is set to allow 100 simultaneous server poll response threads in a wait state; if exceeded, the server does not wait for new messages before returning a response. Typical application servers might have around 200 HTTP request threads available, so you need to make sure you set the maximum allowable number of polling threads to a smaller number and still leave enough threads to handle other HTTP requests.

With servers and proxy servers that support HTTP 1.1, an HTTP streaming channel can be used. A persistent connection is established between the client and the server over which server messages are pushed to the client. HTTP connections can't handle traffic in both directions, so separate, short-lived threads must be used for any other server requests. Network latency is minimized compared to long-polling because connections don’t have to be continually closed and reopened.

 

<channel-definition id="my-streaming-amf" class="mx.messaging.channels.StreamingAMFChannel"> <endpoint url="http://{server.name}:{server.port}/{context.root}/messagebroker/streamingamf" class="flex.messaging.endpoints.StreamingAMFEndpoint"/> </channel-definition>

 

Using HTTP long-polling and streaming the number of simultaneous users that can be connected to a destination is limited by the available number of server HTTP threads. For applications that will have larger numbers of simultaneous users, messages can be pushed using sockets instead of HTTP threads. LiveCycle Data Services includes a NIO-based socket server and has additional channels available for messaging that are not available with BlazeDS. These channels, defined in the services-config.xml file, all contain "nio" in their names. NIO stands for Java New Input/Output and is a collection of Java APIs for I/O operations.

If you are using LiveCycle Data Services you should use the NIO channels over the servlet based channels because they scale better, handling thousands of simultaneous users instead of around a hundred. There are NIO equivalents for each of the AMF polling, long polling, and streaming channels just discussed (my-nio-amf-poll, my-nio-amf-longpoll, my-nioamf-stream). These channels are still using HTTP so in the latter two cases, separate threads are still required for client-server requests and the persistent (or waiting) threads used for the server-to-client updates.

 

<channel-definition id="my-nio-amf-longpoll" class="mx.messaging.channels.AMFChannel"> <endpoint url="http://{server.name}:2080/nioamflongpoll" class="flex.messaging.endpoints.NIOAMFEndpoint"/> <server ref="my-nio-server"/> <properties> <polling-enabled>true</polling-enabled> <polling-interval-millis>0</polling-interval-millis> <wait-interval-millis>-1</wait-interval-millis> </properties> </channel-definition>

 

With LiveCycle Data Services you can choose channels that use the RTMP protocol instead of HTTP.

 

<channel-definition id="my-rtmp" class="mx.messaging.channels.RTMPChannel"> <endpoint url="rtmp://{server.name}:2037" class="flex.messaging.endpoints.RTMPEndpoint"/> <properties> <idle-timeout-minutes>20</idle-timeout-minutes> </properties> </channel-definition>

 

RTMP, the Real-Time Messaging Protocol, was developed by Adobe for high-performance transmission of audio, video, and data between Adobe Flash Platform technologies (like Adobe Flash Player and Adobe AIR) and is now available as an open specification. RTMP provides a full duplex socket connection so that a single connection can be used for all communication between the client and the server, including all RPC and messaging. Another benefit of RTMP is that when a client connection is closed, the endpoint is immediately notified (so the application can instantly respond) unlike when using the HTTP protocol where endpoints do not receive notification until the HTTP session on the server times out. Because RTMP generally uses a non-standard port, though, it is often blocked by client firewalls. In this case, the channel automatically attempts to tunnel over HTTP.

As a general recommendation, if you are using LiveCycle Data Services, use RTMP with failover to NIO-based long-polling. If using BlazeDS, use AMF long-polling or AMF streaming with failover to long-polling.

 

Sending and receiving messages

 

To send messages from a Flex application you use the Producer API and to receive messages, the Consumer API. A basic application sends, receives, and displays messages is shown here.

 

<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009" xmlns:s="library://ns.adobe.com/flex/spark" xmlns:mx="library://ns.adobe.com/flex/mx" creationComplete="application1_creationCompleteHandler()"> <fx:Script> <![CDATA[ import mx.messaging.events.MessageEvent; import mx.messaging.messages.AsyncMessage; protected function application1_creationCompleteHandler():void{ consumer.subscribe(); } protected function button1_clickHandler(event:MouseEvent):void{ var message:AsyncMessage=new AsyncMessage(); message.headers.username=username.text; message.body=msg.text; producer.send(message); msg.text=""; } protected function consumer_messageHandler(event:MessageEvent):void{ messageDisplay.text+=event.message.headers.username+": "+event.message.body+"\n"; } ]]> </fx:Script> <fx:Declarations> <s:Producer id="producer" destination="chat"/> <s:Consumer id="consumer" destination="chat" message="consumer_messageHandler(event)"/> </fx:Declarations> <s:TextArea id="messageDisplay" width="400"/> <s:TextInput id="username" x="0" y="160"/> <s:TextInput id="msg" x="136" y="160"/> <s:Button label="Send" click="button1_clickHandler(event)" x="272" y="160"/> </s:Application>

 

For more information about using the messaging service, see the BlazeDS and LCDS documentation.

 

Data managed applications

 

You can build real-time data applications, applications for which data changes made in one client are "instantly" seen by other clients viewing the same data, using a combination of remoting and messaging. This entails writing a lot of client-side code to keep track of the changes made to the data on the client (additions, updates, and deletions), to make calls to retrieve and persist data on the server, to send messages to other clients when the data has changed, to make calls to retrieve and display this new data, to recognize and handle data conflicts, and to resolve these conflicts on the client and server. To help you more quickly and easily build these types of data-intensive, transaction-oriented applications without having to write so much code. LiveCycle Data Services (and not BlazeDS) provides the Data Management service.

The Data Management service provides client and server-side code to help you build applications that provide real-time data synchronization between client, server, and other clients; data replication; on-demand data paging; and for AIR applications, local data synchronization for occasionally connected applications.

To build a managed data application you define a Data Management service destination in a configuration file on the server and then use the Flex DataService component in the application to call methods of a server-side service specified by that destination. The DataService API provides methods for filling client-side data collections with data from the server and batching and sending data changes to the server. The Data Management service on the server handles checking for conflicts, committing the changes, and pushing the data changes to simultaneously connected clients.

 

Defining destinations

 

Similar to how you configure remoting and messaging, you typically configure data management by defining destinations in a server-side configuration file, in this case, data-management-config.xml. The default configuration file defines a default channel, the RTMP channel discussed in Selecting a channelin the Messaging section of this article, and a default adapter, actionscript.

 

<service id="data-service" class="flex.data.DataService"> <adapters> <adapter-definition id="actionscript" class="flex.data.adapters.ASObjectAdapter" default="true"/> <adapter-definition id="java-dao" class="flex.data.adapters.JavaAdapter"/> </adapters> <default-channels> <channel ref="my-rtmp"/> </default-channels> </service>

 

The adapter is responsible for updating the server-side data. The actionscript adapter is used for services that have no persistent data store on the server but instead manage data in the server's memory. The java-dao adapter passes the data changes to appropriate methods of a Java assembler class, which typically calls methods of a data access object (DAO) to persist data in a database.

When defining a destination using the java-dao adapter, you specify the assembler class that handles the data persistence and the property of the data objects that uniquely identifies an object. Below is a data management destination called employeeService that uses a Java class called EmployeeAssembler to persist data in a database table with a unique field employeeId. The Java assembler class must extend an AbstractAssembler class provided with LiveCycle Data Services that has methods including fill(), createItem(), deleteItem(), and updateItem().

 

<destination id="employeeService"> <adapter ref="java-dao"/> <properties> <source>adobe.samples.EmployeeAssembler</source> <metadata> <identity property="employeeId"/> </metadata> </properties> </destination>

 

You can add additional properties to the destination definition to specify the scope the assembler is available in (request, session, or application), to configure paging, to specify security-constraints, and more.

LiveCycle Data Services also provides some standard assembler classes that you can use so you don’t have to write your own. The SQLAssembler provides a bridge to a SQL database without requiring you to write the Java assembler code. Instead, you specify database info (url, driver, username, password, etc.) and SQL statements (the SQL to execute when data is sent from the Flex application to be added, updated, or deleted) right in the destination definition. This assembler can be used for simple database models that do not have any nested relationships. If you are using Hibernate, you can use the HibernateAssembler, which provides a bridge to the Hibernate object/relational persistence and query service. It uses the Hibernate mapping files to at runtime to execute the necessary SQL to persist data changes to the database.

 

Creating a managed data application

 

To create a Flex managed data application that uses the LCDS Data Management service, you create a DataService object with its destination property set to a destination defined in the data-management-config.xml file. You use the DataService fill() method to fetch data from the server and populate an ArrrayCollection with the data. By default, the DataService commit() method is called whenever data changes in the ArrayCollection it manages. To avoid excessive calls, you can batch the calls by setting the DataService object's autoCommit property to false and then explicitly calling its commit() method.

Here is a simple application that uses the employeeService Data Management destination to retrieve employee data from the database on the server and populate a DataGrid with that data. When changes are made to the data in the DataGrid, the changes are automatically persisted on the server and synchonized with any other instances of the client application.

 

<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009" xmlns:s="library://ns.adobe.com/flex/spark" xmlns:mx="library://ns.adobe.com/flex/mx" xmlns:valueObjects="valueObjects.*" creationComplete="employeeDS.fill(employees)"> <fx:Declarations> <s:DataService id="employeeDS" destination="employeeService"/> <s:ArrayCollection id="employees"/> <valueObjects:Employee id="employee"/> </fx:Declarations> <mx:DataGrid dataProvider="{employees}" editable="true"/> </s:Application>

 

For more information about using the Data Management service, see the Live Cycle Data Services documentation.

 

Model driven development

 

In previous sections of this article, you learned to use the Remoting and Messaging services of BlazeDS and LCDS and the Data Management service of LCDS to build data-centric applications. You can build these types of applications even faster using the Adobe application modeling technology (code named Fiber), a set of technologies that together enable model driven development for Flex applications, which can be used to generate both client and server-side code.

 

Generating client-side code using Flash Builder

 

Instead of using the RemoteObject class (or other RPC classes) to make calls to server-side classes, you can use Flash Builder to create ActionScript service wrapper classes and use these classes. The RPC service wrapper classes have public methods with the same names as the corresponding server-side classes making development and debugging much simpler.

In order to generate client-side code, RDS access must be enabled on the server so Flash Builder can introspect server-side Java classes and configuration files. To enable RDS access, you need to add and/or uncomment a mapping for the BlazeDS 4 or LiveCycle Data Services 3 RDSDispatchServlet in the web application's web.xml file and disable security by setting the useAppserverSecurity parameter to false (or alternatively, set up and enable security).

 

<servlet> <servlet-name>RDSDispatchServlet</servlet-name> <display-name>RDSDispatchServlet</display-name> <servlet-class>flex.rds.server.servlet.FrontEndServlet</servlet-class> <init-param> <param-name>useAppserverSecurity</param-name> <param-value>false</param-value> </init-param> <load-on-startup>10</load-on-startup> </servlet> <servlet-mapping id="RDS_DISPATCH_MAPPING"> <servlet-name>RDSDispatchServlet</servlet-name> <url-pattern>/CFIDE/main/ide.cfm</url-pattern> </servlet-mapping>

 

Once RDS is enabled for the server, you can generate ActionScript service wrappers in Flash Builder using the Data menu (see Figure 5).

 

The Flash Builder Data menu for creating client-side code.
Figure 5. The Flash Builder Data menu for creating client-side code.

 

When selecting Connect to BlazeDS or Connect to LCDS, you will get a dialog box displaying all the server-side destinations defined in the configuration files (see Figure 6).

 

The dialog box for selecting service destinations.
Figure 6. The dialog box for selecting service destinations.

 

Flash Builder generates ActionScript wrapper classes for the selected services and classes for the corresponding data transfer objects (also called value objects) manipulated by these classes (which often correspond to records in database tables) (see Figure 7). You can then manipulate the same types of objects on the client and on the server and pass instances of them back and forth between the two. If you are using LCDS, the generated service classes use LiveCycle specific classes to also provide the additional data management features discussed previously. The use of these generated client-side service wrapper and valueObject classes that map to the server-side classes greatly facilitates application development.

 

The generated client-side classes.
Figure 7. The generated client-side classes.

 

In the services package, the _Super_ServiceName.as class extends the RemoteObject or DataService class it is wrapping and defines the service methods. In the case of RemoteObject, the service class will have public methods with the same names as the corresponding server-side class methods. The ServiceName.as class extends the super class and is intitially empty. This is the class you use in your code. You can modify this class to customize the wrapper. This file is not overwritten if you refresh the service to recreate the client-side code after changes have been made to the server-side service code.

In the valueObjects package, the _EntityNameEntityMetadata.as class contains information about an entity (an object manipulated by the service class) and its relationship with other entities. The _Super_EntityName.as class contains getters and setters for the data properties of an entity. The EntityName.as class extends the super class and is initially empty. This is the class you use in your code. You modify this class to customize the generated value object class.

 

Generating server-side code using LCDS and the Modeler plug-in

 

If you are using LiveCycle Data Services, you can use Flash Builder in conjunction with an additional Modeler plug-in to generate server-side code in addition to client-side code. The Adobe application modeling plug-in for Adobe Flash Builder (the Modeler) is a graphical modeling editor for defining data models and generating client and server-side code to manipulate these data models. You can use the Modeler to define a model based on an existing database and then have it generate and deploy the client-side code, the server-side code, and the server-side configuration files needed to manipulate this data. Or if you are starting from scratch and the database tables don't exist yet, you can use the Modeler to define a model and then have it generate the database tables in addition to generating the client and server-side code to manipulate this data.

In order to use the Modeler, you need to define your data source as a resource in your webapp.xml file, install the Modeler in Flash Builder, and configure RDS in Flash Builder. For detailed steps, see the tutorial Setting up model-driven development with LiveCycle Data Services ES2. You can then use the Modeler and its RDS Dataview view to create data models and generate client and server-side code.

The RDS Dataview view displays SQL databases configured as JDBC datasources on the server (see Figure 8).

 

The Modeler Design view.
Figure 8. The RDS Dataview view.

 

You can drag database tables from the RDS Dataview view to the Modeler Design view to define corresponding entities in your data model (see Figure 9). You can also use the Modeler Design mode tools to define entities and relationships if there are no corresponding database tables. The Modeler Design view uses standard UML notation in its diagrams.

 

The Modeler Design view.
Figure 9. The Modeler Design view.

 

The data model is stored as XML in a file with an FML extension. You can switch to the source mode of the Modeler view to look at the generated model XML code.

 

Generated model code in the Modeler source mode.
Figure 10. Generated model code in the Modeler source mode.

 

To create code to manipulate the entities, you click the Modeler Generate Code button in the Modeler Design view. By default, no server-side code is generated. To customize the generated code, select Window > Preferences, select Adobe > Data Model > Code Generation, and modify the settings. You can specify whether only server-side value objects corresponding to the entities in the data model are created or value objects and assembler classes to manipulate the value objects and persist them in the database are generated (see Figure 11).

 

The dialog box for configuring the Modeler's generated code.
Figure 11. The dialog box for configuring the Modeler's generated code.

 

Example generated value object and assembler classes are show in Figure 12.

 

The generated server-side Java classes.
Figure 12. The generated server-side Java classes.

 

For more information about using moel driven development, see the LiveCycle Data Services documentation.

 

Portal integration

 

Using LiveCycle Data Services, you can deploy Flex applications as local portlets on portal servers that implement the JSR 168 portlet specification or that support Web Services for Remote Portlets (WSRP); this includes JBoss Portal, BEA WebLogic Portal, and IBM WebSphere Portal. The Flex application can be part of a LiveCycle Data Services application (for example, using the Remoting, Messaging, and/or Data Management services) but it does not have to be.

To enable a Flex application to be deployed as a portlet, you need to copy and customize some files included in the LiveCycle Data Services /lcds/resources/wsrp/ directory and then follow the portal server's specific steps to set up the portlet.

 

The required LiveCycle Data Services files for deployment on portals.
Figure 13. The required LiveCycle Data Services files for deployment on portals.

 

You need to copy the flex-portal.jar file to your web application's /WEB-INF/lib/ directory. (If LiveCycle Data Services is not being used on the server, the flex-messaging-common jar file must also be copied to there. ) The flex-portal.jar file contains a GenericFlexPortlet class that handles all WSRP requests and returns appropriate HTML depending upon whether the view, edit, or portlet mode is requested. The LiveCycle Data Services wsrp-jsp folder contains three JSP pages used for the view, edit, and help portlet view modes. You need to copy this wsrp-jsp folder to the root of your web application and customize these pages for your application. When a specific view of the portlet is requested, the GenericFlexPortlet class delivers one of these JSP pages. The portlet-view.jsp contains HTML and JavaScript for loading the application SWF and checking for the necessary version of Flash Player.

Requests for a portlet specify whether the portlet should be maximized, minimized, or normal. The value for this requested window state is passed to the Flex application as a flashvar, and can be accessed as FlexGlobals.topLevelApplication.parameters.PORTLET_WS allowing you to customize the application for the specific window state requested. If a minimized portlet is requested, the GenericFlexPortlet does not return a SWF because the user would not be able to interact with it anyways.

 

Where to go from here

 

This article discussed the architecture of Flex and Java applications. For additional information, use the links contained in the article and the following resources: