Remote Synthesis
Search my blog:
Viewing By Entry / Main
Feb 14, 2008

Using Cairngorm in Adobe AIR

I have read in various places that the Cairngorm framework for Flex isn't really designed for working with AIR. Therefore, I approached a recent project with some hesitation since that is exactly what I intended to use. First of all, not only are we standardized on Cairngorm where I work, but I, personally, appreciate the way it helps me organize and structure my application. Now that I am finished with the proof-of-concept application, I can say that, while it is a small application, I didn't really run into any major issues using Cairngorm with AIR. I did have to make one minor adjustment, however, which I will discuss along with my experience.As an architecture, Cairngorm is designed around the idea that you will be getting your application data via RemoteObject calls. While these calls can play an important role in your AIR application, so will various file system, local database and other AIR specific events. The standard Cairngorm process goes something like:

View --> Event --> Command --> Delegate --> RemoteObject --> Command (result or fault)

At the point of the result or fault from the RemoteObject you would probably set data on the model based upon the data returned from the external call. A standard command class implements the IResponder, which is returned as the result of a RemoteObject call, and expects an IResponder as the argument within the result() and fault() methods. Also, the Delegate class is really just for making your RemoteObject calls. If you are making external RemoteObject calls in AIR, this process doesn't change at all (one note here: I did need to add a specific endpoint to my RemoteObject pointing to my external server's flex2gateway to override the standard endpoint for the pre-defined ColdFusion destination). However, if you are working with the file system, a SQLLite database or the connection monitor - all of which I used on this project - the standard process doesn't exactly work.

Initially, I was building the application whereby only my RemoteObject calls were announced as standard Cairngorm events and everything else was handled within the MXML, but that didn't make sense, isn't a scalable architecture and defeats the purpose of using Cairngorm at all. In the end, I decided to run everything through the standard Cairngorm event process but these processes would end in the command like follows:

View --> Event --> Command --> Command (result or fault)

In order to do this however, I had to modify the command so that it no longer implemented the IResponder. This allowed me to modify the result and fault methods to accept things like the StatusEvent for the URLMonitor result or the SQLEvent for the SQLStatement result without receiving compiler errors. Beyond that my development within Flex and Cairngorm remained the same.

One handy addition that I found was making the event that checked whether the user was connected to the Internet using the URLMonitor flexible enough to be tied to any other event that required Internet connectivity. This was because, while only a limited number of events required connectivity, I didn't bother checking it unless it was necessary, since my application could be used day-to-day without necessarily ever connecting to external data (most of the day-to-day data was copied to the local database). The process would be something like this:

User triggers event that requires connectivity --> check connectivity --> announce initial event

There is nothing groundbreaking here, but what I decided to do was to have a property that was typed as a generic Cairngorm event on what I called my CheckConnectionEvent. Thus the result() method of CheckConnectionEvent simply announced whatever event was passed via the CairngormEventDispatcher. In this way, I could use the CheckConnectionEvent prior to any event that required a connection. While in this case I was fine with handling all faults in a generic manner, this event could be refactored down the road to allow you to specify an event to run on result and on fault. As I said, it's not groundbreaking (and I am sure I am not the first one to do this) but a useful tip for someone getting started with AIR and Cairngorm.

Lastly, and unrelated to Cairngorm, I want to thank both Saskovic and Jeffry Houser (who is speaking, by the way, at next week's Boston CFUG meeting). Their code for minimizing to the system tray worked flawlessly and without modification.

Comments
jwopitz
Great post here.

I haven't really done much with AIR just yet. But the concept of mod'ing a Cairngorm command to do local logic is a great concept. It a) retains separation for MVC constituents, b) retains logical package structure for command-based logic, c) reduces view file sizes because they don't have local command logic and d) makes it a breeze to update as it is in-line with the whole concept of OOP.

Cairngorm is a micro-architecture, yet people still pigeon-hole the thing as this rigid structure. Its very flexible and was intended to be so (no pun intended)

I have been using this approach for quite some time on my pet-projects. I generally do my application initialization via the following steps:

1. application shell loads via Flex built in loading (ala. standard preloader as does any Flex app)
2. app loads necessary content and settings via AppCoreLib - http://code.google.com/p/appcorelib/(shameless plug, sorry)
3. shellInitialized call fires event that triggers InitializeApplicationData which is a ICommand (not IResponder since we are only executing). All model data providers and settings are then set. There is no response or fault. It just happens.


Joe Rinehart
Hey Brian,

I've used Cairngorm quite effectively with AIR, but I use a different approach that retains the "detachedness" of the Delegate and allows the normal view -> event -> command -> delegate -> command flow. The "remote object" shouldn't be part of the equation at all, as the delegate, by definition, should hide its existence...

One difference between how I approach Cairngorm and how it's commonly viewed is that I isolate all knowledge of the backend (is it remote? Is it local? etc.) in my delegate implementations (see http://www.firemoss.com/blog/index.cfm?mode=entry&entry=6D96F27D-3048-55C9-436725DC9CEF7549).

This lets me create something like an IContactDelegate that can save/list/etc. contacts, and implement it with RemoteObjectContactDelegate, SQLListContactDelegate, etc.

When I want to use a delegate, I instantiate the appropriate one (knowledge of that in a factory), handing it a responder that calls back to my local Command in a "neutral" (not specific to concrete delegate impl.) manner.

In code, I'd have this in ListContactCommand:

public function execute()
{

var responder:Responder = new Responder(this.resultHandler, this.faultHandler);

// Note that this may return something that works with RemoteObject, SOAP, local DB, whatever...
var del:IContactDelegate = ContactDelegateFactory.create(responder);

del.listContacts();
}

// callback for success, invoked by delegate doing responder.result(contacts), expects Array of Contact
public function resultHandler(result:Array)
{
// stuff
}


// callback for fault, invoked by delegate doing responder.fault(event)
public function resultHandler(result:Event)
{
// stuff. may used custom fault event wrapping real, etc.
}


david_deraedt
Hello, thanks for this post ;)

I'm not sure you can say that Cairngorm was built around the idea that you will be getting your application data via RemoteObject calls. It just gives some tools (the BusinessDelegate and ServiceLocator, namely) to communicate with an application server using an AbstractService, whether it is RemoteObject, WebServices, HTTPService, or even a lower level mechanism. Their use is not mandatory, you just use it if you need it.

I think that most of what you describe in the first part of your post has little to do with AIR. The question is : does the application has to communicate with the outside world. Whether it's a Flex or AIR application does not matter. A Flex application may have no communication with any application server, and an AIR application may rely heavily on those communications.


Brian Rinaldi
@David - You are correct. It is more than just RemoteObject calls, that was a bit of an oversimplification on my part for a beginner level discussion. I probably should have clarified. It does generally assume the data in general is external (which makes sense given the nature of Flex). So, theoretically, I suppose yo *could* have a Flex application that "has no communication with the outside world", although in reality that would be extremely rare.


Write your comment



(it will not be displayed)