ColdFusion AIR Synchronization

First, let me just cop to the fact that this post draws a lot from Jayesh Viradiya’s post on this ColdFusion/AIR Offline support. I had to simplify his stuff to wrap my mind around it enough to talk about it. So hats off to Jayesh, he’s done some awesome work here.

I was doing a demo on ColdFusion and AIR integration at the keynote for CFUnited, and I figured I would go into a little more detail here.

First off, to call it “ColdFusion and AIR integration” is to do it a bit of a disservice. What our engineers have accomplished is nothing short of “ORM for AIR.” It handles:

  • Creating and maintaining the SQLite database
  • Saving objects to SQLite without SQL statements
  • Handling relationships:
    • one-to-one
    • one-to-many
    • many-to-one
    • many-to-many
  • Syncing those records back to ColdFusion

So I have a sample application that shows a basic demo of this, without the relationships. Jayesh’s demo has the relationships. I’ve attached the code here, so if you want to look at it, just download the whole thing. Otherwise, let me take you through it. One little note, this stuff was written for ColdFusion 9 Beta. There are some bugs. I’ll point them out where I can, and assure you that the bugs are being worked on.

So let’s start in the ColdFusion. First I define an ORM cfc:

component persistent="true"{
property name="personID" fieldtype="id";
property name="firstName";
property name="lastName";
property name="twitter";

public string function serial(){
return "#This.getFirstName()#|#This.getLastName()#|#This.getTwitter()#|#This.getpersonID()#";

Pretty straightforward; then I define a corresponding ActionScript class:

package airdemo
public class Person
public var personID:int;
public var firstName:String;
public var lastName:String;
public var twitter:String;

Note the RemoteClass mapping to the CFC on the back end. Now let’s go to the application. I have a simple data grid and form designed to show and edit the details of these person objects. I’ll skip that and go right to the part where I hook up AIR to the ColdFusion server:

private function init():void
// Provide Credentials for Server side Connection and CFC
syncmanager = new SyncManager();
syncmanager.cfPort = 80;
syncmanager.cfServer = "";
syncmanager.syncCFC = "AirDemo.personManager";

// THis handler will be called when any COnflict
// occurs while writing back changes on serverside
syncmanager.addEventListener(ConflictEvent.CONFLICT, conflictHandler);

//Kick off the application.


This connects this application to the ColdFusion server on port 80 and wires the syncmanager to AIRDemo.personmanager. More on that later. But it also kicks off getRemote which takes care of populating this application with data from the server. So getRemote() fires:

//GET records from BACKEND SERVER
private function getRemote():void{
var token:AsyncToken= syncmanager.fetch("fetch");
token.addResponder(new mx.rpc.Responder(fetchSuccess, fetchFault));

Syncmanager.fetch calls the fetch method of the ColdFusion CFC we set as syncmanager.syncCFC above. That method just retrieves the records using ColdFusion ORM.

remote Array function fetch(){
return EntityLoad('person');;

In most cases it is successful, in which case fetchSuccess calls createLocalDB:

//CREATE the actual SQLite DB
private function createLocalDB():void{
//Create a pointer to actual SQLite db file
dbFile = File.userDirectory.resolvePath("AirDemo.db");

var sessiontoken:SessionToken =syncmanager.openSession(dbFile,017916);
sessiontoken.addResponder(new mx.rpc.Responder(connectSuccess,connectFault));

In most cases that is successful and connectSuccess calls createLocalCacheFromRemote

//PUT records from BACKEND SERVER in SQLite DB
private function createLocalCacheFromRemote():void{
var savetoken:SessionToken = session.saveUpdateCache(pc);
savetoken.addResponder(new mx.rpc.Responder(saveCacheSuccess, savefault));

Those three ActionScript functions in concert with the ColdFusion one:

  • Got the data from the ColdFusion server
  • Created a local SQLite database for the data
  • Populated the local SQLite data with that data

Okay, so now I go through the application, update some data, and want to save it back to the SQLite database:

//SAVE to SQLite DB
private function saveLocal():void
//Generate person object from form.
var person:Person = convertFormToObject();

//session.saveUpdate saves record to SQLite Database
var savetoken:SessionToken = session.saveUpdate(person);
savetoken.addResponder(new mx.rpc.Responder(saveSuccess, savefault));

ConvertFormToObject does exactly what it sounds like, converting values from a form into a Person object, then session.saveUpdate() handles saving the record back to the SQLite store. No SQL required. Then we need to send it off to the ColdFusion server:

private function saveRemote():void
var committoken:SessionToken = session.commit();
committoken.addResponder(new mx.rpc.Responder(commitSuccess, commitFault));

That’s it. That transmits all of that changes made in this session to the ColdFusion server, where it is processed:

remote any function sync(required array operations,
required array clientobjects,
array originalobjects = ArrayNew(1)){

var i= 0;
var conflicts = ArrayNew(1);
var conflictcount = 1;

for (i=1; i <= ArrayLen(operations); i++ ){

var operation = operations[i];
var clientobject = clientobjects[i];
var originalobject = originalobjects[i];

if (operation eq "INSERT" ){
var obj = ORMGetSession().merge(clientobject);

if (isinstanceOf(originalobject,"person")){
var serverobject = EntityLoadByPK("person",originalobject.getpersonID());
throw "Invalid Object";

if (not isdefined('serverobject')){
var conflict = New CFIDE.AIR.conflict();
conflict.clientobject = clientobject;
conflict.originalobject = originalobject;
conflict.operation = operation;
conflicts[conflictcount++] = conflict;

var isNotConflict = (originalobject.serial() eq serverobject.serial());
if (isNotConflict){
if (operation eq "UPDATE"){
obj = ORMGetSession().merge(clientobject);
else if (operation eq "DELETE"){
obj = ORMGetSession().merge(originalobject);
var text="is a conflict";
var conflict = New CFIDE.AIR.conflict();
conflict.clientobject = clientobject;
conflict.originalobject = originalobject;
conflict.operation = operation;
conflicts[conflictcount++] = conflict;
if (conflictcount gt 1){
return conflicts;

So this is a lot of code, but basically it performs the following steps:

  • Check to see if the record is new
    • If so insert it
  • Then check to see if the update is in conflict
    • If not, delete or update accordingly
    • If so, send a conflict back to the AIR client

Now back in the client you have to handle the conflict, in this version of the application, I just replace the client details with the server details:

public function conflictOverwrite(conflicts:ArrayCollection):void{
var token:SessionToken = session.keepAllServerObjects(conflicts);
token.addResponder(new mx.rpc.Responder(conflictSuccess, conflictFault));

Again, one function, session.keepAllServerObjects, handles overwriting everything on the client.

So that is the gist of what I was going to show at CFUnited. The code is attached, (Down at the bottom there is a “download” link) feel free to give it a try and see what you can do with it.

4 thoughts on “ColdFusion AIR Synchronization

Leave a Reply

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s