Fdms Tutorial
Fdms Tutorial
By Christophe Coenraets
Last Update: January 11th 2007
Introduction
The Flex Data Management Services automate the process of synchronizing data between the client
application and the middle-tier. The changes made to the data at the client-side are automatically sent
to a service running in your application server. This service then passes the changes to your business
layer or directly to your persistence layer, whatever your persistence solution is: DAOs with straight
JDBC calls, Hibernate, EJBs, JPA, iBatis, or any other solution. In other words, your client
application is freed of any data synchronization code. Code you no longer have to write includes:
1. Keeping track of all the items created, updated, and deleted by the end-user at the client-side.
2. Keeping track of the original value of the data as initially retrieved by the client. (The original
value is often needed by the persistence layer to implement optimistic locking).
3. Making a series of RPC calls to send changes (creates, updates, deletes) to the middle-tier.
4. Handling the conflicts that may arise during this synchronization process.
Depending on the application you are building, using the Flex Data Management Services leads to
~80% less data manipulation code at the client side.
The goal of this tutorial is to get you started quickly with the Flex Data Management Services.
Prerequisites
This tutorial assumes that you meet the following prerequisites:
• Knowledge of server-side Java development and the structure of a J2EE web application
• Familiarity with SQL and JDBC
• Basic experience with Flex development
• Basic experience with Eclipse
Setting up your environment
Step 1: Install FlexBuilder 2.0.1
Refer to the following instructions to install FlexBuilder 2.0.1:
http://www.adobe.com/support/documentation/en/flex/2/install.html#installingfb2
1. Make sure that you have the JDK 1.5 or higher installed, and that you have a JAVA_HOME or
JRE_HOME environment variable pointing to your Java Development Kit installation.
Note: The JDK 1.5 is a Tomcat 5.5 requirement, not an FDS requirement. The FDS requirement is
JDK 1.4 or higher.
2. Download fds-tomcat.zip
3. Expand fds-tomcat.zip
Note: The instructions in this document assume that you expand fds-tomcat.zip in C:\, which
creates a directory called fds-tomcat at the root level. You can expand fds-tomcat.zip anywhere
else. Just make sure you adjust the path in the tutorial instructions accordingly.
4. Start Tomcat
catalina run
To allow you to run the tutorial “out-of-the-box” without setting up a database, the Test Drive
server includes an HSQLDB database. HSQLDB is a lightweight Java RDBMS that is particularly
well suited to run samples. The HSQLDB database server is automatically started as part of the
Tomcat startup process.
Step 3: Create a Java project
There are several ways you can set up Eclipse to work on the Java classes of a Web application. You
can use a simple Java project, work with the Web Tools Platform (WTP), or other plugins like
MyEclipse. In this tutorial, to avoid dependencies on a specific plugin, we use a simple Java project.
2. Select “Java Project” in the project type tree and click Next
This project configuration allows you to store the source code for your Java classes in WEB-
INF\src directory. These classes will automatically be compiled in WEB-INF\classes.
Step 4: Create a Flex project
1. In the Eclipse menu, select File > New > Project
2. Select “Flex Project” in the project type tree and click Next
3. Fill in the “Create a Flex Project” page of the wizard as follows and click Next
4. Fill in the next page of the wizard as follows and click Next
5. Fill in the next page of the wizard as follows and click Finish
Creating the Server Side
The changes made to the data at the client-side are automatically sent to a data service running in the
application server. The data service then passes those changes to an object called the assembler. The
role of the assembler is to pass the changes, in the appropriate format, to your existing business
objects, or directly to your persistence objects. The assembler class is the only class that you have to
write at the server-side (in addition to your existing business and persistence objects), and is typically
a very simple class.
2. In the fds-tomcat project, right-click the flex.tutorial.fdms package under src, and select New >
Class. Specify ProductAssembler as the class name and click Finish. Define the ProductAssembler
class as follows:
package flex.tutorial.fdms;
import java.util.List;
import java.util.Collection;
import java.util.Map;
import flex.data.DataSyncException;
import flex.data.assemblers.AbstractAssembler;
}
Code highlights:
<destination id="fdms-tutorial-product">
<adapter ref="java-dao" />
<properties>
<source>flex.tutorial.fdms.ProductAssembler</source>
<scope>application</scope>
<metadata>
<identity property="productId"/>
</metadata>
</properties>
</destination>
This XML fragment essentially maps the logical name for the destination (fdms-tutorial-
product) to an assembler class (flex.tutorial.fdms.ProductAssembler).
4. Make sure you saved ProductAssembler.java and data-management-config.xml, and restart the
server
Creating the Client Application
Step 1: Creating a basic client
1. Make sure Eclipse is in the Flex Development perspective
2. Right click the fdms-tutorial project and select New > ActionScript class. Enter Product as the
class name and click Finish. Define the Product class as follows:
package
{
[Managed]
[RemoteClass(alias="flex.tutorial.fdms.Product")]
public class Product
{
public var productId:int;
Code highlights:
</mx:Application>
Code highlights:
Note: Automatically pushing the changes made by one client to the other clients working on the
same data is the default behavior of FDS. We will see how this default behavior can be changed
later in this tutorial.
Step 2: Controlling when changes are sent to the server
By default, FDMS sends a change message to the server immediately after you change a property of
an object. This behavior is appropriate for some applications (for example, a collaborative form-
filling application), but might not be desirable in other applications, because:
For these reasons, you often want to programmatically control when the changes are sent to the
server.
b. In the ControlBar, just after the Delete button, add an “Apply Changes” button defined as
follows:
2. Now that we have control over when the changes are sent to the server, we can also handle the
creation of new products. In the ControlBar, after the “Apply Changes” button, add a “New”
Button defined as follows:
Note: This logic for creating new products would have failed with autoCommit set to true
(default) because the server-side component would have tried to insert an empty row and some
columns of the product table are defined as not supporting null values.
1. To change the default behavior, and prevent the DataService from receiving real time data
changes: add autoSyncEnabled="false" to the DataService declaration.
Note: With autoSyncEnabled set to false, the chances that you are trying to update stale data increase.
For example, you may be trying to update a product that another user has already deleted, or modified
in an incompatible way. This is referred to as a “conflict”. FDS provides a conflict resolution API that
we explore later in this tutorial.
Step 4: Single Item vs Batch Updates
In many data management applications, the data entry process happens in a form where updates are
performed one item at a time. In this section, we modify the inventory application to allow the user to
enter data in a form (rather than directly in the DataGrid), and apply changes one product at a time.
2. Right click the fdms-tutorial project and select New > MXML Component. Enter ProductForm as
the Filename and click Finish. Define ProductForm.mxml as follows:
<Product id="product"
name="{productName.text}"
category="{category.text}"
price="{Number(price.text)}"
qtyInStock="{Number(qty.text)}"
image="{image.text}"
description="{description.text}"/>
<mx:FormItem label="Name">
<mx:TextInput id="productName" text="{product.name}" width="250"/>
</mx:FormItem>
<mx:FormItem label="Category">
<mx:TextInput id="category" text="{product.category}" width="250"/>
</mx:FormItem>
<mx:FormItem label="Image">
<mx:TextInput id="image" text="{product.image}" width="250"/>
</mx:FormItem>
<mx:FormItem label="Price">
<mx:TextInput id="price" text="{product.price}" width="70"/>
</mx:FormItem>
</mx:Form>
3. In inventory.mxml, add the following script block at the beginning of the file, just after the
<mx:Application> tag
<mx:Script>
<![CDATA[
]]>
</mx:Script>
7. Change the “New” button click handler: replace products.addItem(new Product()) with a call to
the newItem() function
8. Remove the “Delete” and “Apply Changes” buttons from the Product List Panel
9. Add the following Panel immediately after the “Product List” Panel.
For example, open ProductDAO.java and examine the difference between the update() and the
updateStrict() methods:
• In the update() method, the WHERE clause is built using the primary key only. As a result, your
update will succeed even if the row has been changed by someone else since you read it.
• In the updateStrict() method, the WHERE clause is built using the primary key and by verifying
that each column still has the same value it had when you read the row. Your update will fail if the
row has been changed by someone else since you read it.
Note: Another way of implementing the same concurrency level without adding all the columns to
the WHERE clause is to add a “version” column to the table. Every time the product is updated,
the product’s version is incremented. Using this approach, you can make sure a row has not been
updated since you read it by building the WHERE clause using the primary key and the version
column only.
The level of concurrency you choose depends on your application. FDMS doesn’t force you to
implement locking in any particular way, and doesn’t even attempt to identify when a conflict has
occurred. You are free to define what represents a conflict in the context of your application. You tell
FDMS when a conflict has occurred by throwing a DataSyncException. For example, open
ProductAssembler.java and examine the updateItem() method. Notice that the method throws a
DataSyncException when the update fails in the DAO (no product matched the WHERE clause).
At the client-side, FDMS provides a sophisticated conflict resolution API. The DataService’s
“conflict” event is triggered when a conflict occurs. The conflict resolution API provides several
options to handle the conflict as appropriate in the event handler.
In this section, we will use the updateStrict() method of ProductDAO because it makes it easier to
generate conflicts and thus experiment with conflict resolution.
1. Open ProductAssembler.java. In the updateItem() method, replace dao.update(…) with
dao.updateStrict(…). Recompile ProductAssembler.java and restart your application server.
2. In inventory.mxml, add the following import statements at the beginning of the <mx:Script>
block, just after <![CDATA[
import mx.rpc.events.FaultEvent;
import mx.data.events.DataConflictEvent;
import mx.data.Conflicts;
import mx.data.Conflict;
import mx.controls.Alert;
Note: acceptServer() represents one approach to resolve conflicts. Refer to the documentation to
explore the other options.
fault="faultHandler(event)" conflict="conflictHandler(event)"
Based on how we wrote the persistence logic at the server side, this is considered a conflict.
The client application uses the conflict resolution API to revert to the current server value of
the object.
Step 6: Supporting multiple fill queries
In the current version of the application the Product List always displays all the products. In real life
applications, you often need to allow the user to specify one or more search criteria to bring back a
subset of items. The fill() method allows you to pass a list of arguments that you can use as query
parameters at the server-side. For example, if you wanted to support the ability to search products by
name, you could modify the implementation of the fill() method in ProductAssembler.java as follows:
This implementation however isn’t very flexible. Imagine, for example, that you have to provide the
ability to search by name and by category. If provided with a single String argument, it would be
impossible for the assembler’s fill method to determine if the user wanted to search by name or by
category (since both searches would take one argument of type String). A good solution to this
problem is to work with named queries, using the first argument passed to the fill method as the query
name.
3. In inventory.mxml, add a TextInput inside the Product List Panel’s ControlBar (after the “New”
button) as follows:
5. Test the application. For example, type 3100 in the TextInput and click the Search button
Step 7: Single Objects vs Collections
In the current version of the application, you are always working on collection objects, even when you
update a single product in the form. As a result, if the list goes away (for example because you
execute a new search), the object you were working on in the form goes away too.
FDMS allows you to work with collections of objects or single items. In this section, we change the
inventory application to use the single item approach when manipulating a product in the form.
import mx.data.ItemReference;
[Bindable]
private var itemReference:ItemReference;
change="productChange(dg.selectedItem as Product)"