The Kiwi Team is Hiring!

Interested in working on a cutting-edge, Flex-based, “Web 2.0″ project? Interested in building one of the first Apollo apps from the ground up? The Kiwi team is currently interviewing candidates for a senior engineering position. If you’ve got solid Java/OO skills (Flex experience is obviously a plus) and live in the SF Bay Area, we want to hear from you! You can find the job description posted here (search for “kiwi”, then click on “Computer Scientist”).

Loading Flex Skins at Runtime

,,

There are lots of great resources out there on how to skin your Flex applications.  Quick recap: Flex supports two approaches to skinning:  graphical and programmatic.  Graphical skinning involves creating graphical assets in Flash, Photoshop, Fireworks, etc. and embedding them in your Flex application.  Programmatic skinning involves creating an ActionScript class which defines the skin for a control.  As you might guess, graphical skinning is easier, programmatic skinning more powerful.

One drawback to both approaches is that the skinned resources (SWF/PNG/GIF/etc. for graphical skins, the AS class file for programmatic skins) must be available at compile-time for the skins to be applied.  Or do they?  In this post I’ll describe a neat trick for pulling in graphical skins at run-time (using a live demo with source code).

To make this example as simple as possible, I’m going to create a Flex app that allows the Button control to be dynamically skinned.  This app will pull down a skinning SWF at runtime, load the skins, and apply them to the Button control.  Again, to keep it simple I’m going to use the skin template file provided in NJ’s skinning article, and apply the RadioButton skins to the Button control.  (A tip of the cap to Roger Gonzalez and NJ for basically coming up with this solution.)

Step #1: Create a wrapper SWF for the skin assets

The skin assets are in the aforementioned skin template file.  I want to create a wrapper SWF that my Flex app can load at runtime and from which it can extract the appropriate assets, in this case the four symbols for the RadioButton control.  Here’s the source for wrapper SWF:

package
{
  import flash.display.Sprite;

  public class Wrapper extends Sprite
  {
    [Embed(source="flex_skins.swf",symbol="RadioButton_upIcon")]
    public var rbUpSkin: Class;
    [Embed(source="flex_skins.swf",symbol="RadioButton_downIcon")]
    public var rbDownSkin: Class;
    [Embed(source="flex_skins.swf",symbol="RadioButton_disabledIcon")]
    public var rbDisabledSkin: Class;
    [Embed(source="flex_skins.swf",symbol="RadioButton_overIcon")]
    public var rbOverSkin: Class;
  }
}

Step #2: Put the wrapper SWF on your web server

The Flex app needs to load the wrapper SWF from somewhere!

Step #3: In your Flex app, use a Loader to load the wrapper SWF

I created a utility class called ClassLoader to wrap up functionality related to loading the SWF and extracting the class.  Here are the key lines:

loader = new Loader();
loader.contentLoaderInfo.addEventListener(Event.COMPLETE, completeHandler);
loader.contentLoaderInfo.addEventListener(IOErrorEvent.IO_ERROR, ioErrorHandler);
loader.contentLoaderInfo.addEventListener(SecurityErrorEvent.SECURITY_ERROR, securityErrorHandler);
...

request = new URLRequest(swfLib);
var context:LoaderContext = new LoaderContext();
context.applicationDomain = new ApplicationDomain(ApplicationDomain.currentDomain);
loader.load(request, context);

Notice that the Loader loads the SWF into an ApplicationDomain that has the current domain as its parent.  This is the key to ensuring that the app can access the loaded class and its assets.

Step #4: Get the class from the loaded SWF and instantiate it

Here we load the Class using an agreed-upon className (in this case, "Wrapper"):

var wrapperClass:Class = loader.contentLoaderInfo.applicationDomain.getDefinition(className) as Class;
var wrapper:Object = new wrapperClass();

Step #5: Apply the skins using setStyle

You can apply the skins to a particular instance of Button or to all instances of Button.  Here’s the latter approach:

StyleManager.getStyleDeclaration("Button").setStyle("upSkin", wrapper.rbUpSkin);
StyleManager.getStyleDeclaration("Button").setStyle("downSkin", wrapper.rbDownSkin);
StyleManager.getStyleDeclaration("Button").setStyle("disabledSkin", wrapper.rbDisabledSkin);
StyleManager.getStyleDeclaration("Button").setStyle("overSkin", wrapper.rbOverSkin);

Step #6: Run the app

My sample app (running live here, with "View Source" enabled) displays a text field where you can enter the URL of the wrapper SWF (also live here).  Enter the wrapper SWF’s URL, click the "Apply" button, and you’ll see that the button now looks like an oversized radio button.  As you mouse over or click on the button, you’ll notice that its appearance changes to display the appropriate skin.  If you create your own wrapper SWF which exposes a different set of symbols with the same class names (rbUpSkin, etc.), you could point the sample app at it and have it load and display a different set of skins.

 

So why would you want to do this?  Dynamic skinning gives you one incredibly powerful benefit:  you can let your users put their skins on your app.  Imagine an MP3 player like Winamp built in Flex.  The developer doesn’t need to create a library of skins for the app, he/she can just expose a UI for setting the skin SWF (perhaps in the Preferences dialog) and let the user pick and choose from whatever skins the community comes up with. Equally important, the developer has fine-grained control over which controls can be skinned and which should retain their original skins (by only calling setStyle on the desired controls).  And last but not least, keeping the skins outside of the application SWF will keep the application file size from exploding as the skins proliferate (with a minor but certainly-acceptable-to-the-user performance hit when the skins are loaded).

 

Notetag in Depth

File under: ,,,,,

Brian Riggs has penned an in-depth article entitled "NoteTag: Anatomy of a Flex mashup", now available on Flex DevNet site, that unifies many of the blog entries here to describe the architecture behind Notetag.

Making HTTP calls in ActionScript 3

The lingua franca of the web is HTTP, and no programming language can claim to be a web-oriented language if it doesn’t fully support it. Custom APIs and modern protocols are increasingly being built on top of HTTP. Just as it’s rarely a good idea to invent a new XML language, it’s increasingly apparent that HTTP, and more broadly REST, is almost always the most appropriate foundation upon which to build new web APIs.

So how does ActionScript 3 stack up in the area of HTTP support? I’ll (arbitrarily) give a score to AS3’s support for the core elements of a minimal HTTP implementation.

Methods

AS3’s HTTPService class supports the basic HTTP methods (GET, POST, PUT, DELETE, HEAD, OPTIONS, and TRACE), but doesn’t allow custom methods. The WebDAV protocol uses a handful of custom HTTP methods (PROPFIND, PROPPATCH, MKCOL, etc.) which HTTPService would be unable to invoke, so it may be a while before we see an AS3-based WebDAV implementation.

Score: 4/5 (Support for custom methods is missing, but few APIs use them anyhow)

Request headers

HTTPService has a headers property which can hold an arbitrary Array of name-value pairs.

Score: 5/5

Response headers

Unfortunately, there’s no way to extract the headers from an HTTP response. (If you’re interested in verifying this, just make an HTTPService call to a bogus page and set a breakpoint in the fault handler. When execution stops there, examine the FaultEvent in Flex Builder’s Variables window — there’s no evidence anywhere that the error is indeed a 404.) I had a conversation with Matt Chotin of the Flex team about this some months ago, and he indicated that not all browsers expose the headers.

This is a pretty significant limitation, especially since REST principles are motivating the designers of more recent protocols and APIs to leverage the HTTP status code vocabulary to define the semantics of method results, rather than invent new vocabularies in the form of custom responses.

Score: 1/5 (I’ll give it one point, since at least it’s possible to distinguish between successful calls and unsuccessful calls.)

Authentication

HTTPService supports authentication against a remote endpoint via the setRemoteCredentials method. Alternatively, the user’s credentials can be specified in Flex Data Server’s proxy-service.xml file.

Score: 5/5

Overall: 15/20

With the glaring exception of support for response headers, AS3’s support for HTTP is full-featured enough to implement just about any REST-style protocol or API. Let’s hope the Flex and Apollo teams can fill this one remaining hole in a future release.

Notetag updated for Flex 2 release

File under: ,,,

Flex 2 was officially released today.  Accordingly, we’ve updated the Notetagdownloadable bits on labs.adobe.com to be compatible with this official release.  Enjoy!

RSS and RIAs

File under: ,,,,,,

Quoted from http://blogs.zdnet.com/Stewart/?p=33:

» RSS Round Up and the Rich Internet | The Universal Desktop | ZDNet.com

I think RSS is so important for the RIA vision because it breaks down the data into simple, easy to consume bits – perfect for a developer creating an application that puts the content front and center. As RSS expands, and we’re seeing that with microformats, the lure of pairing the technology with RIAs is only going to get stronger.

We completely agree, which is why The Kiwi Project exists at Adobe.

An Asynchronous Cookbook (Part Two)

In the previous post, I outlined a few different solutions to the problem of how to structure your asynchronous ActionScript code. The concept of an asynchronous method chain, in which a series of methods that make asynchronous calls to external services are executed sequentially, is an important one for Flex developers who want their apps to pull in data from multiple web services. NoteTag is a prime example: it publishes notes to Blogger and/or TypePad, and stores references to notes and tasks on del.icio.us.

But suppose that one method in the asynchronous chain needs some piece of data generated by an earlier method in the chain. How can you make state available to more than one method in the chain?

Problem #4: One or more methods in an asynchronous method chain need access to data generated somewhere else in the chain. For example, imagine that our getFirstFeed method needs to return the first feed in an Atom service document and the Atom service document.

Solution #4a: Stash the data onto the call object and pass it down the chain (per Sho’s suggestion). This approach works fine in the simple case, but suppose you have a really long chain of methods – you might end up passing data through a bunch of methods that don’t need it, just so that the last method can use the data.

Solution #4b: Stash the data on the enclosing object as a private data member. This is perhaps the simplest solution, as it makes the data available to any method in the chain. But if the client method is called a second time before it has finished its first series of asynchronous calls, then the stashed data could be overwritten! What we really want is a way to ensure that any state we generate during the execution of the asynchronous method chain is scoped on a per-method-invocation basis…

Solution #4c: Achieve per-method-invocation variable-scoping by putting the logic for the chained calls into an internally-scoped delegate class which is instantiated when the client method is called. Because a new delegate is instantiated each time the method is called, the data can be stored on the delegate without any risk of its being overwritten. This approach can be done by convention (by forcing your clients to use a factory class that creates a new object for each operation) or explicitly (by having the method literally create a delegate object, as below).

public class FeedRetriever extends EventDispatcher
{
private var protocol:AtomPublishingProtocol; // initialized elsewhere

// Dispatches a GetFeedEvent after retrieving the first feed listed
// in the service document.
public function getFirstFeed(serviceURI:URI):void
{
var delegate:FeedRetrieverDelegate =
new FeedRetrieverDelegate(this,protocol);
delegate.getFirstFeed(serviceURI);
}
}

// Internally-scoped delegate which is instantiated on every call to
// FeedRetriever.getFirstFeed (and which can therefore safely store state).
internal class FeedRetrieverDelegate
{
private var target:IEventDispatcher;
private var protocol:AtomPublishingProtocol;

// State that needs to be tracked (the service document).
private var service:IService;

public function FeedRetrieverDelegate(target:IEventDispatcher,
protocol:AtomPublishingProtocol):void
{
this.target = target;
this.protocol = protocol;
}

public function getFirstFeed(serviceURI:URI):void
{
// Use a utility class to handle registration and unregistration
// of event listeners.
var responder:AsyncResponder = new AsyncResponder();
responder.setNextEventHandler(protocol,
onGetService, // result handler
“getService”, // result event
onFault,      // fault handler
“atomFault”); // fault event

// Make the asynchronous call to retrieve the service document.
protocol.getService(serviceURI);
}

private function onGetService(event:GetServiceEvent):void
{
// Store the service document since the next method needs
// access to it.
this.service = event.service;

// Get the collection that represents the first feed.
var collection:ICollection = service.workspaces[0].collections[0];

// Use a utility class to handle registration and unregistration
// of event listeners.
var responder:AsyncResponder = new AsyncResponder();
responder.setNextEventHandler(protocol,
onGetFeed,    // result handler
“getFeed”);   // result event
onFault,      // fault handler
“atomFault”); // fault event

// Make the asynchronous call to retrieve the first feed.
protocol.getFeed(collection);
}

private function onGetFeed(event:GetFeedEvent):void
{
// Dispatch a result event to the caller, using
// the passed-in target object.
var newEvent:GetFeedEvent = new GetFeedEvent();
newEvent.feed = event.feed;
newEvent.service = this.service;
target.dispatchEvent(newEvent);
}

private function onFault(event:AtomFaultEvent):void
{
// Do fault processing...
}

// ...
}

But it’s not always ideal from an object-orientation perspective to separate this logic into two classes. Is there a way that we can attain per-method-invocation variable-scoping without using multiple classes? Our final solution accomplishes just that.

Solution #4d: Put your responder code in one or more closures. (A closure is a function that exists within the body of another function and which when executed has its own set of local variables.) Within a closure, you can access variables in the outer scope – a nice benefit. The drawback? Because the closure needs to be declared prior to the asynchronous method call, the flow of control within the code can be a bit confusing.

public class FeedRetriever extends EventDispatcher
{
private var protocol:AtomPublishingProtocol; // initialized elsewhere

// Dispatches a GetFeedEvent that contains the first feed and
// the service document.
public function getFirstFeed(serviceURI:URI):void
{
// #1: The flow of control starts here...

// Declare a function closure that will be called later.
var onGetService:Function = function(event1:GetServiceEvent):void
{
// #3: After protocol.getService completes, control jumps
// to here...

// We set the service document into a local variable,
// which is accessible even from within the subsequent
// closure.
var serviceLocalVar:IService = event1.service;

// Get the first feed in the service document.
var collection:ICollection = serviceLocalVar.workspaces[0].collections[0];

// Declare another function closure to be called later.
var onGetFeed:Function = function(event2:GetFeedEvent):void
{
// #5: After protocol.getFeed completes, control
// jumps to here, and finishes by dispatching
// the client event.

// Dispatch a result event to the caller.
var newEvent:GetFeedEvent = new GetFeedEvent();
newEvent.feed = event2.feed

// Notice how we can access serviceLocalVar -
// this is one nice benefit of closures.
newEvent.service = serviceLocalVar;
dispatchEvent(newEvent);
}

// #4: ... and continues down to here...

// Use a utility class to handle registration and
// unregistration of event listeners.
var responder2:AsyncResponder = new AsyncResponder();
responder2.setNextEventHandler(protocol,
onGetFeed, // result handler
GetFeedEvent.GET_FEED, // result event
onFault, // fault handler
AtomFaultEvent.ATOM_FAULT); // fault event

// Make the asynchronous call to retrieve the first feed.
protocol.getFeed(collection);
}

// #2: ... and continues down to here...

// Use a utility class to handle registration and unregistration
// of event listeners.
var responder1:AsyncResponder = new AsyncResponder();
responder1.setNextEventHandler(protocol,
onGetService, // result handler
“getService”, // result event
onFault,      // fault handler
“atomFault”); // fault event

// Make the asynchronous call.
protocol.getService();
}

private function onFault(event:AtomFaultEvent):void
{
// Do fault processing...
}
}

Following the flow of control in the previous example is a bit like watching a yoyo in action. Sho has some ideas
on how to improve the readability of closure-heavy code.

Summary: When it comes to implementing asynchronous methods in ActionScript, there are a number of approaches that you can take. In many cases you’ll be able to stick with the simplest approach (create an event listener to respond to the event), but there are certain situations where the more complicated approaches are called for. For each such situation, ask yourself the following questions:

  • Do I need to process the result of an asynchronous call?
  • Will the object that makes the asynchronous call be used again after I’m done with it?
  • Do I need to make multiple sequential asynchronous calls?
  • In the chain of sequential asynchronous calls, does a method need access to data that is generated by a different method in the chain?

If the answer to any of these questions is “yes”, then consider using one or more of the following:

  • A helper class that can manage event registration (a la AsyncResponder)
  • Stateful objects
  • Delegates that achieve per-method-invocation variable scoping
  • Closures

An Asynchronous Cookbook (Part One)

If you haven’t read Sho Kuwamoto’s series of blog posts on dealing with asynchronous events, I highly recommend them. Any Flex developer that builds applications that make remote calls is eventually going to run into the problem of how best to structure their code. In the course of developing NoteTag, we tried dozens of solutions to “the asynchronous problem”. The bad news: there is no one-size-fits-all solution. The good news: there are a few key rules-of-thumb which can guide you towards choosing the solution that’s right for the situation.

I’ll walk through the specific problems we’re trying to solve, using the example of a class which provides support for retrieving an Atom service document. (In Atom Publishing Protocol parlance, a service document is an index of blogs that are available from a particular account on a blog server.)

Problem #1: Asynchronous calls don’t return right away, but you often need to process the result.

Solution #1: Create a result handler method, and add it as an event listener to the object which is making the asynchronous call.

public class AtomPublishingProtocol extends EventDispatcher
{
// Dispatches a GetServiceEvent after retrieving the service document.
public function getService(serviceURI:URI):void
{
var service:HTTPService = new HTTPService();
service.url = serviceURI.toString();
service.method = "GET";
service.resultFormat="e4x";
service.addEventListener("result",onGetServiceResult);
service.send();
}

private function onGetServiceResult(event:ResultEvent):void
{
var xml:XML = new XML(event.result);
var atomEvent:GetServiceEvent = new GetServiceEvent();

// Convert the XML to a domain object.
atomEvent.service = ModelFactory.createServiceFromXML(xml));

dispatchEvent(atomEvent);
}

// ...
}

This is the simplest problem, with a correspondingly simple solution. (I’ve left out the fault listener to keep this simplest of examples as simple as possible.) One thing to point out: we didn’t need to unregister the HTTPService event listener because we created a new instance and “threw it away”. But that won’t always be the case…

Problem #2: Asynchronous calls are made using an object that can be called from multiple places in the application. (Imagine that our app reuses one instance of HTTPService for all remote calls.)

Solution #2: For every event registration, there must be a corresponding “unregistration”. Failure to unregister an event listener could lead to it being called again if the event-generating object (HTTPService, in the following example) is called from somewhere else.

public class AtomPublishingProtocol extends EventDispatcher
{
private var service:HTTPService; // initialized elsewhere

// Dispatches a GetServiceEvent after retrieving the service document.
public function getService(serviceURI:URI):void
{
service.url = serviceURI.toString();
service.method = "GET";
service.resultFormat="e4x";
service.addEventListener("result",onGetServiceResult);
service.addEventListener(”fault”,onFault);
service.send();
}

private function onGetServiceResult(event:ResultEvent):void
{
// Manually unregister the listeners in case service
// is used from somewhere else.
service.removeEventListener(“result”,onGetServiceResult);
service.removeEventListener(“fault”,onFault);

var xml:XML = new XML(event.result);
var atomEvent:GetServiceEvent = new GetServiceEvent();

// Convert the XML to a domain object.
atomEvent.service = ModelFactory.createServiceFromXML(xml));

dispatchEvent(atomEvent);
}

private function onFault(event:FaultEvent):void
{
// Manually unregister the listeners here too!
service.removeEventListener(“result”,onGetServiceResult);
service.removeEventListener(“fault”,onFault);

// Do fault processing...
}

// ...
}

Problem #3: A method needs to perform multiple asynchronous calls before returning. Let’s make the use case a bit more complicated: we now use our AtomPublishingProtocol implementation to return the first feed listed in the service document.

Solution #3a: Chain the calls together manually.

public class FeedRetriever extends EventDispatcher
{
private var protocol:AtomPublishingProtocol; // initialized elsewhere

// Dispatches a GetFeedEvent after retrieving the first feed listed
// in the service document.
public function getFirstFeed(serviceURI:URI):void
{
// Manually set up the event listeners.
protocol.addEventListener(“getService”,onGetService);
protocol.addEventListener(“atomFault”,onGetServiceFault);

// Make the asynchronous call to retrieve the service document.
protocol.getService(serviceURI);
}

private function onGetService(event:GetServiceEvent):void
{
// Manually unregister the event listeners.
protocol.removeEventListener(”getService”,onGetService);
protocol.removeEventListener(”atomFault”,onGetServiceFault);

// Get the collection that represents the first feed.
var collection:ICollection = event.service.workspaces[0].collections[0];

// Manually set up the next set of event listeners.
protocol.addEventListener(“getFeed”,onGetFeed);
protocol.addEventListener(“atomFault”,onGetFeedFault);

// Make the asynchronous call to retrieve the first feed.
protocol.getFeed(collection);
}

private function onGetServiceFault(event:AtomFaultEvent):void
{
// Manually unregister the event listeners.
protocol.removeEventListener(”getService”,onGetService);
protocol.removeEventListener(”atomFault”,onGetServiceFault);

// Do fault processing...
}

private function onGetFeed(event:GetFeedEvent):void
{
// Ugh!  How many times do we have to manually
// add and remove event listeners?!?!?
protocol.removeEventListener(”getFeed”,onGetFeed);
protocol.removeEventListener(“atomFault”,onGetFeedFault);

// Dispatch a result event to the caller.
var newEvent:GetFeedEvent = new GetFeedEvent();
newEvent.feed = event.feed;
dispatchEvent(newEvent);
}

private function onGetFeedFault(event:AtomFaultEvent):void
{
// Manually unregister the event listeners.
protocol.removeEventListener(”getFeed”,onGetFeed);
protocol.removeEventListener(”atomFault”,onGetFeedFault);

// Do fault processing...
}

// ...
}

As you can see, it’s a real pain to manually handle registration and unregistration of event listeners. A helper class which can take care of the event registration for us will simplify our code.

Solution #3b: Use a helper class (e.g. AsyncResponder to manage event registration. (For reasons of space, I don’t include the source for the helper class, but you can find it in the Connections library, or in the NoteTag download.)

public class FeedRetriever extends EventDispatcher
{
private var protocol:AtomPublishingProtocol; // initialized elsewhere

// Dispatches a GetFeedEvent after retrieving the first feed listed
// in the service document.
public function getFirstFeed(serviceURI:URI):void
{
// Use a utility class to handle registration and unregistration
// of event listeners.
var responder:AsyncResponder = new AsyncResponder();
responder.setNextEventHandler(protocol,
onGetService, // result handler
“getService”, // result event
onFault,      // fault handler
“atomFault”); // fault event

// Make the asynchronous call to retrieve the service document.
protocol.getService(serviceURI);
}

private function onGetService(event:GetServiceEvent):void
{
// Get the collection that represents the first feed.
var collection:ICollection = event.service.workspaces[0].collections[0];

// Use a utility class to handle registration and unregistration
// of event listeners.
var responder:AsyncResponder = new AsyncResponder();
responder.setNextEventHandler(protocol,
onGetFeed,    // result handler
“getFeed”);   // result event
onFault,      // fault handler
“atomFault”); // fault event

// Make the asynchronous call to retrieve the first feed.
protocol.getFeed(collection);
}

private function onGetFeed(event:GetFeedEvent):void
{
// Dispatch a result event to the caller.
var newEvent:GetFeedEvent = new GetFeedEvent();
newEvent.feed = event.feed;
dispatchEvent(newEvent);
}

private function onFault(event:AtomFaultEvent):void
{
// Do fault processing...
}

// ...
}

This chaining of methods works quite well, and is reasonably easy for a reader to follow. (NoteTag uses this approach in a number of places, both explicitly with AsyncResponder and indirectly via the ChainedCommand class, which uses AsyncResponder to chain asynchronous method calls within Cairngorm Commands.)

In the next post, I’ll add another wrinkle to this problem: what if one method in the asynchronous method chain needs access to data that was generated earlier in the chain?

Architecting a Flex App

Web developers who take Flex for a spin may initially be confused by the user interface model.  Although a typical, servlet-style, request-response model will work in Flex, There Is A Better Way.  Thanks to the "[Binding]" tag in the Actionscript language, you can bind your view to your model data so that changes to the model are automatically reflected in the view.  The Cairngorm microarchitecture formalizes this approach, and is a great starting point for developers who want to figure out how to "make it all work together".  In this post I’ll describe how variable binding, feature-driven development, and Cairngorm all work together for NoteTag.

Here’s how a typical, non-trivial Flex app might be architected:

Domain

  • The classes that make up the domain model.  In NoteTag, this includes Notes, Tasks, and Subscriptions.  (A Subscription is a collection of related Notes or Tasks.)

Model

  • A Singleton that holds bindable instances of the domain model.  In NoteTag, the ModelLocator Singleton holds the user’s list of Subscriptions, the user’s Connections, the current Subscription, the current Note, etc.

View

  • The UI components (generally, though not always, MXML files).  The UI components that are state-dependent are bound to instance variables in the ModelLocator.  Any changes to data in the ModelLocator will cause the UI to automatically update, provided that that data is marked as "[Bindable]".  An example in NoteTag is the NoteListView, which presents the list of Notes in the current Subscription.  If the current Subscription or any of its Notes change then the NoteListView will automatically update to reflect the changes.

Controller

  • Infrastructure for implementing features as event-driven Commands.  Examples in NoteTag include GetSubscriptionCommand, GetNoteCommand, etc.

Business

  • Business logic classes perform operations on Domain objects, often making calls to remote services and returning the results asynchronously.  The SubscriptionManager class is the entry point for most of NoteTag’s business logic.

Service

  • The services layer, which holds all classes that are used to make remote service (HTTPService, RemoteService, and WebService) calls.  NoteTag uses a set of service factory classes, which decouple the configuration of specific HTTP services from the components that make calls to HTTP services.

Most application features touch on some or all of these components.  Here’s the workflow for a typical feature:

  1. The View broadcasts an event.
  2. The singleton Controller receives the event, maps it to its corresponding Command, and executes the Command.
  3. The Command delegates to the appropriate Business object to perform the business logic.
  4. The Business object performs the business logic, possibly making one or more asynchronous calls to various Services, and returns the result by dispatching a new event to the Command.
  5. The Command assigns the result to the singleton Model.
  6. Any Views that are bound to the data in the singleton Model are automatically updated.

So how would this work for a specific feature?  When the user selects a Note from the list of Notes (at the top of the screen, below), the Note is loaded from the appropriate repository (Blogger or TypePad) and displayed in the editor (at the bottom of the screen, below):

 

1. Broadcasting the Event

When the user clicks on the first Note in the list, NoteListView dispatches an event, as follows:

// NoteListView.mxml
private function getSelectedEntry(event:ListEvent) : void
{
  var selectedEntry:TagBasedEntry =
    TagBasedEntry(currentFeed.entries[event.rowIndex-1]);

  Application.application.dispatchEvent(
    new GetNoteEvent(selectedEntry.metadata,true));
}

2. Responding to the Event

Because NoteTag’s Front Controller has registered itself to listen for all Command Events, it will be notified when GetNoteEvent is dispatched:
// NoteTagController.as
public class NoteTagController extends FrontController
{
  public function NoteTagController()
  {
    addCommand(LoginEvent.EVENT_LOGIN, LoginCommand);
    addCommand(GetNoteEvent.EVENT_GET_NOTE, GetNoteCommand);
    addCommand(GetTaskEvent.EVENT_GET_TASK, GetTaskCommand);
    addCommand(PostNoteEvent.EVENT_POST_NOTE, PostNoteCommand);
    // more commands...
  }
}

Cairngorm’s FrontController provides the infrastructure for listening for events and responding to them by executing the appropriate command.

3. Executing the Command

To retrieve the Note, NoteTag needs to make a call to the blog server that stores the user’s notes.  The GetNoteCommand, which implements Cairngorm’s Command interface, takes care of this:

// GetNoteCommand.as
internal class GetNoteCommand extends ChainedCommand
{
 
public override function execute(event:CairngormEvent):void
 
{
    var initialEvent:GetNoteEvent = GetNoteEvent(event);

   
var subscriptionManager:SubscriptionManager =
     
ModelLocator.getInstance().subscriptionManager;

   
setNextEventHandler(subscriptionManager,
     
handleLoadNote,
     
LoadNoteEvent.EVENT_LOAD_NOTE,
     
onSubscriptionFault,
     
SubscriptionFaultEvent.EVENT_SUBSCRIPTION_FAULT);

    subscriptionManager.loadNote(initialEvent.metadata);
  }

  private function handleLoadNote(event:LoadNoteEvent):void
  {
    // handle result here...
  }

  // ...
}

(You may have noticed that GetNoteCommand extends ChainedCommand — this class chains asynchronous calls together using the setNextEventHandler method.  In a future post, I’ll go into greater detail on ChainedCommand, and asynchronous responders in general.)

4. Performing the Business Logic

The SubscriptionManager handles the loading of the Note by executing a series of HTTP service calls to the tag server and the blog server.  When the note has been loaded, the SubscriptionManager will dispatch a LoadNoteEvent, which will be handled by GetNoteCommand.handleLoadNote (see the next item).

5. Updating the Model

GetNoteCommand responds to the event by assigning the loaded Note to the appropriate data member on the ModelLocator:

// GetNoteCommand.as
internal class GetNoteCommand extends ChainedCommand
{
 
// ...

  private function handleLoadNote(event:LoadNoteEvent):void
  {
    ModelLocator.getInstance().currentNote = event.note;
  }

  // ...
}

6. Updating the View

Any views that are bound to the ModelLocator’s currentNote member will automatically update themselves to reflect the new data.  NoteView, the component that’s responsible for displaying the Note in an editor, is one such view:

// NoteView.mxml
<mx:VBox xmlns:mx="http://www.adobe.com/2006/mxml"
   
xmlns:view="com.adobe.kiwi.notetag.view.*"
 
 
xmlns="*">
 
<mx:Script>
  <![CDATA[
    import com.adobe.kiwi.notetag.model.*;

    [Bindable] private var model:ModelLocator = ModelLocator.getInstance();
  ]]>
  </mx:Script>

  <view:NoteEdit id="noteEditor" width="100%" height="100%"
   
note="{model.currentNote}" />

</mx:VBox>

 

Every other feature — publishing a Note, fetching a Subscription, updating a Task — is implemented with the same Event-to-Command-to-Model-to-View approach.  Command-specific Events can be dispatched from multiple contexts, without knowing which Views will be affected.  Views can bind to Model changes without knowing where the originating Event was dispatched from.  Loose coupling leads to cleaner, more maintainable code.

NoteTag Installation: Tips From the Field

Here are some “tips from the field” to help with the NoteTag installation process. If you have any problems (or suggestions!), add them to the comments for this thread.

1) There are a few references to “C:\Dev\workspace” in the installation guide. This is the suggested workspace folder location, but feel free to use any path.

2) With regard to the SSL keystore file, be sure that the name you use with keytool.exe is the same as the element in jrun.xml. For example, if the command to generate the keystore file is:

keytool -genkey -alias localhost -validity 9999 -keyalg RSA -keystore c:\notetag.keystore -keypass mypass -storepass mypass

then the SSL service in jrun.xml should look like this:

<service class="jrun.servlet.http.SSLService" name="SSLService">
    <attribute name="port">9100</attribute>
    <attribute name="keyStore">{jrun.rootdir}/lib/notetag.keystore</attribute>
    <attribute name="keyStorePassword">mypass</attribute>
    <attribute name="trustStore">{jrun.rootdir}/lib/trustStore</attribute>
    <attribute name="socketFactoryName">jrun.servlet.http.JRunSSLServerSocketFactory</attribute>
    <attribute name="deactivated">false</attribute>
    <attribute name="bindAddress">*</attribute>
    <attribute name="interface">*</attribute>
    <attribute name="clientAuth">false</attribute>
</service>