Tom Sugden: Flex Archives

April 16, 2010

Optimizing the Flex DataGrid for Frequent Updates

The default behaviour of a Flex DataGrid is to redraw itself entirely when an item in its data provider changes. This makes good sense in some cases, since the item renderers might need to grow or shrink in response, but at other times it can be problematic. If subsets of the data provider change frequently, like in a real-time price grid, excessive redrawing and increased CPU can occur. This blog post explains a simple technique for overriding the default behaviour to support frequent updates with reduced CPU.

DefaultAndOptimizedDG.png

Figure 1: Redraw regions of default (left) and optimized (right) data grids

You can download an example Flash Builder project to accompany this blog post:

Thanks to Christophe Coenraets for first showing this DataGrid optimization technique to me.

Understanding the Default Behavior

Let's say you're rendering a data provider full of simple, bindable value objects. Each time one of the properties of an item changes, the auto-generated binding code dispatches a propertyChange event. This is heard by the enclosing collection, triggering a collectionChange event of the update kind. Then deep within the DataGrid and its baseclass, ListBase, an invalidation occurs:

protected function collectionChangeHandler(event:Event):void
{
    ...
    itemsSizeChanged = true;

    invalidateDisplayList();
}

So the DataGrid will be redrawn during the next update. In the worst case scenario, one or more items of data might change during each frame interval, causing the grid to invalidate itself and be redrawn on every frame. This would increase CPU.

Overriding the Default Behavior

The default behaviour of the DataGrid can be overridden so that item renderers invalidate themselves independently and only when changes to their individual data items occur. This optimization usually involves two steps:

  1. Extend DataGrid to override collectionChangeHandler() to ignore collection change events that arise from updates to items in the data provider.
  2. Write a custom item renderer that handles its own invalidation when it detects changes in its data item.

Extend DataGrid

Shown below is a simple extension of DataGrid that overrides the collectionChangeHandler() function to ignore collectionChange events with the update kind.

public class FastDataGrid extends DataGrid
{
    override protected function collectionChangeHandler(event:Event):void
    {
        if (event is CollectionEvent &&
            CollectionEvent(event).kind != CollectionEventKind.UPDATE)
        {
            super.collectionChangeHandler(event);
        }
    }
}

This change stops the DataGrid redrawing excessively when frequent changes occur, but it needs to be complemented with a modified item renderer that knows how to invalidate itself.

Note that this customization is only necessary if the value objects in your data provider are dispatching propertyChange events. If you're using custom events instead, then the collection will ignore these and so the invalidation of the DataGrid will not be triggered.

Self-Invalidating Item Renderer

Shown below is a custom ActionScript item renderer based on UIComponent containing a single UITextField. The item renderer listens to its data item for matching propertyChange events and invalidates itself when they occur.

public class PropertyChangeRenderer
    extends UIComponent 
    implements IDropInListItemRenderer, IListItemRenderer 
{
    private var textField:UITextField;
    private var column:DataGridColumn;
    private var updateText:Boolean;

    //-------------------------------
    //  listData
    //-------------------------------

    private var _listData:DataGridListData;

    [Bindable("dataChange")]
    public function get listData():BaseListData
    {
        return _listData;
    }

    public function set listData(value:BaseListData):void
    {
        _listData = value as DataGridListData;
        column = _listData 
            ? DataGrid(_listData.owner).columns[_listData.columnIndex] as DataGridColumn 
            : null;
    }

    //-------------------------------
    //  data
    //-------------------------------

    private var _data : Object;

    public function get data() : Object
    {
        return _data;
    }

    public function set data(value:Object):void
    {
        if (_data == value) return;

        if (_data && _data is IEventDispatcher)
        {
            IEventDispatcher(_data).removeEventListener(
                PropertyChangeEvent.PROPERTY_CHANGE,
                propertyChangeHandler);
        }

        _data = value;
        updateText = true;

        if (_data && _data is IEventDispatcher)
        {
            IEventDispatcher(_data).addEventListener(
                PropertyChangeEvent.PROPERTY_CHANGE,
                propertyChangeHandler,
                false, 0, true);
        }

        dispatchEvent(new FlexEvent(FlexEvent.DATA_CHANGE));
    }

    private function propertyChangeHandler(event:PropertyChangeEvent):void
    {
        if (event.property == _listData.dataField)
        {
            updateText = true;
            invalidateProperties();
        }
    }

    //---------------------------------------------------------------------
    //
    //  Overrides : UIComponent
    //
    //---------------------------------------------------------------------

    override protected function createChildren():void
    {
        super.createChildren();

        textField = new UITextField();

        // hardcoded size and position to confine the redraw region, 
        // since it never changes in this example
        textField.width = 130;
        textField.height = 16;
        textField.x = 3;

        addChild(textField);
    }

    override protected function commitProperties():void
    {
        super.commitProperties();

        if (updateText)
        {
            updateText = false;
            textField.text = column.itemToLabel(data);
        }
    }
}

Some compromises are made in this item renderer. It's not as general-purpose as the DataGridItemRenderer and its creation-time is going to be higher, since it uses a UIComponent to contain a UITextField, whereas the DataGridItemRenderer simply extends UITextField. However, at runtime the CPU will be reduced, since only a region of 130x16 will be redrawn for each property change instead of the whole grid.

Sample Application

The FastDataGrid.zip project contains an application, FastDataGridExample.mxml, that demonstrates the default and optimized data grids. You can use this as a starting point to refine the grid and item renderer and perhaps make further optimizations or better generalizations. The project contains two implementations of the item renderer with comments describing the differences.

Conclusion

If your application contains data grids that change frequently, use the "Show Redraw Regions" feature of Flash Debug Player to make sure that excessive redrawing is not taking place. If so, consider optimizing the data grid and your item renderers to minimize redrawing and reduce CPU. The same technique described in this blog post also applies to the AdvancedDataGrid.

Posted by tsugden at 10:16 AM | Comments (2)

February 22, 2010

How to Unload Modules Effectively

It's well known that unloading modules in Flex can be tricky. The principle is simple: remove all references to the module and it will become eligible for garbage collection. The harder part is that those references can exist in unexpected places. Did you know that the code generated by the Flex 3 MXML compiler when processing a CSS stylesheet can sometimes cause a module to be pinned in memory? There are a number of causes of memory leaks like this, but they can be avoided by following certain practices.

This blog post demonstrates how to unload a Flex 3 module effectively that is styled and localized. It applies several of the techniques described in Alex Harui's earlier post, What We Know About Unloading Modules. Note that Flex 4 (out now!) and Flash Player 10.1 both bring improvements in the area of memory management, such as the modular styles sub-system in Flex 4, so not all of the points below are applicable to Flex 4 projects.

Getting Started

The best way to understand module unloading is to create a sample project and run some tests. Here's a sample Flash Builder project that uses Flex SDK 3.5 to demonstrate the effective unloading of a module. A number of practices are applied and these are described later.

The sample project contains an application that loads and unloads a module repeatedly, displaying the System.totalMemory reading. Click the image below to launch the application:

ExampleApp.png

You can click the "Load/Unload" toggle button to load and unload the module manually, or the "Run Stress Test" button to begin repeatedly loading and unloading the module for a given number of repetitions. A checkbox can be used to control whether or not the garbage collector is forced after each unload. When running the application from Flash Builder in debug mode, you should see the following trace produced by Flash Player when the "Unload" button is clicked:

[Unload SWF] Workspaces:MyWorkspace:ModuleUnloading:bin-debug:module:MyModuleStyles.swf
[Unload SWF] Workspaces:MyWorkspace:ModuleUnloading:bin-debug:module:MyModule.swf

This trace indicates that the module and associated compiled CSS SWF have both been successfully unloaded. When running the stress test in the release Flash Player, you should observe the memory falling after each unload and no excessive accumulation of memory, which would indicate a module unloading memory leak. The figure below shows the expected profile when a module is not unloading effectively on the left-side, with the expected profiling when unloading is successful on the right-side:

MemoryProfile.png

You can then make alterations and observe the impact on memory consumption. If you make some changes and you no longer observe this trace, then you have pinned the module in memory, creating a memory leak! Use the memory profiling features of the Flash Builder Profiler to see which objects exist before loading and after unloading, and use your operating system tools (Task Manager, Activity Monitor, etc.) to watch the real memory footprint of the browser process while running the application.

Note that when using the Flash Builder Profiler you need to be running the debug version of Flash Player, but while observing the operating system process, you need to use the release player to get an accurate picture. It's handy to have the installers for both versions to hand, or if you're running on Windows, you might install one version in Internet Explorer and the other in Firefox. The FlashSwitcher plug-in for Firefox is another option.

The rest of this article explains the practices that have been followed in the sample application to ensure the module unloads effectively. While reading it, you can refer to the comments inside the source code that highlight each practice.

Practices for Unloading Modules

Take Care With Style Tags

When the MXML compiler processes a Style tag, it generates some code for registering the specified style selectors with the StyleManager. This code produces a back-reference from the 'StyleManager' to the auto-generated class that performs the registration. If a Style element is used inside an application that is no problem, but if one is used inside a module the result is a reference to a class inside that module. This reference pins the module in memory preventing unloading.

In the sample application, you can see that a Style tag is used in the shell application for loading a CSS file that contains some type selectors and some class selectors.

<mx:Style source="app/MyAppStyles.css"/>

If you add the -keep compiled argument, then you will be able to see the style registration code that is auto-generated by the MXML compiler. Note that the specifics of the auto-generated code can change between releases, but with Flex 3.5, a file called MyApp-generate.as will be produced that has a function called _MyApp_StylesInit(), which performs the actual registration.

You'll also notice that no Style tag is used inside the module. Instead, style selectors that are specific to the module are specified in the module/ModuleStyles.css file and compiled into a SWF that is loaded and unloaded together with the module. More details on this later.

Compile Resource Bundles Into The Shell Application

In a similar manner to styles, when the MXML compiler processes some [ResourceBundle] metadata, some code is auto-generated to register the resources from the specified properties file with the ResourceManager singleton. This code creates a reference to the auto-generated class, so if it takes place inside a module, the module will end up being pinned in memory. Instead, the resources need to be compiled into the shell application or else loaded at runtime using a resource module.

In the sample application, the resources for the module are compiled into the shell application:

<mx:Metadata>
    [ResourceBundle("app")]
    [ResourceBundle("module")]
</mx:Metadata>

If the resource bundles are not large and are known at compile time, and you don't need to switch locale at runtime, then this is the simplest approach. If the resource bundles are large, unknown at compiled time or you do need to switch locale, then using resource modules is a better approach. Refer to the Flex documentation for instructions to compile resource modules. They can be loaded and unloaded via the ResourceManager.

Link Components With Default Styles (or keep all type selectors)

When the MXML compiled encounters a component that has default styling specified in a type-selector of a CSS file inside the Flex SDK or another referenced CSS file, it will auto-generate code to register those styles. If that registration happens inside a module, the module will be pinned in memory in the same way as with a Style tag.

There are two ways to avoid this. The first is to statically link components with default styles into the shell application. Then the default style registration will take place when the application starts up and not when the module is loaded. This can be seen in the sample application:

Button;
RadioButton;
CheckBox;
ComboBox;
TextInput;
DataGrid;
DataGridColumn;
Module;

Alternatively, the -keep-all-type-selectors compiler argument can be used to force the compiler to generate style registration code for all known type selectors, regardless of whether or not the components are linked into the application.

This is one of the areas of improvement in Flex 4. Instead of registering style selectors with the StyleManager singleton, Flex 4 features a revised styles sub-system that registers styles with each module, preventing the issue described above.

Link Classes with RemoteClass Metadata

When [RemoteClass] metadata is encountered, the MXML compiler auto-generates code to register the remote class mapping with Flash Player. Before Flash Player 10.1b3, this resulted in a reference that can prevent module unloading. To avoid this issue with earlier versions of Flash Player, classes with such metadata can be linked into the shell application. Then the remote class registration takes place at application start-up and doesn't pin the module in memory.

The use of HTTPService in the module appears to cause this issue, presumably because it links other classes with [RemoteClass] metadata. For this reason, the HTTPService is statically linked into the shell application too:

HTTPService;

This issue has been resolved in Flash Player 10.1b3. For earlier versions, the above work-around can be used. An alternative approach that also works is to compile the remote classes into a separate module that is loaded at start-up.

Move Focus Out of Modules Before Unloading

The Flex FocusManager holds a reference to the component currently in focus. If that component is inside a module, then that reference will pin the module in memory preventing unloading. When a module is unloaded it is necessary to move the focus onto a component outside of the module. This is the user experience that would generally be desired anyway, so the next appropriate control is in focus for the user to continue their work.

In the sample application, a TextInput has been declared:

<mx:TextInput id="textInput"/>

Just before unloading takes place, the focus is moved to this TextInput:

textInput.setFocus();

There are also cases where Flash Player will hold a reference to the last interactive control, but this will be released as the user proceeds to interact with other parts of the application.

Load Modules Into Child Application Domains

When a module or compiled CSS SWF is loaded, an application domain can be given. This is the application domain into which the class definitions inside the SWFs are loaded and stored. These class definitions can only be unloaded when no references to instances remain and no reference to the application domain itself remains. For this reason, it is important to load modules into a child application domain and not the current application domain if you wish to unload them. The current application domain contains the shell application classes and can never be made eligible for garbage collection.

In the sample application, a child application domain is created, then used for loading both the module and compiled CSS SWFs:

var childDomain:ApplicationDomain = 
    new ApplicationDomain(ApplicationDomain.currentDomain);

ml.applicationDomain = childDomain;
ml.loadModule("module/MyModule.swf");

StyleManager.loadStyleDeclarations(
    "module/MyModuleStyles.swf", 
    true, false, childDomain);

When it comes to unloading, it is important to nullify the reference that the ModuleLoader component holds to the child ApplicationDomain, so it can be garbage collected:

ml.applicationDomain = null;

For more information about application domains and their subtle nuances, refer to the following blog posts:

Use Compiled CSS for Module Styling

When the MXML compiler is used to compile a CSS file into a SWF, the generate code contains both registration and de-registration functions. This means that compiled CSS supports unloading, so it is a good approach for styling modules. Any selectors that are unique to the module can be compiled into a stylesheet SWF that is loaded together with the module, then unloaded again afterwards.

This approach can be seen in the sample application. The module/MyModuleStyles.css is compiled into module/MyModuleStyles.swf and the StyleManager is used to load the SWf into a child application domain and unload it again afterwards:

StyleManager.loadStyleDeclarations(
    "module/MyModuleStyles.swf", 
    true, false, childDomain);
...
StyleManager.unloadStyleDeclarations(
    "module/MyModuleStyles.swf");

Consider Forcing the Garbage Collector

When the unload() method is called to unload a module, there is no guarantee that it will be immediately garbage collected. If there are no remaining references, then it will becomes eligible for garbage collection (GC) and may be removed later, when Flash Player chooses to run the garbage collector. The System.gc() method will force the garbage collector to run in debug versions of Flash Player, but there is no such API for release versions at present.

There is however an unsupported hack known to prompt the garbage collector in most versions of Flash Player. This is documented, along with various caveats, in Grant Skinner's blog post:

By attempting to open 2 local connections then swallowing the resulting error, a full mark-and-sweep garbage collection is instructed:

try
{
    new LocalConnection().connect("anything");
    new LocalConnection().connect("anything");
}
catch (e:*) {} // ignore intentionally

The sample application contains a check-box that allows you to control whether or not this hack is applied after each unload. In my simple tests, the memory footprint remained lower when the hack was applied than without, but I have not conducted exhaustive testing and this technique is unsupported by Adobe. Note however that there is an enhancement request to provide an official API for forcing the garbage collector, which you are free to vote for:

JP Auclair's blog contains a detailed explanation of the Flash Player garbage collection algorithm:

Understanding the Expected Behaviour

When profiling module unloading, it is important to understand the expected behaviour and not misinterpret the memory footprint of the browser process as indicative of a memory leak. When a module is unloaded and the garbage collector has cleaned it up, the memory footprint should not be expected to return all the way to the base-line level. This is because the pages of memory that Flash Player and the browser have allocated from the operating system may not be completely empty. Blocks within these pages may have been allocated to different objects in the application. Only empty pages can be returned to the operating system. So after unloading and garbage collection you should expect some reduction, but when the next module is loaded, the holes in the existing pages will be filled before more pages are grabbed from the operating system. Overall the memory footprint is expected to be lower when applying unloading than without.

Unloading is Not Always Necessary

It should be noted that for some applications unloading is not necessary and a load-once policy may be adequate. Furthermore, some applications may require more elaborate policies, where some modules are unloaded, but the modules most frequently used by users are always kept in memory. Remember that there are many benefits to modularization, not least of which is the efficiency gains for developers working on distinct functional areas that can be developed, tested and profiled in greater isolation.

If an application has a relatively small number of modules and the overall memory footprint when every module has been loaded is acceptable, then perceived performance will be better and programming easier if unloading is not used. Similarly, if a user is only entitled to access a subset of the available modules, it may be acceptable to leave them in memory after loading. It is better to load-once-and-leave rather than unsuccessfully attempting to unload and creating a memory leak.

Light-Touch Optimization

There are many ways to optimize a Flex application to improve perceived performance and reduce resource consumption. However, optimization can often have an impact on code simplicity and clarity, so the benefit needs to outweigh this cost. The best kind of optimization has a light touch on the existing code base. Where manual clean-up code ot hacks are deemed necessary, it is best to try and isolate this code and not to allow it to spread widely through the codebase. For instance, the extensive use of dispose() methods has sometimes been advocated, but this typically complicates a large number of application classes. The example in this blog post gets by without such methods.

Conclusion

Flex provides simple yet powerful features for building modular applications, but some care needs to be taken to ensure that modules unload effectively. This article demonstrated some practices for ensuring that styled and localized Flex 3 modules do unload. These can be used to reduce the overall memory footprint of large, modular applications. The latest betas of Flash Player 10.1 and Flex 4 both introduce further improvements that help to simplify memory management, such as the modular styles sub-system in Flex 4 and the new explicit disposal APIs in Flash Player 10.1.

For more information about memory management in Flex, refer to the comprehensive series of blog posts written by Alex Harui. These are essential reading for enterprise Flex developers:

Posted by tsugden at 9:58 PM | Comments (0)

February 9, 2010

The Flexible Configuration Options of Parsley

One of the nice design decisions taken by Jens Halm when he created the Parsley Application Framework was to separate the configuration mechanism from the core of the framework, so different forms of configuration can be used as required. This idea in itself is not new, since Martin Fowler advocated it in his 2004 paper, "Inversion of Control Containers and the Dependency Injection pattern":

"My advice here is to always provide a way to do all configuration easily with a programmatic interface, and then treat a separate configuration file as an optional feature. You can easily build configuration file handling to use the programmatic interface. If you are writing a component you then leave it up to your user whether to use the programmatic interface, your configuration file format, or to write their own custom configuration file format and tie it into the programmatic interface" - Martin Fowler

The application of this design principle is particularly effective in Parsley. While some frameworks are restricted to specific configuration mechanisms, Parsley provides programmatic interfaces for synchronous and asynchronous configuration, and several out-of-the-box implementations. These interfaces provide an extension-point so developers can plug-in their own configuration processors when the need arises.

Configuration Processors

In Parsley, a configuration processor uses some form of configuration data to build up a registry of object definitions. This process is abstracted by the following interfaces:

Parsley has a number of standard implementations, including the following two most commonly used:

The developer manual has a section explaining how to extend the framework with a new configuration processor:

Example: Modular Configuation Processor

There are various reasons to write a custom configuration processor. Perhaps you want to support your own particular configuration files, loaded and processed at runtime. However, these interfaces open up some other doors for more interesting forms of configuration. For example, they can be used to process configuration data from a compiled module.

Consider a large, modular application. Let's say the application consists of a Flex shell application that loads 20 modules, and 10 of these rely on the same set of shared services. It's undesirable to compile these services into the shell application, where they could be inherited by the modules, since the shell should have no knowledge of these lower level details. Instead they could be compiled into a module and the shell application could load that module at start-up, so the services are available for inheritance, but there is no dependency imposed on the shell.

This can be achieved quite simply by writing a new configuration processor, something like this:

package com.adobe
{
     import flash.events.ErrorEvent;
     import flash.events.Event;
     import flash.events.EventDispatcher;

     import mx.events.ModuleEvent;
     import mx.modules.IModuleInfo;
     import mx.modules.ModuleManager;
     import mx.utils.StringUtil;

     import org.spicefactory.parsley.core.builder.AsyncConfigurationProcessor;
     import org.spicefactory.parsley.core.builder.ConfigurationProcessor;
     import org.spicefactory.parsley.core.registry.ObjectDefinitionRegistry;

     public class ModularConfigurationProcessor 
          extends EventDispatcher 
          implements AsyncConfigurationProcessor
     {
          private static const MODULE_LOADING_ERROR : String = 
               "Unable to load the module at URL {0} due to {1}";
          private static const MODULE_INCOMPATIBLE_ERROR : String = 
               "The module doesn't implement the ConfigurationProcessor interface.";

          private var url : String;
          private var module : IModuleInfo;
          private var registry : ObjectDefinitionRegistry;

          public function ModularConfigurationProcessor( url : String )
          {
               this.url = url;
          }

          public function cancel() : void
          {
               module.removeEventListener( ModuleEvent.READY, moduleReadyHandler );
               module.removeEventListener( ModuleEvent.ERROR, moduleErrorHandler );
          }

          public function processConfiguration(
               registry : ObjectDefinitionRegistry ) : void
          {
               this.registry = registry;
               module = ModuleManager.getModule( url );
               module.addEventListener( ModuleEvent.READY, moduleReadyHandler );
               module.addEventListener( ModuleEvent.ERROR, moduleErrorHandler );
               module.load( registry.domain );
          }

          private function moduleReadyHandler( event : ModuleEvent ) : void
          {
               try
               {
                    processConfigurationWithModule();
                    dispatchEvent( new Event( Event.COMPLETE ) );
               }
               catch ( e : Error )
               {
                    dispatchErrorEvent( e.message );
               }

          }

          private function processConfigurationWithModule() : void
          {
               var instance : Object = module.factory.create();

               if ( instance is ConfigurationProcessor )
               {
                    ConfigurationProcessor( instance ).processConfiguration( registry );
               }
               else
               {
                    throw new Error( MODULE_INCOMPATIBLE_ERROR );
               }
          }

          private function moduleErrorHandler( event : ModuleEvent ) : void
          {
               dispatchErrorEvent( MODULE_LOADING_ERROR, url, event.errorText );
          }

          private function dispatchErrorEvent( message : String, ... rest ) : void
          {
               dispatchEvent( new ErrorEvent(
                    ErrorEvent.ERROR,
                    false,
                    false,
                    StringUtil.substitute( message, rest ) );
          }
     }
}

The processor is initialized with the module URL. It loads the module, creates an instance, then checks whether the module itself is a configuration processor. If so, it delegates configuration processing to the module. Here's an example module:

package com.adobe
{
    import mx.modules.ModuleBase;

    import org.spicefactory.parsley.asconfig.processor.ActionScriptConfigurationProcessor;
    import org.spicefactory.parsley.core.builder.ConfigurationProcessor;
    import org.spicefactory.parsley.core.registry.ObjectDefinitionRegistry;

    public class MyModule extends ModuleBase implements ConfigurationProcessor
    {
        public function processConfiguration(
            registry : ObjectDefinitionRegistry ) : void
        {
            new ActionScriptConfigurationProcessor(
                [ MyModuleConfiguration ] ).processConfiguration( registry );
        }
    }
}

Parsley's extension points can be taken a little further by writing a complementary configuration tag:

package com.adobe
{
    public class ModularConfig implements ContextBuilderProcessor 
    {
        public var url : String;

        public function processBuilder( builder : CompositeContextBuilder ) : void 
        {
            builder.addProcessor(
                new ModularConfigurationProcessor( url ) );
        }
    }
}

So now a modular configuration can be easily combined with other forms of Parsley configuration using the usual MXML tags:

<mx:Application ... xmlns:sf="http://www.spicefactory.org/parsley">

    <sf:ContextBuilder>
        <sf:FlexConfig type="{ MyShellApplicationConfig }"/>
        <adobe:ModularConfig url="MyModularConfig.swf"/>
        <adobe:ModularConfig url="MyOtherModularConfig.swf"/>
    </sfConfigBuilder>

    ...

</mx:Application>

Here the shell application configuration will be combined with the configuration from two modules to form a Parsley context that can be inherited by other modules, loaded later on-demand.

Conclusion

When creating a framework, it is wise to define generic interfaces for configuration processing, so that different formats can be used where appropriate. In many cases programmatic configuration with MXML is the simplest and most desirable option, but there are several valid cases for configuration from XML and other kinds of file (including SWFs) loaded at runtime. The configuration interfaces provided by Parsley satisfy this requirement very well.

Posted by tsugden at 10:25 PM | Comments (0)

December 10, 2009

Writing Genuinely Reusable Flex Components

On larger projects and within enterprises, there's often a case for extracting a set of reusable components into a Flex library project. In theory, the same components can be reused across modules and sub-applications of multiple Flex or AIR clients, bringing greater consistency and more rapid development. However, in practice there are some common mistakes that limit the reusability of components. This post explains what makes a component genuinely reusable and highlights some techniques from the Flex SDK that can be applied to your own components to make them more reusable.

What Makes a Component Genuinely Reusable?

There are different levels of reusability, but a fully reusable component should be able to render any kind of data. It should be equally comfortable with an array of basic, dynamic Objects or a collection of concrete Kangaroos. The Flex DataGrid has this quality:

<mx:DataGrid dataProvider="{ kangaroos }">
   <mx:columns>
      <mx:DataGridColumn headerText="Name" dataField="name"/>
      <mx:DataGridColumn headerText="Weight" labelFunction="calculateWeight"/>
   </mx:columns>
</mx:DataGrid>

Notice how the dataField and labelFunction properties tell the component how to get its data from the Kanagoo objects without imposing any dependency. These are two of the mechanisms available to make a component genuinely reusable. Even if the developer has no control over the Kangaroo class itself, perhaps it's part of a 3rd-party library, they can still easily render these objects within a DataGrid.

The Data Interface Anti-Pattern

One common mistake is to insist that the data being rendered by a component implements a specific interface. For example, consider a DistributionBar component that renders a simple graph, like that shown in Figure 1.

DistributionBar.png

Figure 1 - A Distribution Bar Component

The distribution bar shows a number of regions with different sizes, each containing a label. It's tempting to configure this using an array of IRegion objects:

public interface IRegion
{
   function get label() : String;
   function get size() : int;
}

The distribution bar can then extract the size and label information for each region through this interface. The rationale for this is that the interface decouples the component from the concrete objects it renders. Anything can be rendered so long as it implements IRegion, but that so long is in fact the design flaw. By imposing the use of the IRegion interface the reusability is limited. The interface needs to be added to existing model classes before they can be rendered in a distribution bar, and worse, if the models were produced by another library or separate team, it might not be an option to change them, so they'd need to be wrapped. For these reasons, the component is not genuinely reusable.

Reusable Components of the Flex SDK

The Flex SDK contains many reusable components and this is achieved by applying a few standard approaches:

  1. Data Fields
  2. Data Functions
  3. Data Descriptors
  4. Factory Objects

These approaches are now described and the same techniques can be applied to your own components to make them reusable.

Data Fields

A data field is a String property that specifies the name of another property. For example, the labelField property of the ComboBox or the dataField and dataTipField properties of the DataGridColumn:

<mx:ComboBox dataProvider="{ items }" labelField="name"/>

The component implementation uses the data field to read the data values from the items it renders. For example:

for each (var item:Object in dataProvider)
{
    var value:Object = item[dataField];
    // do something with the value
}

This is a simple approach but it offers great flexibility. The component can render any readable property of any class of object.

Data Functions

A data function is a property of type Function that is used to specify a reference to another function. For example, the labelFunction property of the ComboBox or the dataFunction property of the DataGridColumn.

<mx:DataGridColumn headerText="weight" dataFunction="calculateWeight"/>

The component then invokes the data function, typically passing through an item of data as a parameter. For example:

for each (var item:Object in dataProvider)
{
    var value:Object = dataFunction(item);
    // do something with the value
}

This approach is similar to using a data field, but offers even more flexibility, since the function can perform calculations or formatting before returning a value to the component for rendering.

Data Descriptors

A data descriptor is an interface through which a component can analyze the items of data it renders. The developer can then pass their own implementation of the interface to the component in order to configure it. An example can be seen in the Tree component of the Flex SDK:

<mx:Tree dataProvider="{ items }">
    <mx:dataDescriptor><my:MyDataDescriptor/></mx:dataDescriptor>
</mx:Tree>

The tree can then discover characteristics of the data by querying its data descriptor interface. For example:

for each (var item:Object in dataProvider)
{
    var isBranch:Boolean = dataDescriptor.isBranch(item, dataProvider);
    // do something with the outcome
}

This approach is very powerful, but is only necessary for complex components such as the Tree. The effort of using the component is more than a simple List or ComboBox, but the component is still completely decoupled from the data it renders. If a developer needs to render a new class of object in a tree, they typically write a new implementation of the ITreeDataDescriptor interface.

Factory Objects

A factory object, in terms of component developent, is a property of type IFactory that is used to instantiate children at runtime. For example, the itemRenderer property of the List and DataGrid or the dropdownFactory property of the ComboBox.

<mx:List dataProvider="{ items }" itemRenderer="my.package.MyItemRenderer"/>

The component uses the standard IFactory interface of the Flex SDK to create new objects at runtime:

var itemRenderer:Object = itemRenderer.newInstance();

Then it passes data items into the new object through the IDataRenderer interface:

if (itemRenderer is IDataRenderer)
{
    IDataRenderer(itemRenderer).data = item;
}

This approach gives great control over the visual appearance of parts of the component. By providing custom item renderers, completely different results can be achieved. The logic for processing the item of data can be as simple or complex as needed and it can be encapsulated inside the item renderer class. Having said that, components that use factories should define sensible default values, so the component is easy to use in simple cases without setting special factories. This is the case for all the ListBase components, such as DataGrid, which uses the general purpose DataGridItemRenderer by default.

It's worth noting here that the Flex compiler has a special relationship with properties of type IFactory. When it notices such a property being set in MXML, it will automatically generate code to convert class names and in-line components into instances of ClassFactory. This makes the components easier to use, so developers don't usually need to instantiate class factories manually, but instead just specify a class name or declare an in-line component.

Conclusion

When fully reusable components are needed, it's good to remember the simple rule: a reusable component should be able to render any kind of data. This is best achieved by following the conventions laid out in the Flex SDK, such as data fields, data functions, data descriptors and object factories. It's important to resist the urge to introduce new interfaces that impose unnecessary obligations on users of your component, because to do so limits its reusability.

Postscript: Since fully reusable components tend to use mechanisms such as dynamic property lookups and function references, there are some trade-offs to consider. These techniques are slower than accessing properties of strongly-typed objects and they're not resiliant to compile-time type checking. However, the advantages of flexibility and reduced dependencies can outweigh these drawbacks for larger projects and enterprises.

Posted by tsugden at 9:18 AM | Comments (2)

November 23, 2009

Modularity in Flex Applications

There has been talk recently about the trend towards inversion-of-control frameworks in Flex. In addition to this, a higher-level movement can be seen towards modularity frameworks. Such an approach to architecture can bring many benefits, particularly in enterprise settings with larger teams and formal release processes. This post explains what modularity means in the context of Flex and discusses some of the benefits and options available for implementation.

What is Modularity?

Modularity is something more general than the Flex Module and ModuleLoader components. These are a form of modularity, but the principle is broader: modularity is about separating applications into smaller units that can be developed and deployed independently. In the context of Flex, these units might be modules, sub-applications or any other kind of encapsulated content. A modular application usually has a structure like that shown in Figure 1.

Modularity Figure.002.png

Figure 1 — The structure of a modular application

In the figure above, the application consists of a thin shell that loads three modules: Dashboard, Contacts and Messages. These modules represent different functional areas of the application. Their implementation detail is independent of one another. The application shell is responsible for loading and laying out the modules, and providing a means of communication between them. This might be a global data model, a registry of interfaces, or in the case of Figure 1, some kind of message bus.

What are the Benefits of Modularization?

Modularization can bring benefits for end-users of an application and also for the teams that develop and deliver them; Here are some of the benefits:

  • A module can be developed, tested and profiled in relative isolation.
  • Build times are shortened, since changes to one module don't require other modules or the shell application to be recompiled.
  • Modules can be loaded on demand, so the initial download for an application is smaller.
  • If a user never uses the features of one module, that module need not be loaded.
  • Individual modules can be deployed into production, instead of redeploying an entire application.
  • Different modules can be loaded for different users based on their entitlements.
  • A module is easier to understand and maintain than a monolithic application, since it is more functionally cohesion.
  • The interactions between modules can be confined to a thin API, reducing regression as an application grows.

The benefits in terms of architecture and development efficiency are arguably the most important. Small groups of developers can work on individual modules. The contracts of communication between modules can be agreed, so the implementation is free to change and improve without regression.

Framework Provisions for Modularization

Some frameworks have features to help with building modular applications. PureMVC was probably the first to do so with the multi-core version of the framework and its Pipes utility for controlled flow of messages between modules. The Parsley application framework is also designed to support modular applications, in which a module would normally have its own inversion-of-control context that inherits object definitions from the context of a shell application. Parsley includes a messaging framework that can be used for loosely-coupled communication between modules.

The other frameworks are hot on their heels. Spring Actionscript will have modular support as of version 0.9, where each module can have its own context with the option to inherit the object definitions of its parent. A parent context will be able to control which object definitions are exposed to a child context. And the lightweight Swiz framework is going to improve its support for modular development in the near future with its 1.0 release.

In addition to these established frameworks, there are some emerging frameworks focussed more specifically on modularity. Potomac and Adobe Gravity are two new frameworks inspired by OSGi, the dynamic module system for Java. Both aim to provide the infrastructure for loading bundles (i.e. modules) at runtime and communicating between them through service interfaces. Adobe Gravity remains an Adobe internal project but there are plans for a future open-source release, while Potomac is publicly available and includes some developer tooling in the form of a Flash Builder plug-in.

Cairngorm 3 and Modularization

One of the key messages of the Cairngorm 3 reference architecture (currently in draft) is to separate applications into distinct functional areas that can be developed independently; in other words a modular architecture, where each functional area is a module. The Cairngorm guidelines recommend that communication between functional areas takes place through a thin API that might consist of interfaces, events and data transfer objects. This approach minimizes dependencies between the distinct functional areas of an application. It promotes what Robert Martin terms "good dependencies" in the direction of stability. More details can be found in the Creating Functional Areas draft document.

The Enterprise Solution: Adobe LiveCycle Mosaic ES2

LiveCycle Mosaic ES2 is far more than a modularity framework, but modularity is an important part of its design. It's a client-and-server-side technology for building applications by composition, combining different pieces into personalized views that are focussed on the activities that different users perform. On the server-side, reusable application assets can be stored and shared, while LiveCycle DataServices is available for integrating with different data sources. On the client-side, applications are assembled from different tiles, that can be developed from scratch or adapted from existing Flex and HTML applications. A customizable shell application loads and lays out the tiles, and the framework provides a publish-subscribe messaging API for communication between them. For more details about the Adobe enterprise solution to modularity, refer to the LiveCycle Mosaic ES2 product page.

Conclusion

As we attempt to build bigger and better applications in Flex and AIR, the topic of modularity becomes more important to ensure efficient development, scalability and maintainability. It is vital to be able to separate portions of a large application so they can be developed, tested and deployed independently. The Flex SDK provides some simple means of modularization with Modules and Sub-Applications, and some frameworks build on top of these to provide more features. Furthermore, Adobe has now released an enterprise solution in LiveCycle Mosaic ES2 that provides the infrastructure for rapidly developing and deploying RIAs to the browser and desktop in a modular way.

Posted by tsugden at 10:32 PM | Comments (0)

September 21, 2009

Morgan Stanley Matrix at MAX

Next month at Adobe MAX 2009 in Los Angeles, Børre Wessel (Lab49) and myself will be presenting Matrix, the next-generation sales and trading platform from Morgan Stanley.

ms-matrix-1.jpg

The session is entitled, "Building Matrix – Scaling Flex for a Large Trading Application" and it will cover our experiences as we worked on the client-side architecture of Matrix, together with a large team of developers. We will discuss the patterns and practices that made the delivery possible — modularization, dependency management, performance tuning, inversion-of-control, etc. — and the challenges we faced along the way, providing guidance for other large-scale enterprise Flex projects.

For more information, put on your headphones and check out the Morgan Stanley Matrix micro-site. Some other resources are listed below. Hope to see you there!

Posted by tsugden at 9:40 PM | Comments (2)

September 13, 2009

Coming Soon: Cairngorm 3

Cairngorm is about to undergo a transformation that will broaden its scope and increase its value to clients and partners. Instead of centering around a specific implementation of the Model-View-Controller pattern, Cairngorm 3 will consist of a set of best practices, tools and libraries, many of which apply across frameworks. This is the knowledge gathered by the Adobe Technical Services organization and partners over the last five or six years, condensed and presented to help others to deliver successful Flex projects in the enterprise.

For those familiar with the Cairngorm 1 & 2 micro-architecture, the traditional Cairngorm library remains a part of Cairngorm 3. Our best practices for applying the Cairngorm library will be shared, together with information about tried-and-tested extensions. And like before, Cairngorm 3 will recommend a layered architecture that separates concerns and supports test-driven development, but this time the approach has been refined and most parts of it complement multiple frameworks.

So what is Cairngorm now if it isn’t just a framework? Well it’s a foundation for delivering successful Flex projects. It’s the answer to the question, “What are the best practices for Flex in the enterprise?”, to help set new teams off on the right track. It’s the information that can’t always be found in official documentation or framework manuals. It's going to be accessible, informative and open-source. And it’s coming soon...

Posted by tsugden at 8:57 PM | Comments (6)

September 4, 2009

Eliminate Common Bad Practices with FlexPMD

Adobe Technical Services are pleased to release FlexPMD to the community. FlexPMD is a new open-source tool for improving code quality. It works by analyzing ActionScript and MXML source files to identify common bad practices, like over-long classes or functions, reliance on magic strings, unused parameters, and many other programming mistakes or misjudgments. It can even spot code that might be causing performance problems, and furthermore, the ruleset can be customized and extended, to include rules specific to the coding conventions of your own project.

FlexPMD can be launched from the command line, but the best practice is to invoke it from your Ant or Maven continuous integration build scripts. In this case, a report will be generated each time a build is performed, describing the violations of your chosen ruleset. As soon as someone checks in their fancy new 1,000 line algorithm with nested loops, numeric variable names and a gaggle of change-watchers, the red flag will be raised. The report produced by Flex PMD allows simple coding mistakes to be identified and corrected immediately, when the cost is far cheaper than attempting to refactor a system that has been decaying for months.

FlexPMDReport.jpg

Figure 1: A Sample FlexPMD Report

Another benefit of FlexPMD is that code reviews become more valuable. Since FlexPMD can automatically identify the common problems, code reviewers are left to reflect on the deeper issues, like domain modeling, separation of concerns, proper encapsulation, and so on.

To find out more about FlexPMD and start using it on your own projects, follow these links to Adobe Open Source:

Credit goes to Xavier Agnetti for conceiving and leading this effort, with support and contributions from many in the Technical Services organization and other parts of Adobe.

Posted by tsugden at 7:21 AM | Comments (0)

August 24, 2009

Applying the Presentation Model in Flex

Introduction

The purpose of the Presentation Model design pattern is simple to grasp: it’s a way of moving state and logic out of a view component and into another class, where it can be developed, comprehended and unit tested more easily. The pattern promotes a clean separation of concerns, helping to keep MXML views as structural definitions, free from Script-block logic. However, there are several ways to apply presentation models in Flex. Some of these can help us to build simpler, more testable systems, while others may lead towards entanglement and maintenance difficulties.

For a comprehensive introduction to the Presentation Model pattern and a comparison between it and other presentation patterns, please refer to Paul Williams' blog series on the topic. At Adobe Professional Services, we have found the Presentation Model to be well suited to Flex, since it takes advantage of language features like bindings and in-line event handlers, while side-stepping some of the difficulties of unit testing display objects.

This article discusses two approaches to applying the Presentation Model -- hierarchical and componentized -- and makes a recommendation in favour of the latter.

Two Approaches: Hierarchical or Componentized

Hierarchical

With the hierarchical approach to the Presentation Model (PM) pattern, a hierarchy of PMs is developed that reflects the hierarchy of view components, as shown in the Figure 1.

PM1.png

Figure 1 - A hierarchy of views and presentation models.

Each MXML view component has a corresponding PM. The parent view contains the child views, and likewise, the parent PM contains the child PMs. Below these View-to-PM pairs, there are only basic UI controls, implemented in ActionScript without corresponding PMs.

With this approach, the top-level PM is usually instantiated in the top level view. It may be a singleton to allow other parts of the application, such as commands to access it as required. The child PMs are then passed down manually into the child views, as shown below:

<example:MyComponent ... >
<mx:Script>
[Bindable] public var model:MyComponentPM;
</mx:Script>
<example:MyChildComponent model="{ model.myChildComponentPM }" ... />
...
</example:MyComponent/>

When a hierarchy of presentation models is established, coordination takes place by method calls between the PMs, sharing objects amongst them, and dispatching events up the hierarchy. So if a user selects an item from a DataGrid in View 2, a selectedItem property may be updated on PM 2 and an event dispatched to announce the selection. PM 1 may listen to this event and perform its own logic in response.

Componentized

With the componentized approach to the Presentation Model pattern, there is a single hierarchy of view components but no hierarchy of presentation models. Each view component has a corresponding presentation model, but these models are independent of one another, as shown in Figure 2.

PM3.png

Figure 2 - A hierarchy of components, each with a presentation model

With this approach, the PMs are usually injected into their corresponding views using the view-wiring mechanism of an IoC framework. For example, the following code could be used with Parsley 2:

<example:MyComponent ... 
   addedToStage="dispatchEvent(new Event('configureIOC', true'))">
<mx:Script>
[Inject] [Bindable] public var model:MyComponentPM;
</mx:Script>
<example:MyChildComponent/>
...
</example:MyComponent/>

Here the configureIOC event instructs Parsley to inject a presentation model instance, declared in a configuration file, into the model property of the view. Notice that there is no need to pass a model into the child component. Each component is self-contained and takes care of its own business.

A variation of this approach is to declare the presentation model directly in the view, as shown below:

<example:MyComponent ... >
<example:MyComponentPM id="model"/>
<MyChildComponent/>
...
</example:MyComponent/>

Although the presentation models are kept independent of one another with the componentized approach, there remains a need to coordinate the components in some way. This can best be achieved using an Inversion-of-Control (IoC) framework, such as Parsley, Swiz or Spring ActionScript. There are ways to do so:

  • Messaging - route messages directly between the models using whatever mechanism your preferred framework provides. Parsley 2 includes a loosely-coupled messaging framework; Swiz has a notion of mediated events; while Cairngorm MVC has a singleton event dispatcher that can serve a similar purpose.
  • Domain Interfaces - inject shared domain models into multiple PMs. Coordination then takes place when the PMs call methods on the interfaces to these models, and listen for events dispatched by them. All IoC frameworks support this feature.
  • Controller/Presenter - use separate classes, known as controllers or presenters, to coordinate multiple PMs. These classes are typically injected with a number of presentation models, using an IoC framework. The controllers then listen for events and invoke methods on the PMs.

The relative merits of these are not discussed here, but will be tackled in another article. Each approach achieves a similar result of de-coupling the components from one another. The two approaches to the Presentation Model pattern are now compared in terms of their responsiveness to change.

Responsiveness to Change

It is common for the visual designs of a user interface to change during development, based on user feedback, client demands, or moments of creative inspiration from the designers. In my experience, this usually happens when I've just put the finishing touches on the implementation, the pixels are perfectly aligned and the unit tests running green! So it's important to write code in such a way as to minimize the cost of change, allowing components to be manoeuvred from place to place and logic to be reused without great effort.

Consider the case where a view component needs to move from one region of the user interface to another. Starting with the simple hierarchy shown earlier in Figure 1, changes are required to four classes, now highlighted in red in Figure 3.

PM2.png

Figure 3 - Moving part of a View-and-PM hierarchy


View 3 needs to be detached from its starting place in View 1 and re-declared in View 2. Similarly, the reference to PM 3 contained in PM 1 needs be removed and introduced to PM 2. Any coordination logic that was in PM 1 also needs to be moved into PM 2.

In contrast, the componentized approach, first shown in Figure 2, responds more easily to change. Only the declaration of View 3 needs to be moved from View 1 to View 2, as highlighted in red in Figure 4.

PM4.png

Figure 4 - Moving a component within a hierarchy

Since the logic for the View-to-PM components is self-contained and coordination takes place externally, though messaging, domain interfaces, or controller/presenters, no further changes are necessary. If test-driven development is being practiced, the unit tests for the PMs also remain intact, whereas they would need to be refactored with the hierarchical approach.

Conclusion

The Presentation Model is a useful pattern for building testable Flex applications. By moving the state and logic used to present data and handle user gestures into PMs, it can be unit tested in isolation and understood more easily than Script-block logic placed directly within views. However, rich user interfaces can be somewhat volatile, changing their shape often during development, so it is recommended to apply a componentized version of the Presentation Model that is easy to adapt, rather than developing and ultimately maintaining a hierarchy of connected presentation models.

Posted by tsugden at 9:32 PM | Comments (6)

August 11, 2009

Best Practices for the Flex Logging Framework

Introduction

The Flex Logging Framework is easy to learn and flexible to use. It supports many different scenarios, from helping developers to debug their code, to sending the details of production application errors over the wire to a remote server for monitoring. To learn the basics of the Logging Framework refer to the latest Flex Developer Guide and the Flex Logging Framework chapter of Professional Flex 3. This document describes some best practices for applying the Logging Framework on enterprise projects.

The APIs provided by the Logging Framework are simple, but they need to be used properly to get the best out of them. If care is not taken, the benefits that logging can provide for debugging and monitoring an application in production can be lost. Performance problems can even be created. This article provides a set of best practices to keep the developers in your team on the right track.

Best Practices

The following best practices are covered:

  1. Get Loggers by Class
  2. Declare Loggers as Static Constants
  3. Format Log Statements Consistently
  4. Parameterize Log Statements with Tokens
  5. Use Log Levels to Indicate Severity
  6. Use Log Filters for Focus
  7. Include Categories to Show Class Names
  8. Use Guard Conditions Appropriately
  9. Configure Logging at Runtime

Get Loggers By Class

Use a simple utility method to retrieve the logger for a particular class, instead of passing in the qualified class name as a string.

Good:

private static const LOG:ILogger = LogUtil.getLogger(MyClass);

Bad:

private static const LOG:ILogger = Log.getLogger(“my.package.MyClass”);

With the utility method approach, the class name can be refactored without needing to edit the string. Here is an implementation for the LogUtil.getLogger() method:

public static function getLogger(c:Class):ILogger 
{
    var className:String =  
        getQualifiedClassName(c).replace("::", ".")
    return Log.getLogger(className);
}

If performance profiling shows this method call to be a performance bottleneck, you may decide to revert to passing in the class name manually, since this will run a little faster. However, in most cases the convenience and refactor-ability of the above approach is the best practice.

Declare Loggers as Static Constants

In most cases, log statements apply to a particular class, so a logger should be declared as a static constant and not an instance variable.

Good:

private static const LOG:ILogger = LogUtil.getLogger(MyClass);

Bad:

private var log:ILogger = LogUtil.getLogger(MyClass);

Format Log Statements Consistently

Log statements should be formatted consistently and not haphazardly. This ensures that logging code looks professional and improves readability of log files (for humans or machines).

Good:

LOG.error(
    "Something bad has happened: event={0}, message={1}", 
    event.type, 
    message);

Bad:

LOG.error("-----------  SOMETHING BAD HAS HAPPENED!  -----------------");

Parameterize Log Statements

Parameterize log statements using the rest parameter and tokens, instead of appending strings manually. This produces cleaner code and prevents the composite string from being assembled in the event that no log target is registered.

Good:

LOG.debug(
    "Something interesting is happening: event={0}, message={1}", 
    event.type, 
    message);

Bad:

LOG.debug(
    "Something interesting is happening: event=" + 
    event.type + 
    ", message=" + 
    message);

Use Log Levels to Indicate Severity

Use the debug/info/warn/error log levels to indicate the severity of the message, instead of emphasizing important messages with special characters.

Good:

LOG.error("The service has failed and no data is available.");

Bad:

LOG.debug("!!! ERROR !!! The service has failed !!! ERROR !!!");

Use Log Filters for Focus

Set the log filters on your logging targets in order to focus on the logs produced by a certain part of the system. Do not instead try to make certain log statements stand-out with special characters. Remember to keep the format of log messages consistent.

Good:

target.filters = [ "my.important.package.MyClass" ];
target.level = LogEventLevel.INFO;
...
LOG.info("My important message");

Bad:

LOG.debug("----------- My really important message! -----------");
LOG.debug("<<<<<<<    another super important log!    >>>>>>>>");
LOG.debug("************* CAN YOU SEE ME????? ***************");

The trouble with the “special character” approach is that what is important for one developer one day is different to what is important for another developer on another day. If every developer uses their own notation for making their logs stand-out, the resulting log file becomes harder to read than when no emphasis has been placed in the text. Log levels and filters provide a more controllable and consistent mechanism for the same purpose.

Include Categories to Show Class Names

If you want to see the name of the class issuing a log statement, include categories for your log targets. Do not instead hard-code the name of the class into log statements.

Good:

target.includeCategory = true;
...
LOG.debug("Executing command");

Bad:

LOG.debug("<<<   Executing SynchronizationCommand   >>>");

The bad practice above is not refactor-safe. If the class is renamed, the message becomes confusing and if categories are included in the output, part of the message becomes redundant.

Use Guard Conditions Appropriately

Use guard conditions to prevent unnecessary processing where a log statement is expensive or nested within a loop or iteration. However, do not use guard conditions around every single log statement since this clutters up code. Decide whether or not the log statement may be expensive or use the profiler to verify this.

Good:

if (Log.isDebug())
{
    LOG.debug("Result received: {0}", ObjectUtil.toString(model));
}
for (var i:int = 0; i<10000; i++)
{
    if (Log.isDebug())
    {
        LOG.debug("Blah blah blah: i={0}", i);
    }
}

Bad:

LOG.debug("Result received: {0}", ObjectUtil.toString(model));
for (var i:int = 0; i<10000; i++)
{
   LOG.debug("Blah blah blah: i={0}", i);
   ...
}

In the bad practice above, the model will be converted into a string even if there is no registered target for the debug-level. Similarly, 10,000 log statements will be issues inside the loop regardless of whether or not there is a target registered.

Configure Logging at Runtime

Configure logging at runtime is a best practice, since it allows developers to change their log filters without rebuilding and also allows logging to be reconfigured in production without redeploying. Different log settings can even be applied to different users.

The process for configuring logging at runtime is beyond the scope of this article, however, the Parsley 2 Application Framework provides a feature for doing precisely that by loading an external XML file that specifies the targets, levels and filters. There are also examples available showing how to do the same thing using Prana/Spring ActionScript.

Posted by tsugden at 9:15 AM | Comments (2)

July 18, 2009

A Declarative Approach to Flex Popups

Yaniv De Ridder and myself have developed a small Flex library for opening and closing popups. Instead of using the PopUpManager directly and writing script-block logic to manage their creation and removal, a pair of simple MXML tags are available for declaring within view components. Here's the "Hello World" of declarative popups:

<popup:PopUpWrapper open="{model.openPopUp}">
   <mx:Label text="Hello World"/>
</popup:PopUpWrapper>

The PopUpWrapper tag is a non-visual component that manages opening and closing the popup. When its open property is set to true, a popup is opened containing the component wrapped by the tag; in this case a Label. When the open property is set back to false, the popup closes again. Alternatively, the component may dispatch an Event.CLOSE event, which will be handled by the PopUpWrapper itself.

This approach helps to keep MXML views components clean and free from ActionScript logic, whilst removing duplicated code wherever the PopUpManager is needed. The opening and closure can be controlled conveniently through bindings, as above, which plays nicely with presentation models. There are also simple ways to control the life-cycle of the popup and to apply special behaviors, such as effects that play while it opens and closes.

The remainder of this post covers the two components available -- PopUpWrapper and PopUpFactory -- explaining the differences between them. The library, source code, unit tests and a sample application are available for download here:

Using Declarative Popups

There are two components with slightly different capabilities:

  • PopUpWrapper - this is the simplest tag. It defers the creation of the popup view until the first time the popup is opened, then reuses the same view every subsequent time. There is no mechanism for releasing that view to be garbage collected.
  • PopUpFactory - this is the more flexible but less simple tag. It also defers creation of the popup view, but gives control over whether or not the view should be reused when the popup is next opened. If reuse is disabled, the view is made eligible for garbage collection upon closure.

Some examples of each are now provided.

The PopUpWrapper Component

Here's a more detailed version of the "Hello World", this time showing all of the properties and events available:

<popup:PopUpWrapper
    open="{model.openPopUp}"
    center="true"
    modal="false"
    childList="{PopUpManagerChildList.POPUP}"
    opening="openingHandler()"
    opened="openedHandler()"
    closing="closingHandler()"
    closed="closedHandler()">
    <mx:Label text="Hello World!"/>
<popup:PopUpWrapper>

The properties provide the same control over the popup as the PopUpManager. The events are dispatched at various points during the life-cycle of a popup:

  • PopUpEvent.OPENING is dispatched after the popup view has been created but just before it has actually been added to the display list.
  • PopUpEvent.OPENED is dispatched just after the popup view has been added to the display list.
  • PopUpEvent.CLOSING is dispatched just before the popup view is removed from the display list.
  • PopUpEvent.CLOSED is dispatched just after the popup view has been removed from the display list.

Here's another example where the popup is opened or closed using buttons instead of binding. The view used for the actual popup is a custom component.

<popup:PopUpWrapper id="popup">
    <mypopup:MyPopupView/>
</popup:PopUpWrapper>

<mx:Button label="Open" click="popup.open = true"/>
<mx:Button label="Close" click="popup.open = false"/>

The popup view itself can also instruct the closure of the popup by dispatching an Event.CLOSE event. For example:

<mypopup:VBox ... >
    <mx:Label text="My Popup!"/>
    <Button 
        label="Close" 
        click="dispatchEvent(new Event(Event.CLOSE))"/>
</mypopup:VBox>    

The PopUpFactory Component

The PopUpFactory tag uses a different mechanism for specifying the popup view. It is in fact the same approach that list-based controls use for their item renderers. The factory property can either be set to the class name for the popup view, or else an in-line component can be declared.

Here is a simples use case, specifying the popup view class name:

<popup:PopUpFactory 
    open="{model.openPopUp}"
    factory="my.package.MyPopupView"
    reuse="false"/>

Notice that the PopUpFactory includes a reuse property. This gives control over whether or not the popup view is reused, in contrast to the PopUpWrapper tag which always reuses the view and never makes it eligible for garbage collection.

When working with the PopUpManager directly it is common to make popups eligible for garbage collection once they have been removed. However there are good use cases for both approaches. If an application shows a large popup at start-up and then never again, it is wasteful of resources to keep it in memory. Alternatively, if a popup is repeatedly shown and hidden, such as a context menu, it is more efficient to reuses a single instance. As with all Flex development, the Flex Profiler should be used to ensure popup views are successfully garbage collected, since they can be a source of memory leaks.

Here is another example where the popup view is declared as an in-line component, in a similar manner to an item renderer on a list-based control.

<popup:PopUpFactory 
    open="{model.openPopUp}"
    reuse="false">
    <mx:Component>
        <mypopup:MyPopupView
            title="{outerDocument.model.popUpTitle}"/>
    </mx:Component>
</popup:PopUpFactory>

The PopUpFactory tag also provides the same properties as the PopUpWrapper tag, since they both inherit from a common base class.

Popup Behaviors

When we first introduced a declarative approach for popups to our projects, we noticed developers customizing the classes with more and more specific behavior. In one situation, a popup needed to play a transition while opening; in another the popup had to remain centered whenever its contents resized. Soon the popup tags became bloated with code for handling all of these special requirements, each applicable only to one or two popups.

The IPopUpBehavior interface was introduced to extract these specialities into their own classes. In this way, a set of behaviors can be created, independent of one another, and the appropriate behaviors can be applied for a given situation. This design creates a nice extensibility point, allowing developers to write their own behaviors without needing to customize the declarative tags at all.

Here is an example of a popup with a number of behaviors applied.

<popup:PopUpFactory
    open="{model.showPopUp}"
    factory="my.package.MyPopUpView">
    <popup:behaviors>
        <mx:Array>
            <popup:ZoomAndFadePopUpBehavior/>
            <popup:KeepCenteredPopUpBehavior/>
        </mx:Array>
    <popup:behaviors>
</popup:PopUpFactory>

In this case, two custom popup behaviors are being applied. The first plays a zoom and fade effect upon opening and closure, while the second ensures that the popup remains centered whenever its contents is resized. The declarative approach makes it simple to configure these behaviors.

To write your own popup behavior, just implement the IPopUpBehavior interface:

public interface IPopUpBehavior
{
    function apply(opener:PopUpBase):void;
}

A reference to the popup component is passed through the apply() method to each behavior. The behavior can then attach event handlers in order to control the behavior of the actual popup while it is opened or closed. Some examples are provided in the sample project (download above). A behavior can also suspend and resume closure by calling the suspendClosure() and resumeClosure() methods of the PopUpEvent class. This allows something asynchronous, such as the playing of an effect, to take place during closure, before the popup is actually removed from the display list.

How Does It Work?

The design for the popup tags is relatively simple. There's a common base class -- PopUpBase -- that is extended by the two tags: PopUpWrapper and PopUpFactory. The base class contains the common properties -- open, center, modal -- and also holds an array of behaviors, applying them during opening and closure. The two concrete tags implement different creation and destruction policies using the standard mechanisms for deferred instantiation and object factories: IDeferredInstance and IFactory. The Flex compiler takes care of the rest, converting the declared popup view into an appropriately configured DeferredInstanceFromFunction/Class or ClassFactory object. See the Flex Language Reference for more details.

Conclusion

We've been using versions of these tags on our projects for several years now. They help us to separate concerns, keeping our views as simple declarations of components, free from script block logic that would make them harder to understand and more difficult to unit test.

It's worth noting that Flex 4 includes its own declarative tag -- PopUpAnchor -- which also takes advantage of MXML and deferred creation, but it is designed for drop-down menu scenarios. There are some similarities, but the PopUpWrapper and PopUpFactory components are designed for more general popup handling. Perhaps a future version of the SDK will include declarative tags that make it simple to manage the life-cycle of popups and apply different behaviors, but in the meantime we hope you enjoy using these components.

Posted by tsugden at 10:07 PM | Comments (5)