Blog

Entries Tagged as AIR

ColdFusion AIR Synchronization

August 21, 2009 · 3 Comment s

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
{
   [Bindable]
   [RemoteClass(alias="AIRDemo.person")]
   [Entity]
   public class Person
   {

      [Id]
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 = "centaur.dev";
   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.
   getRemote();
      
}

This connects this application to the ColdFusion server centaur.dev 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:

//SAVE to BACKEND 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);
            EntitySave(obj);
         }
         else{
         
            if (isinstanceOf(originalobject,"person")){
               var serverobject = EntityLoadByPK("person",originalobject.getpersonID());
            }
            else{
               throw "Invalid Object";   
            }   
         
            if (not isdefined('serverobject')){
                  var text="CONFLICT::SERVER OBJECT NOT FOUND, RECORD MAY BE DELETED ALREADY";
                  var conflict = New CFIDE.AIR.conflict();
                  conflict.clientobject = clientobject;
                  conflict.originalobject = originalobject;
                  conflict.operation = operation;
                  conflicts[conflictcount++] = conflict;
                  continue;
            }
            
            var isNotConflict = (originalobject.serial() eq serverobject.serial());

            if (isNotConflict){
                  if (operation eq "UPDATE"){
                     obj = ORMGetSession().merge(clientobject);
                     EntitySave(obj);
                  }          
                  else if (operation eq "DELETE"){
                     obj = ORMGetSession().merge(originalobject);
                     EntityDelete(obj);
                  }
            }
            else{
                  var text="is a conflict";
                  var conflict = New CFIDE.AIR.conflict();
                  conflict.clientobject = clientobject;
                  conflict.originalobject = originalobject;
                  conflict.operation = operation;
                  conflicts[conflictcount++] = conflict;
                  continue;
            }
         }
      }
      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:

//OVERWRITE from BACKEND SERVER
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.

3 Comment s Tags: ColdFusion · AIR

ColdFusion + AIR applications

February 25, 2009 · 6 Comment s

I'm just getting started into this evangelist thing, and I'm having trouble figuring out what AIR applications out there are being powered by ColdFusion on the backend. The only one that comes to mind for me at the moment is Broadchoice.

Can the ColdFusion community help me find the following?

AIR applications using either Flex or HTML/JS that use ColdFusion for any part of their backend

Please send URL's and any commentary to the comments of this page. Alternately, contact me through my contact page if you're willing to help, but can't talk about these things publicly.

6 Comment s Tags: ColdFusion · Flex · AIR

DMD Project at Carnegie Mellon University

February 11, 2009 · No Comments

I got to see a very cool use for Flex and AIR in Higher Education today. It's the DMD Project from the Masters in Human-Computer Interaction Program at Carnegie Mellon University.

It's a prototype RIA for patient management at dental offices. It was designed for touch screens, but also can be used with the traditional keyboard and mouse interface as well. It shows everything dentists, hygienists, and front office staff would need to manage the entire patient interaction.

I got a chance to talk with one of the developers, Jaanus Kase, about it. He told me that they spent most of their time upfront researching how exactly dentist's offices would need to interact with a system. That yielded a few custom interfaces, including the "radiograph view" which the rest of us just know as "an x-ray of all of your teeth." The other cool thing I noticed was the high emphasis they place on important information like "Severe Penicillin Allergy." Clearly, this program really does focus on quality computer-human interactions.

The project itself is a prototype; don't expect to see it in your dentist's office anytime soon. But the app is available as an AIR application for your review.

Jaanus is done with the Masters program now, and is working with an startup in New York named World Evolved.

No Comments Tags: Flex · AIR · Higher Ed

New AIR Marketplace

February 10, 2009 · 1 Comment

The Adobe AIR Marketplace has been updated. If you are not familiar with it, the market place serves two roles:

  • Allows AIR developers a place to showcase their apps
  • Allows everyone else to easily find AIR applications

New features include:

  • New look and feel
  • Community features for sharing applications and publicly evaluating them
  • New tools for developers to track how their apps are performing

Link: http://airmarketplace.adobe.com

1 Comment Tags: AIR · Adobe

...Hello Adobe

January 16, 2009 · 40 Comment s

Wow that took less time than I thought... So yeah, I'm joining Adobe. Specifically I'm joining Adobe's Platform Evangelism group. I'll be working under Kevin Hoyt with the team that includes Ryan Stewart, Lee Brimelow and Danny Dura amongst others and ultimately headed by Ben Forta. So it will take all of my composure to not, you know, break down into an Adobe fanboy in my first staff meeting.

What does that mean? It means that I will be working with the rest of the team to spread excitement about the Adobe Platform Products:

  • Flash
  • Flex
  • Air
  • ColdFusion
  • LiveCycle
  • Flash Catalyst
  • BlazeDS

I'll be promoting the entire platform, but considering my experience to date, I imagine that I'll start with a slight focus on ColdFusion and AIR.

However in addition to that focus I will have an overriding goal:

Get Adobe Platform Technologies taught in the classrooms of Higher Ed.

It's a big goal, and not a trivial challenge. I see a lot of different paths to achieving it. I can't wait to work with all of you to accomplish it.

And as my first act of Evangelism I will remind you once again that both ColdFusion and Flex Builder are available free to Higher Education. All you have to do is go to one of their respective "freeriatools" sites, fill out a form, and upload a picture of your Academic ID.

40 Comment s Tags: ColdFusion · Flex · AIR · Higher Ed · Adobe

Webmaniacs 2008 - AIR and SQLite

May 21, 2008 · 5 Comment s

Yesterday, I presented on AIR and SQLite (for HTML and JavaScript developers.) I've posted a zip file of all of the content, including the presentation up on my site if anyone wants to download it.

I also included the source for AboutTime, my approximate clock application.

5 Comment s Tags: Web Development · ColdFusion · AIR

Yet Another Update to CFAir

November 21, 2007 · No Comments

I made a little update to the CFAir compiler package. I tweaked the generated application to be a little more complicated, and CFAir still works on it. There's a screenshot to the right (If you're own my site.)

No Comments Tags: Web Development · ColdFusion · AIR

CFAIR Compiler Update

November 14, 2007 · 2 Comment s

I made some changes to the CFAIR compiler project.

  • Added some documentation
  • Fixed a bug that caused the first run of the app to not create an air file.
  • Made sure that images referenced by ColdFusion CSS are imported

Additionally I added a screenshot of the image to the RIAForge site.

So check out CF Air Compiler, and please let me know what you think of it. Is this worth developing further? Or should I just wait for Centaur? Anybody have any suggestions or ideas on how to proceed?

2 Comment s Tags: Web Development · ColdFusion · AIR

CF Air Compiler

November 10, 2007 · 2 Comment s

I was inspired by Ben Forta's post on creating AIR application from ColdFusion. I felt like there had to be a better way than just copying the source of ColdFusion generated HTML files. So I started working with it and came up with a proof of concept project on RIAForge: CF Air Compiler.

I wrote a simple application called "testapp" that just binds a CFGrid to the results of a CFC call that returns a CFFeed of coldfusionbloggers. This is the application that will be packaged into an Air application.

I used CFHttp to grab the ColdFusion page outputted as HTML and move them to a repository. Then I used ReMatch to find all of the used CFIDE javascript files, css files, and images, and copy them to the repository. Then I used ReMatch to grab all of the cfc calls and convert them to fully qualified url calls.

The second part was a little easier. All it required was wrapping the adt.bat in a CFC wrapper. It just has two methods: certificate and package. They correspond to the options for the adt.bat file. I used that to compile the application into AIR.

There were a few challenges with it. I haven't figured out a way to get to the images used in CSS. I could just grab them indiscriminately, but I want to do a little better, expect an update after I think about it for a bit. Also the CF JavaScript evidently tries to do some things that violate the security model of the Air Runtime. It required wrapping the ColdFusion generated html in an iframe. To read more about the issue, you can check out the Air Wiki.

It's not perfect code, but I figured I would at least get it out there and maybe someone smarter could take it a little further. Check it out, please feel free to offer suggestions and criticism.

2 Comment s Tags: ColdFusion · AIR