Sunday, May 10, 2009

Command Module Framework

What is it?

The Command Module Framework is a series of classes for working with modules in Flex, which extends the default module behavior to compensate for numerous known problems as well as to add needed functionality.

In particular the following is a list of some of the more important functional differences:

  • The ability to queue events on the module loader which get dispatched to the module implementation once it is loaded and ready
  • Modules when unloaded go through a series of magic tricks to help them actually unload. Some day I will get around to explaining what a lot of these tricks are and why they are needed.
  • The ability to configure a manager to handle loading and unloading modules in view stacks
  • The built in ability to share data between all modules
  • The built in ability to optionally display module loading progress
  • Makes it easier to communicate between modules

What is it intended to be used for?

This library is intended to be used for Flex applications that need to load and unload modules, with a lot of the common code used for doing this already written.

Modules are used so that the user of your application only has to download the parts they need when they need it. For example if an application is a series of 100 different forms, it makes sense that the user should only have to download the forms they intend to use. The is a big deal in web applications because is means the difference (depending on your connection speed) between spending 15 minutes downloading a large application versus spending 5 seconds downloading an application framework and 3 seconds for each additional module.

How does it work?

There are two general ways to use it:

  1. Using the Command Module Loader, which is similar to the Module Loader
  2. Using the Command Module Manager, which is similar to the Module Manager

Using the Command Module Loader (Example #1)

The Command Module Loader is very similar to the Module Loader, and can be used as in the same way in a basic example:

Application.mxml

<?xml version="1.0" encoding="utf-8"?>
<mx:Application
    xmlns:mx="
http://www.adobe.com/2006/mxml"
    xmlns:commandmodulefr="net.sourceforge.commandmodulefr.*">
    <commandmodulefr:CommandModuleLoader url="valentino/modules/ModuleA.swf" />
</mx:Application>

The code for Module A is the following:

ModuleA.mxml

<?xml version="1.0" encoding="utf-8"?>
<commandmodulefr:CommandModule
    xmlns:commandmodulefr="net.sourceforge.commandmodulefr.*"
    xmlns:mx="
http://www.adobe.com/2006/mxml">
    <mx:Panel title="Module A" width="100%" height="100%">
        <mx:Button label="I am a button" />
    </mx:Panel>
</commandmodulefr:CommandModule>

The result is an application that loads the module at the given URL, assuming it inherits from the CommandModule class, which in the case of this module results in a panel with a button in it:

image

Sending events to modules (Example #2)

Let’s say that for some reason you want to send an event to your module, but being a smart Flex developer you are aware of the following:

  • I can’t reference an instance of my custom module class in my main application because that would cause that class to get compiled in with my main application, which would defeat the purpose of modules
  • If I dispatch an event directly to the loader using dispatchEvent, my module could miss that event if it is not already loaded

For these reasons the CommandModuleLoader class has a queueEvent function which ensures that the module always gets the event no matter when it is dispatched.

For this example there is a button in the main application that when pressed sends some custom event to the module:

Application.mxml

<?xml version="1.0" encoding="utf-8"?>
<mx:Application
    xmlns:mx="
http://www.adobe.com/2006/mxml"
    xmlns:commandmodulefr="net.sourceforge.commandmodulefr.*">
    <mx:Script>
        <![CDATA[
            private function sendSomeEvent():void
            {
                var event:Event = new Event("someCustomEvent");
                moduleLoader.queueEvent(event);
            }
        ]]>
    </mx:Script>
    <mx:Button label="Send Some Custom Event" click="sendSomeEvent()" />
    <commandmodulefr:CommandModuleLoader id="moduleLoader" url="valentino/modules/ModuleA.swf" />
</mx:Application>

In the module there is a listener created on initialization for the custom event, which when the event is dispatched displays a popup window:

ModuleA.mxml

<?xml version="1.0" encoding="utf-8"?>
<commandmodulefr:CommandModule
    initialize="onInit()"
    xmlns:commandmodulefr="net.sourceforge.commandmodulefr.*"
    xmlns:mx="
http://www.adobe.com/2006/mxml">
    <mx:Script>
        <![CDATA[
            import mx.controls.Alert;
            private function onInit():void
            {
                this.addEventListener("someCustomEvent", someHandler);
            }
            private function someHandler(event:Event):void
            {
                Alert.show("Some Custom Event!");
            }
        ]]>
    </mx:Script>
    <mx:Panel title="Module A" width="100%" height="100%">
        <mx:Button label="I am a button" />
    </mx:Panel>
</commandmodulefr:CommandModule>

image

Receiving events from modules (Example #3)

In order to receive an event from a module you must attach some listener on the actual module, and being a smart Flex developer you are aware of the following:

  • I can’t reference an instance of my custom module class in my main application because that would cause that class to get compiled in with my main application, which would defeat the purpose of modules
  • Being able to reference an event function using an attribute in the MXML of the loader would require the loader to already have the desired event metadata declarations

For these reasons the base implementation of the loader has an event for when the module is loaded, which can be caught in order to obtain a reference to the CommandModule class. This instance can then have a listener for anything you want added to it.

Application.mxml

<?xml version="1.0" encoding="utf-8"?>
<mx:Application
    xmlns:mx="
http://www.adobe.com/2006/mxml"
    xmlns:commandmodulefr="net.sourceforge.commandmodulefr.*">
    <mx:Script>
        <![CDATA[
            import mx.controls.Alert;
            import net.sourceforge.commandmodulefr.CommandModule;
            import net.sourceforge.commandmodulefr.event.CommandModuleEvent;
            private function moduleLoaded(event:CommandModuleEvent):void
            {
                var module:CommandModule = event.module;
                module.addEventListener("someCustomEvent", someHandler);
            }
            private function someHandler(event:Event):void
            {
                Alert.show("Some event from a module!");
            }
        ]]>
    </mx:Script>
    <commandmodulefr:CommandModuleLoader
        moduleLoaded="moduleLoaded(event)"
        url="valentino/modules/ModuleA.swf" />
</mx:Application>

When the button is pressed in the module, it dispatches the expected event which is then picked  up in the main application.

ModuleA.mxml

<?xml version="1.0" encoding="utf-8"?>
<commandmodulefr:CommandModule
    xmlns:commandmodulefr="net.sourceforge.commandmodulefr.*"
    xmlns:mx="
http://www.adobe.com/2006/mxml">
    <mx:Script>
        <![CDATA[
            private function sendEvent():void
            {
                var event:Event = new Event("someCustomEvent");
                this.dispatchEvent(event);
            }
        ]]>
    </mx:Script>
    <mx:Panel title="Module A" width="100%" height="100%">
        <mx:Button label="I am a button" click="sendEvent()" />
    </mx:Panel>
</commandmodulefr:CommandModule>

The result is a window that is displayed when the button within the module is pressed.

image

Sharing data between modules and the main application (Example #4)

Besides just using a singleton, the Command Module Loader accepts an Application Context object that is passed to all of the modules that are loaded. This class contains a data attribute which is an object that can be populated with anything, or you can just extend this class and defined a shared structure.

In this example the main application declares the Application Context, and sets the data so that “someVar” has a value.

Application.mxml

<?xml version="1.0" encoding="utf-8"?>
<mx:Application initialize="onInit()"
    xmlns:mx="
http://www.adobe.com/2006/mxml"
    xmlns:commandmodulefr="net.sourceforge.commandmodulefr.*">
    <mx:Script>
        <![CDATA[
            import net.sourceforge.commandmodulefr.data.ApplicationContext;
            [Bindable]
            private var _context:ApplicationContext = new ApplicationContext();
            private function onInit():void
            {
                _context.data.someVar = "This is some text";
            }
        ]]>
    </mx:Script>
    <commandmodulefr:CommandModuleLoader
        context="{_context}"
        url="valentino/modules/ModuleA.swf" />
</mx:Application>

The module then uses that value from the Application Context in order to get the text that it displays on the button.

ModuleA.mxml

<?xml version="1.0" encoding="utf-8"?>
<commandmodulefr:CommandModule
    xmlns:commandmodulefr="net.sourceforge.commandmodulefr.*"
    xmlns:mx="
http://www.adobe.com/2006/mxml">
    <mx:Panel title="Module A" width="100%" height="100%">
        <mx:Button label="{context.data.someVar}"  />
    </mx:Panel>
</commandmodulefr:CommandModule>

The result is the following:

image

Using the Command Module Manager (Example #5)

The Command Module Manager is far more generic, and is actually intended to be used as part of some other framework in an application. The module manager class is useful for when you do not want to have to declare every module that you intended to use. An example usage would be in an application that has a list of the URL’s of all of the module that it can load in some external XML file, where various events in that application would result in modules from that XML file being loaded.

The two most notable things about the Command Module Loader class are the following:

  1. It takes a view stack as a parameter and loads and unloads module into and out of that view stack.
  2. It cleans up modules so that they can actually be unloaded (somewhat, but this is for a later lengthy discussion)

Now consider an application that just has a series of buttons for loading and unloading modules, and a general area that the modular content is displayed in using a view stack. Though this usage seems really simple, it is a quite common template for an application.

Application.mxml

<?xml version="1.0" encoding="utf-8"?>
<mx:Application
    xmlns:mx="
http://www.adobe.com/2006/mxml"
    xmlns:commandmodulefr="net.sourceforge.commandmodulefr.*">
    <commandmodulefr:CommandModuleManager id="manager" viewStack="{view}" />
    <mx:Button label="Load Module A"
        click="manager.requestLoadModule('valentino/modules/ModuleA.swf')" />
    <mx:Button label="Unload Modules" click="manager.unloadAll()" />
    <mx:ViewStack id="view" />
</mx:Application>

The idea behind the module manager is to allow an application an easy way to change out dynamic content which comes from modules. The generic nature of the Command Module Manager then allows the individual application to decide about the details of how module communication occurs.

ModuleA.mxml

<?xml version="1.0" encoding="utf-8"?>
<commandmodulefr:CommandModule
    xmlns:commandmodulefr="net.sourceforge.commandmodulefr.*"
    xmlns:mx="
http://www.adobe.com/2006/mxml">
    <mx:Panel title="Module A" width="100%" height="100%">
        <mx:Button label="I am a button"  />
    </mx:Panel>
</commandmodulefr:CommandModule>

The result is a simple application that lets you load and unload modules using buttons from the main application.

image

In Practice

How you would go about using this library really depends on what you are doing, but for most implementations the Command Module Manager is probably going to be the most useful. This is because for any application of a medium to large size and for many reasons including maintainability and user experience, you are going to want to divide the application into modules. If you are using a considerable number of modules, the application is a lot easier to modify if the usage of those modules are externally defined.

For example if you have an application that has 100 modules and you wanted to use a module loader for each one, you would have to have 100 module loader declarations in your application. Every time someone added a module they would also have to add a module loader declaration, which would become very cumbersome. You could of course deal with the module loaders programmatically but you would essentially be recreating the equivalent of a module manager.

A technique which I have used on various projects that seems to be pretty straight forward is to have some external file, almost always XML, which specifies the module locations as well as other information. This file at its most basic level is just a way to declare module names and locations outside of your code, giving more flexibility at runtime.

For example if I had the following file:

<Modules>

<Module name=”ModuleA” url"=”valentino/modules/ModuleA.swf” />

<Module name=”ModuleB” url"=”valentino/modules/ModuleB.swf” />

<Module name=”ModuleC” url"=”valentino/modules/ModuleC.swf” />

</Modules>

I would then write some type of manager (referred to as a State Process Manager) class in my application to listen for particular events, and then based on those events look up a module’s URL in the external XML file and then pass them to the Command Module Loader to do the loading.

The resulting architecture looks something like the following:

image 

State Process Manager - The objective of the state process manager is to control the event flow of the application, requiring reference to the one or more command module managers, the external module definitions, and the modules.

Command Module Manager - The command module manager is just an easy way to load an unload modules in and out of a view stack using URL’s.

Module Definitions – A list of the URL’s that contain the modules to load, which can also include any other information regarding module runtime configuration.

Modules – Portions of the UI to be loaded and unloaded.

Link to the Open Source Project

https://sourceforge.net/projects/commandmodulefr/

7 comments:

jase21 said...

It seems to simplify a lot of things. Let me check out. Thanks btw.

jase21 said...

Is it free for Commercial use? Please don't make it GPL. Is it LGPL ? I don't see any license information in your code. Am I free to use it for any work?
This is a good one. Thanks.

John Valentino said...

It is listed as MPL on the SourceForge page. If this doesn't work for you let me know and I will change it.

jase21 said...

Yes, Mozilla Public License works fine. Thanks.
I'm using your library to load, and work with modules instead of the default ModuleLoader in the flex sdk. So far it works very well. This library is light-weight and simple.

jase21 said...

Hey, the files aren't available now. Will you be uploading the updated version soon? (Try github or google code ;) ).

jase21 said...

I'm following this tutorial (documentation) on how to use this framework. But you skips a lot of (deep) concepts for another post. Will you please blog more thorough treatment on modules in flex applications?
--
Jaseem V V

John Valentino said...

For the time being the source code can be obtained through subversion on SourceForge: http://commandmodulefr.svn.sourceforge.net/viewvc/commandmodulefr/

Yes, no one really uses SourceForge for Flex these days.

I posted quite a bit of other material on Flex and modules elsewhere in this blog:
- Flex Memory Issue #1: The debug player always leaks (http://jvalentino.blogspot.com/2009/05/flex-memory-tip-1-debug-player-always.html)
- Flex Memory Issue #2: Component effects cause leaks (http://jvalentino.blogspot.com/2009/05/flex-memory-issue-2-component-effects.html)
- Flex Memory Issue #3: Garbage Collection (http://jvalentino.blogspot.com/2009/05/flex-memory-issue-3-garbage-collection.html)
- Flex Memory Issue #4: Modules and Application Domains (http://jvalentino.blogspot.com/2009/06/flex-memory-issue-4-modules-and.html)
- Flex: Building Modular Rich Internet Applications (http://jvalentino.blogspot.com/2009/07/flex-building-modular-rich-internet.html)