Rich Internet Applications (RIAs) are increasingly being seen as successful models for creating lightweight front-ends for SOAs. They not only allow developers to overcome the static natured HTML’s limitation of refreshing the entire page to load new information but also allow flashy graphics, and immersive user experiences. Adobe’s Flex is a free open source framework for building and maintaining RIAs that allows consistent deployment on all major browsers. Adobe provides a commercially available development environment (Flex builder) with free trial version for 60 days that is highly recommended for ease of development.
Besides the rich interface library, Flex provides SOA based data access components that allows server side interaction for the flex applications. This article will look at the different ways for bringing server side data into a Flex application, primarily focusing on Remote Services using BlazeDS.
Flex Data Access
Flex provides a variety of interfaces for enabling Flex based applications, that may be flash player based browser applications or AIR based desktop applications, to communicate with servers that may be running Java, ColdFusion, .Net, PHP, Ruby or any other server side technologies. These interfaces differ primarily in terms of how the data is passed over HTTP. Depending on how the server side is implemented and the best way to pass data and handle results from the services, one of the following components may be used.
REST-style or SOAP compliant web services
REST and SOAP services can work with any type of server side technology. These are standards and most of the biggest service providers like Yahoo, Amazon, and eBay etc. expose their API using both of them.
For REST-style web services, the Flex application uses the ‘HTTPService’ component to communicate with a server implementation that is expecting a HTTP POST or GET request. This provides an easy way to access a server resource without a more formalized API such as those provided by SOAP compliant web services or remote object services.
For SOAP compliant web services, the Flex application uses the ‘WebService’ component to communicate with the server implementation. Flex applications can interact with web services that define their interfaces in a Web Services Description Language (WSDL) document. Many server technologies provide the ability to interact with applications as web services.
Action Script Message Format (AMF) remote services
Remote object services allow access to business logic directly in its native format rather than formatting it as XML, as required with REST and SOAP web services. On using Flex’s ‘RemoteObject’ component, data is passed between the Flex application and the server implementation in the binary Action Script Message Format (AMF).
REST/SOAP services having well established standards, can be written once and used from most of the systems, not just Flex applications. However, they are heavy across the wire (especially SOAP is often very verbose), which can result in higher client-side memory requirements and processing time. With remote object services, although data exchanges still happen over HTTP(S),the data is serialized into a binary representation resulting in less data going over the wire. Besides, remote object services offer automatic conversion between Action Script and the Java objects (or another language).
BlazeDS
Flex supports Action Script Message Format (AMF) protocol that transmits binary data over HTTP in place of text based protocols that transmit XMLs. This allows developers to eliminate the extra data abstraction layer and hence improves application performance and developer productivity. The server side AMF capabilities are part of Flex's Live Cycle Data Services (LCDS). In the last couple of years, Adobe has made AMF specifications publically available allowing compliant server side implementation for .NET, PHP, Ruby etc. It also has introduced the BlazeDS project as a limited open source version of LCDS.
BlazeDS, an open source Java implementation of AMF based remoting and messaging, allows Flex based front end applications to easily connect to existing methods on Plain old Java objects (POJOs), Spring services, or EJBs. BlazeDS works with a wide range of Java-based application servers, including Tomcat, WebSphere, JBoss, and ColdFusion.
Unlike LCDS, BlazeDS does not provide true push functionality through Adobe’s Real Time Messaging Protocol (RTMP) which creates a constant connection between itself and the client. BlazeDS is open source, and since RTMP isn't, it is not available as a channel in the BlazeDS configuration files. One option for implementing push messaging in BlazeDS is to create a channel for AMF polling, configure the settings for polling, and then define your message producers and consumers. More details around how BlazeDS is different from LCDS are available here.
Using BlazeDS
For BlazeDS, you can quickly get started using the Turnkey distribution (from here) that contains a ready-to-use version of Tomcat, and BlazeDS WAR file deployed and configured with a variety of sample applications. In case you have your application already deployed and running on a J2EE server, BlazeDS is easy to setup and integrate your business logic to Flex based applications. The following are the high levels steps to have your Flex application talking to the BlazeDS enabled server.
a) Download and install BlazeDS (from here), Flex3 SDK (from here) and trial version of Flex Builder (from here).
b) Assuming you have your business layer implemented and deployed on the server, you can create a POJO Java class to expose the business methods you want to access from your Flex application.
package com.summa; import org.apache.log4j.Logger; import com.summa.bd.UserManager; public class FlexSpringInterfaceService { Logger logFile = Logger.getLogger(FlexSpringInterfaceService.class); public void addUser(User newUser) { logFile.info("Invoking Business Layer Routine to Add New User"); UserManager.getInstance().addUser(newUser); } }
c) Copy the 'flex' folder from blazeds.war\WEB-INF into your WEB-INF folder. This folder contains the configuration files needed by BlazeDS. In the remoting-config.xml file, add a new remote destination pointing to the remote POJO that exposes the business logic deployed on the server (from previous step),
<destination id="remoteJavaApp"> <properties> <source>com.summa.FlexSpringInterfaceService</source> <scope>application</scope> </properties> </destination>
The root tag for remoting-config.xml looks like,
<service id="remoting-service" class="flex.messaging.services.RemotingService">
This RemotingService class will handle all the calls made to all remote destinations by invoking the appropriate adapter. The adapter is responsible for invoking methods on the Java object based on the source tag and returns the result. If the destination does not have a specified adapter, the default adapter from remoting-config.xml will be used.
d) Flex provides a RemoteObject component that may be added to your MXML or ActionScript file and allows you to call methods on the Java object associated with the corresponding destination.
<mx:RemoteObject id="remoteService" showBusyCursor="true" destination="remoteJavaApp"> <mx:method name="findAllUsers" /> <mx:method name="addUser" /> <mx:method name="editUser" /> <mx:method name="delUser" /> </mx:RemoteObject>
e) Everything else is transparent from the user’s point of view (including the conversion between ActionScript and the Java objects and the serialization mechanism that can handle complex objects graph). You will only need to declare the mapping between the Action Script and the Java objects using the [RemoteClass] metadata such as:
package com.summa.vo { import mx.collections.ArrayCollection; [RemoteClass(alias="com.summa.entity.User")] [Bindable] public class User { public var id: Number; public var username: String; public var password: String; public var firstName: String; public var lastName: String; public var userBankAccounts: ArrayCollection; } }
The mapping between Action Script and Java objects is critical not only from implementation perspective but also from maintenance perspective. Depending upon the implementation, it may be a good idea to have the Action Script class map to the database entity via an additional layer of Value Objects defined on the server side. Although this will require translation of the additional value object to the actual database entity but this would cause minimal or no impact on the client implementation in case of any non UI specific database and/or business logic change.
f) The folder named “lib” under blazeds.war\WEB-INF contains all the jar files needed by BlazeDS. Copy these libraries into your WEB-INF\lib folder. Keep the latest version of the jar files in case of conflict with existing files.
g) Update channel definition in service-config.xml to point to appropriate address and port number.
<channel-definition id="my-amf" class="mx.messaging.channels.AMFChannel"> <endpoint url=http://localhost:8080/flexSpringJPA/messagebroker/amf class="flex.messaging.endpoints.AMFEndpoint"/> </channel-definition>
You can also turn on the debugging in service-config.xml in case of any errors.
h) Add the following entries into your web.xml file (taken from BlazeDS 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> <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> <init-param> <param-name>flex.write.path</param-name> <param-value>/WEB-INF/flex</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>
The diagram below depicts the interactions between the flex based UI client application, BlazeDS and the server side implementation which is currently encapsulated under business logic box. This service implementation may be a POJO java class, Spring service, or EJBs etc.
An interesting aspect of working with RIAs is how you design your data objects with respect to the user interface. For instance a User might have a collection of Roles, each of which in turn may have a collection of permissions. In such cases, you may either design many granular functions to return desired data sets in multiple requests or return all the data in one call by joining multiple tables in your query. The later method requires less code and is easier to maintain but results in a huge performance hit due to higher consumption of network bandwidth and local resources. Lazy loading, offered by most ORM frameworks, can help in such cases as it lets you define complete object model, such as a User with an array of Roles, but only return the data you are actually using in your presentation layer.
In case of Flex based RIAs, the lazy loaded remote Java objects are slightly trickier to handle with remote object services than using REST and SOAP web services. Since BlazeDS handles your data transformation implicitly, it tries to serialize the persistent object including the data members that are marked for lazy loading. This will result in a LazyLoadingException because the underlying database connection or service transaction session has already been closed (which would always be the case in a serious Service/DAO implementation). In order to resolve this, all data members that are marked for lazy loading should explicitly be set to null (in recursive manner) in the service layer to avoid this exception.
For example, in case of One-To-Many mapping between User and Role entity with the User entity containing a collection of Roles, if this collection is set for fetch type of lazy load, the service layer must set this collection to null to stop BlazeDS from trying to build this collection by going back to the service layer. Note that in such an implementation, the Role entity may have a reference to the User entity as well and in scenarios where Role collection is eagerly fetched within a User object, the User object in each of the Role in the collection must be explicitly set to null to avoid the same problem. This problem can be minimized if the Role entity is not required to contain a reference to User entity.
Another thing to keep in mind while using BlazeDS is that in order to run the test cases during development, the developers will need to have their server running at all times. Whereas in case of REST and SOAP based web services, a hard-coded response XML file may suffice for testing purposes.