Work at SourceForge, help us to make it a better place! We have an immediate need for a Support Technician in our San Francisco or Denver office.

Close

HowToUseCairngormModule

How to use the Cairngorm Module Library (Requires Parsley)

Contents

Summary

The Cairngorm Module library is designed to simplify the configuration, rendering and loading of modular content. Additonally it offers flexiblity to communicate to modules. It contains infrastructure classes, view components, a mechanism for loading and communicating to modular content on-demand in response to Parsley messages.

Introduction

The biggest benefit of modularization we see is often not the reduced initial SWF size, although that is a benefit, but rather the development efficiency and encapsulation of functional areas. A module can be developed, tested and profiled in relative isolation by a small group of developers, and the contracts can be agreed between modules. Build times are quick and one module can effectively offer services to another. The implementation of one module can change without impact on others. This approach can scale to large delivery teams.

The Flex SDK provides a ModuleLoader view component for placing modular content in-line with other views. The ModuleManager singleton offers a manual management of modules. The IModuleInfo interface abstracts the loading and unloading of modular content.

The Cairngorm Module library provides additional features on top of the Flex SDK and Parsley module support:

  • Declaration of module configuration externally, so the URLs for modules might be declared in MXML or loaded at runtime from an XML file.
  • Lazy loading of modules in response to application messages. For example, a StartChatMessage is sent, so the Chat module needs to be loaded and initialized, then the StartChatMessage processing can resume.
  • Routing of messages to the appropiate modules (i.e. communicating to all modules, all modules of one type, or a specific module instance)

Configuration

The ParsleyModuleDescriptor can be used to describe the location and application domain of the module. This can be specified in a Parsley MXML or XML context.

Compile-Time MXML Configuration

When configuring the module library via MXML, use the CairngormModuleSupport ContextBuilder when creating the Parsley context:

<spicefactory:ContextBuilder>
    <cairngorm:CairngormModuleSupport/>
    <spicefactory:FlexConfig type="{ CairngormModuleLibSampleContext }"/>
</spicefactory:ContextBuilder>

Then, describe the module in the MXML context:

<cg:ParsleyModuleDescriptor objectId="moduleA"
                            url="example/moduleA/ModuleA.swf"
                            applicationDomain="{ ClassInfo.currentDomain }"/>
Run-Time XML Configuration

When configuring the module library via XML that can be loaded at runtime, use the CairngormModuleXMLSupport additionally to the CairngormModuleSupport ContextBuilder when creating the Parsley XML context:

<spicefactory:ContextBuilder>
    <cairngorm:CairngormModuleSupport/>
    <cairngorm:CairngormModuleXMLSupport/>
    <spicefactory:XmlConfig file="runtimeContext.xml"/>
</spicefactory:ContextBuilder>

Then, describe the module in the XML context:

<objects xmlns="http://www.spicefactory.org/parsley"
         xmlns:cairngorm="http://www.adobe.com/cairngorm"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://www.spicefactory.org/parsley
                http://www.spicefactory.org/parsley/schema/2.0/parsley-core.xsd">

    <cairngorm:parsley-module-descriptor object-id="moduleA"
                                         url="example/moduleA/ModuleA.swf"/>

</objects>

Rendering

In order to reference a definition of a module you need to take the objectId of a ParsleyModuleDescriptor...

<cg:ParsleyModuleDescriptor objectId="moduleA"
                            url="example/moduleA/ModuleA.swf"/>

...and connect it with an IModuleManager instance defined in the view that loads the module.

<mx:Script>
    <![CDATA[
        import com.adobe.cairngorm.module.IModuleManager;

        [Inject(id="moduleA")]
        public var moduleManager:IModuleManager;
    ]]>
</mx:Script>

<mx:Label text="moduleId: { moduleId == null ? 'NOT SET' : moduleId }"/>

<module:ModuleViewLoader moduleManager="{ moduleManager }"
                         skinClass="com.adobe.cairngorm.module.ModuleViewLoaderSkin">
    <module:loadPolicy>
        <module:BasicLoadPolicy/>
    </module:loadPolicy>
</module:ModuleViewLoader>

As soon as the IModuleManager implementation is injected into the view, (which would be the DisplayObject's addedToStage event following the default of Parsley), the module starts loading through the ModuleViewLoader component. The skinClass property allows to define the visual display during the module loading and error state, which is likley to be custom to most applications.

For Flex 3, you can use a ViewLoader component like the below

<module:ViewLoader moduleManager="{ moduleManager }">
    <module:loadPolicy>
        <module:BasicLoadPolicy/>
    </module:loadPolicy>
</module:ModuleViewLoader>

Since this library was migrated to support Flex 4, the ViewLoader was refactored to mimic the new SDK 4 ModuleViewLoader. However, now the ViewLoader does not anymore support loading and error states in the same way. For customizable loading and error states we suggest to either migrate to SDK 4 and use ModuleViewLoader or alternativley subclass the ViewLoader to add these behaviors which is likely to be custom to most applications.

Load Policy Strategies

When using the ModuleViewLoader (with SDK 4) or the ViewLoader (SDK 3) the module library provides a load policy strategy mechanism that will take care of loading the module when appropriate according to the strategy provided.

The module library provides two different strategies but developers can easily implement their own specific strategies using the ILoadPolicy interface.

Basic Loading Policy

The BasicLoadPolicy strategy is the default loading policy that mimic the SDK ModuleLoader. This strategy will load the module as soon as the ModuleViewLoader (or ViewLoader) is added to stage and it's going to unload the module as soon as removed from stage.

Any messages sent, while the module itself is not yet loaded and ready, are kept in a queue and re-dispatched as soon as the module is available.

Lazy Loading Policy

The LazyModuleLoadPolicy strategy is going to load the module on demand when a message destined to that module is dispatched.

Therefore, when the ModuleViewLoader (or ViewLoader) is added to stage, the module is not yet loaded it will instead wait for a message that is destined to this specific module. When this is occurring the ModuleViewLoader (or ViewLoader) is automatically loading the module and as soon as the module is loaded will redispatch the message that was waiting in the queue.

The LazyModuleLoadPolicy do not provide any automatic ways to unload a module so when using a this strategy we need to manually take care of unloading the module when necessary via the view component.

Communication Setup

When messages need to be send to modules using Parsley messaging, the modules need to be loaded and initialized in order to receive Parsley messages.

The module library takes care of this aspect and keep the messages in a queue until the destined modules are ready to accept the messages.

Currently when a message is dispatched and the destination module is never loaded, the message is kept forever in the queue however we are currently working on improving this by providing a timeout to the queue so when the timeout occur, messages are cleaned from the queue.

The relationship between a module definition and a module view does not have to be 1:1.

Applications can for example display multiple instances of one module. The module library allows to control the communication between the loader and one or many module definitions and one or many module instances.

In any case for the inter-communication to work with the Module library there are few things to consider.

Loading with Dispatching a Message

The user needs to define the message inside the context with a ModuleMessageInterceptor definition.

Creating a Parsley Module

In order to receive the message, the loaded module needs to implement the IParsleyModule interface, which points to the Parsley context that can receive the message

<mx:Module xmlns:mx="http://www.adobe.com/2006/mxml" <br=""> xmlns:spicefactory="http://www.spicefactory.org/parsley" implements="com.adobe.cairngorm.module.IParsleyModule" >

<mx:Script>
    <![CDATA[
    import com.adobe.cairngorm.module.IParsleyModule;            
    import org.spicefactory.parsley.flex.FlexContextBuilder;

    public function get contextBuilder():ContextBuilderTag
    {
    return contextBuilderTag;
    }                  
    ]]>
</mx:Script>

<spicefactory:ContextBuilder id="contextBuilderTag" 
                             config="{ ModuleAContext }" />
Handling a Message inside a Parsley Module

Now, any object within the ModuleAContext can accept the module message like this:

[MessageHandler(scope="local")]
public function broadcastMessageHandler(message:BroadcastMessage):void
{ ... }

Communication Options to Module Instances

Dispatching a Message to All Instances of One Defintion

The ModuleMessageInterceptor from above...

<cg:ParsleyModuleDescriptor objectId="moduleA"
                            url="example/moduleA/ModuleA.swf"/>

<cg:ModuleMessageInterceptor type="{ BroadcastMessage }"
                             moduleRef="moduleA"/>

...directs every BroadcastMessage to all module instances from the definition "moduleA".

Dispatching a Message to All Instances of All Defintions

The below message is being directed to all instances of all module definitions.

<cg:ModuleMessageInterceptor type="{ ClearLogMessage }"/>
Dispatching a Message to One Instance of One Definition

In order to dispatch to one particular instance of one module definition, the ModuleMessageInterceptor searches for a property annotated with a ModuleId definition....

<cg:ModuleMessageInterceptor type="{ PingMessage }"/>

...which can be defined inline:

public class PingMessage
{
    private var _moduleId:String;

    public function PingMessage(moduleId:String)
    {
        this._moduleId=moduleId;
    }

    [ModuleId]
    public function get moduleId():String
    {
        return _moduleId;
    }
}

The value of this ModuleId property must be defined on the ViewLoader component's moduleId property.

Module Rig

As described in the guidelines around modularity, we tend to separate RIAs into functional areas, which allows for an isolated development and testing of a part of an RIA. Especially with large scale RIA and large teams, sub-teams can be responsible for developing those functional areas. When a functional area translates into a module, the module library can provide additional benefits.

With the Module Rig feature, a developer can now create a new isolated application we call 'module rig'. In Order to use the Module Rig feature, a ParsleyModuleDescriptor, a ModuleRigBootstrap and a ModuleRigContainer can be leveraged.

The ParsleyModuleDescriptor

As shown in earlier chapters, a ParsleyModuleDescriptor inside a Parsley Context describes the location and loading parameters of a module:

<cairngorm:ParsleyModuleDescriptor
        objectId="moduleA"
        url="example/moduleA/ModuleA.swf"
        applicationDomain="{ ClassInfo.currentDomain }"
        />
The ModuleRigBootstrap

To run a module in isolation as intended, it may also be important to initialize various parts of the application prior launching the module to get to a state where the module can be shown which mimic the state of the shell application when getting to that given module.

For example, a module may assume that the user is authenticated and some object initialized in order to run as intended.
This feature aims to facilitate the initialization of such objects by defining a ModuleRigBootstrap inside a Context. The ModuleRigBootstrap extends Spicelib's SequentialTaskGroup. The Module is only going to be loaded if the Module Rig Bootstrap succeed.

<cairngorm:ModuleRigBootstrap objectId="moduleRigBootstrap">
    <modulerig:SampleModuleRigTask message="task one" />
    <modulerig:SampleModuleRigTask message="task two" />
    <modulerig:SampleModuleRigTask message="task three" />
</cairngorm:ModuleRigBootstrap>
The ModuleRigContainer

The ModuleRigContainer is the View part of the mechanism. It is a SkinnableContainer which is associated to a default Skin with some Skin States. It will load the bootstrap and once successful, the module will be loaded. The default skin provide a header with a title, and the content where the Module is loaded.

<cairngorm:ModuleRigContainer 
    id="moduleRig"
    skinClass="com.adobe.cairngorm.module.rig.ModuleRigContainerSkin"
    left="0" right="0" top="0" bottom="0"
    bootstrap="{ bootstrap }"
    moduleManager="{ module }" />

For more information, please view the examples within the ModuleTest project.

### Cairngorm 3 - [ Home ][1] - [ Guidelines ][3] - [ Tools ][4] - [ Libraries Downloads ][2]
### Cairngorm 2 - [ Home ][5] - [ Framework Downloads ][6] - [ Eclipse Plugin ][7]
### Project - [ Source ][8] - [ Bug Database ][9] - [ Submitting a Patch ][10] - [ Developer Documentation ][11] - [ Forums ][12] - [ License ][13]