6 Messaging

Parsley 2 introduces a new generic Messaging Framework that allows you to exchange messages between objects in a fully decoupled manner. Decoupled not only means that the sender and the receiver do not have to know each other. It is equally important that the sending and receiving objects are also fully decoupled from the framework itself. This is an advantage ignored by most other Flex Frameworks (including version 1 of Parsley) where you have to use objects or static methods from the Framework API for dispatching application events or messages. Why is that important? If your objects are decoupled from the framework you can reuse them in a different context where you might want to use a different framework or no framework at all. For example you might want to wire dispatching and receiving instances programmatically for Unit Tests without the extra burden to initialize an application framework.

The Parsley Messaging Framework is generic in a sense that it does not impose a particular usage style. This is also a difference to some of the existing Flex MVC Frameworks which often advocate a certain structure and usage pattern or even offer concrete base classes for the controller, model and view parts. With Parsley 2 you are completely free to design your application architecture. In case you do use the Messaging Framework to build a classic MVC architecture you may want to read 10 Building MVC Architectures in addition to this chapter.

This chapter describes how you can configure objects for sending and receiving messages. For every configuration option examples for AS3 Metadata, MXML and XML configuration are included.

6.1 Dispatching Messages

For an object that wants to dispatch messages managed by Parsley you have the following setup options:

6.2 Receiving Messages

For an object that wants to receive and process messages managed by Parsley you have the following setup options:

6.3 Managed Events

If the class that you want to dispatch messages managed by Parsley is a regular EventDispatcher this is the most convenient option. It would work for any existing EventDispatcher implementation even if it wasn't designed with Parsley in mind. It requires two steps:

Metadata Example

[Event(name="loginSuccess",type="com.bookstore.events.LoginEvent")]
[Event(name="loginFailed",type="com.bookstore.events.LoginEvent")]
[Event(name="stateChange",type="flash.events.Event")]
[ManagedEvents("loginSuccess,loginFailure")]
public class LoginServiceImpl extends EventDispatcher implements LoginService {

    [...]
    
    private function handleLoginResult (user:User) : void {
        dispatchEvent(new LoginEvent("loginSuccess", user));
    }
    
}

In the example above the service declares three events. Two of them (loginSuccess and loginFailure) are application events and should be managed by Parsley and dispatched to all objects in the Context interested in that event. The third one is a low-level event only of interest for objects interacting directly with that service. Those objects may still register a regular event listener for that event.

The example method above shows how a result handler (that probably was registered as a handler for a remote service invocation) translates the result into an event and simply dispatches it. No FrontController.getInstance().dispatch... or anything like that. Since loginSuccess was declared as a managed event it will be passed to all MessageHandlers configured in Parlsey.

MXML Example

<Object type="{LoginServiceImpl}">
    <ManagedEvents names="['loginSuccess','loginFailure']"/>
</Object>

If you declare the managed events in MXML you can omit the [ManagedEvents] metadata tag from the previous example. Note that you still have to include the [Event] metadata tags, since those are not a configuration artifact of Parsley but a regular Flash API feature.

XML Example

<object type="com.bookstore.services.LoginServiceImpl">
    <managed-events names="loginSuccess,loginFailure"/>
</object>

As always very simliar to MXML configuration apart from several notation differences.

6.4 Injected MessageDispatchers

Sometimes you don't want to work with events for your application messages. Somehow several event semantics may not make much sense in a particular scenario. Application events managed by Parsley cannot "bubble", stopPropagation would not have any effect in the Parsley message processing sequence and for fully decoupled messaging you may even want to avoid that the message receiver can get hold of the message dispatcher through event.target.

In those cases Parsley offers the option to use any class as an application message, whether it extends flash.events.Event or not. You can then request the framework to inject a message dispatcher function that you can use for your custom application messages. Assuming you created the following simple message class:

class LoginMessage {

    public var user:User;
    
    public var role:String;
    
    function LoginMessage (user:User, role:String) {
        this.user = user;
        this.role = role;
    }
    
}

You can then use it in a service like this:

public class LoginServiceImpl implements LoginService {

    [MessageDispatcher]
    public var dispatcher:Function;

    [...]
    
    private function handleLoginResult (user:User) : void {
        dispatcher(new LoginMessage(user));
    }
    
}

Now your service does not extend EventDispatcher. Instead it declares a variable of type Function annotated with the [MessageDispatcher] tag which instructs Parsley to inject a message dispatcher function on object creation. You can then simply pass any kind of object to this dispatcher function.

MXML Example

<Object type="{LoginServiceImpl}">
    <MessageDispatcher property="dispatcher"/>
</Object>

XML Example

<object type="com.bookstore.services.LoginServiceImpl">
    <message-dispatcher property="dispatcher"/>
</object>

If you don't want to use Metadata tags you can also request the dispatcher injection with MXML or XML configuration.

6.5 MessageHandlers

Message Handlers are the most common approach for the receiving side. You can declare methods to be invoked when a particular application message gets dispatched. In the most simple case the method will simply be selected by parameter type:

Metadata Example

[MessageHandler]
public function handleLogin (message:LoginMessage) : void {

In this case the method will be invoked whenever a message of a matching type (or subtype) gets dispatched.

MXML Example

<Object type="{LoginAction}">
    <MessageHandler method="handleLogin"/> 
</Object>

XML Example

<object type="com.bookstore.actions.LoginAction">
    <message-handler method="handleLogin"/> 
</object>

There is also a variant where you split properties of the message class to arguments of the message handler method:

[MessageHandler(type="com.bookstore.events.LoginMessage",messageProperties="user,role"]
public function handleLogin (user:User, role:String) : void {

Note that in this case you also have to declare the message type since it cannot be detected from the parameter type.

Finally you may encounter a situation where selection by message type is not sufficient. If you dispatch the same message type in different scenarios and application states you may want to further refine the message selection process. See 6.11 Using Selectors for details.

6.6 MessageBindings

Message Bindings are simply a shortcut, where you want to bind a property of a class to a property of a message, that should be updated whenever a message of a matching type is dispatched. In the following example the user property of the example will be set to the value of the user property of the LoginMessage instance whenever such a message is dispatched.

Metadata Example

[MessageBinding(messageProperty="user",type="com.bookstore.events.LoginMessage")]
public var user:User;

MXML Example

<Object type="{LoginServiceImpl}">
    <MessageBinding 
        targetProperty="user" 
        messageProperty="user"
        type="{LoginMessage}"
    />
</Object>

XML Example

<object type="com.bookstore.services.LoginServiceImpl">
    <message-binding 
        target-property="user" 
        message-property="user"
        type="com.bookstore.events.LoginMessage"
    />
</object>

As with MessageHandlers you may want to use Selectors with MessageBindings. See 6.11 Using Selectors for details.

6.7 MessageInterceptors

This is the third option for the receiving side. Interceptors may come in handy when you want to decide whether or not the message should be passed to handlers and bindings based on application state or user decisions. Interceptors have the following characteristics:

A simple example where you might want to use such an interceptor, is when you have an application that can be used without being logged in, but does include some actions which will not be accessible without login. In that case an interceptor could suspend the message processing, present a login dialog and resume the message processing after successful login.

Another example would be to show a simple warning before any delete operation is actually performed like in the following example:

public class DeleteItemInterceptor {
 
    [MessageInterceptor(type="com.bookstore.events.ShoppingCartDeleteEvent")]
    public function interceptDeleteEvent (processor:MessageProcessor) : void {
        var listener:Function = function (event:CloseEvent) : void {
            if (event.detail == Alert.OK) {
                processor.proceed();
            }  
        };      
        Alert.show("Do you really want to delete this item?", "Warning", 
            Alert.OK | Alert.CANCEL, null, listener);
    }
 
}

When the user hits cancel, the MessageProcessor never resumes and no subsequent handler or bindings will be executed.

Like with MessageBindings you have to declare the message type since it cannot be detected from the method signature. Interceptor methods must always have a single parameter of type MessageProcessor. Again you can use MXML or XML instead of Metadata tags for declaring the interceptor methods:

MXML Example

<Object type="{LoginServiceImpl}">
    <MessageInterceptor 
        method="interceptDeleteEvent" 
        type="{ShoppingCartDeleteEvent}"
    />
</Object>

XML Example

<object type="com.bookstore.services.LoginServiceImpl">
    <message-interceptor
        method="interceptDeleteEvent"
        type="com.bookstore.events.ShoppingCartDeleteEvent"
    />
</object>

It is recommended to use interceptors sparingly as it is the only feature of the Messaging Framework that ties you to the Parsley API, as we have to pass a MessageProcessor instance to you so that you are able to rewind or proceed with message processing.

6.8 Error Handlers

Since version 2.1 you can also configure a method to be invoked whenever a handler or interceptor for a matching message threw an Error:

[MessageError(type="com.bookstore.LoginEvent")]
public function handleError (processor:MessageProcessor, error:IOError) : void;

In the example above the error handler would be invoked whenever any handler for a message of type LoginEvent throws an IOError. So you can chose the matching message type and the matching Error type and of course, like with all tags, specify an additional selector attribute.

But you can also create a global handler, for every type of error and any type of message:

[MessageError]
public function handleError (processor:MessageProcessor, error:Error) : void; 

Finally, since an error handler configured with the tag shown above always listens to a single scope, you may want to add an error handler that will be automatically attached to every scope created for an application. You can do that programmatically through the GlobalFactoryRegistry:

var provider:ObjectProvider = Provider.forInstance(this);
var handler:MessageErrorHandler 
                  = new DefaultMessageErrorHandler(provider, "handleError", Object);
GlobalFactoryRegistry.instance.messageRouter.addErrorHandler(handler);

6.9 Asynchronous Command Methods

Introduced with version 2.2 the Parsley framework now incorporates several ideas which originated in the Cairngorm 3 Library project, in particular the Integration Library.

This collection of tags allow for a different type of message handling than the existing [MessageHandler] tag. With the new [Command] tag you can instruct the framework to manage the execution of an asynchronous command for you:

[Inject]
public var service:RemoteObject;

[Command(selector="save")]
public function saveUser (event:UserEvent) : AsyncToken {
    return service.saveUser(event.user);
}

The [Command] tag supports the same set of attributes like the [MessageHandler] tag. The logic for matching messages to commands is also the same. In the example above the command will be executed for all messages of type UserEvent (including subtypes) with the selector save which usually refers to the event type.

The difference is that a command is not required to have a void return type like a regular message handler. Instead it can specify a type that the framework can use to manage the asynchronous command execution for you. In this case it's the AsyncToken that was returned by the remote call. Now the framework will also manage the result or eventual fault for you.

Handling the result

Any object can now contain a method that wishes to handle the result of such a call:

[CommandResult(selector="save")]
public function handleResult (user:User, event:UserEvent) : void {

In this case the User instance returned by the remote call will be passed to the result handler alongside the original message that triggered the action. The second parameter is optional, but if you omit it you have to specifiy the message type in the tag to allow for exact target matching:

[CommandResult(type="com.foo.events.UserEvent" selector="save")]
public function handleResult (user:User) : void {

This is necessary because like with regular message handling Parsley does not solely rely on string identifiers (event types) for target selection to allow for more type-safety. It is always a combination of message type (polymorphically) and an optional selector value which serves as a secondary selection key.

Alternatively you can also specify the ResultEvent instead of the actual result value as the parameter type, in cases where you need access to headers returned from the server for example:

[CommandResult(selector="save")]
public function handleResult (result:ResultEvent, trigger:UserEvent) : void {

Handling command completion, but ignoring the result

If you are not interested in the actual result, but instead only want to execute some logic triggered by the completion of the command, you can use another tag:

[CommandComplete]
public function handleResult (event:UserEvent) : void {

In this case we omitted the selector attribute. This would mean that this method would get invoked whenever any command triggered by an UserEvent has completed.

The message parameter is also optional and can simply be omitted:

[CommandComplete(type="com.foo.events.UserEvent", selector="save")]
public function afterUserSaved () : void {

Handling errors

For receiving the eventual fault events, another tag can be used:

[CommandError(selector="save")]
public function handleResult (fault:FaultEvent, trigger:UserEvent) : void {

The parameters are again both optional and the rules for matching are the same as for [CommandResult].

Finally you can also observe the status of executing commands:

[CommandStatus(type="com.foo.events.UserEvent", selector="save")]
public var isSaving:Boolean;

This boolean flag will always be true if one or more asynchronous commands matching the specified type and selector are currently executing. It will be false otherwise. This is very convenient for tasks like disabling buttons during command execution for example.

Supported Command Types

The preceding examples all showed commands that execute remote calls and return an AsyncToken. This is not the only option available though. The command feature can also be used with Cinnamon Remoting, which returns instances of ServiceRequest instead as it does not use Flex RemoteObjects. Finally the commands are integrated with the Spicelib Task Framework, so a command can also declare Task as the return type.

As you might expect these are only the three builtin options. You can extend the framework easily through creating your own CommandFactories. For details see 12.8 Custom Command Types.

6.10 Short-lived Command Objects

The Command feature as demonstrated in the previous section does not alter the nature of the objects executing the command in any way. The tags [Command], [CommandResult] and [CommandError] can be used on any method in any managed object. It can be a root singleton object or even a view (although this would be a bad idea in most cases in terms of architecture if the view executes a command). Each object can contain multiple command methods and also any number of result handlers for various types of commands. The result handler can be on the same object that executes the command or on any other object. In short: These tags only control the single method or property they are placed upon.

Alternatively the framework allows for a second way of defining a command. Instead of just applying tags to individual methods you configure an entire command object. This object only has a single method that executes a command, and optionally a method to receive the result and one for an eventual error. The result handler would only be invoked for the command that the same instance executed. Such a command object might look like this:

public class SaveUserCommand {

    [Inject]
    public var service:RemoteObject;

    public function execute (event:UserEvent) : AsyncToken {
        return service.saveUser(event.user);
    }

    public function result (user:User) : void {
        // do something with the result
    }
    
    public function error (fault:Fault) : void {
        // do something with the result
    }
    
}

It can then be defined in a configuration class with the new DynamicCommand tag:

<DynamicCommand type="{SaveUserCommand}" selector="save"/>

As you see this is really a minimal configuration. We do specify the selector, but we can omit the message type as it will be deduced from the parameter type of the execute method. Furthermore we must not use the regular command metadata tags in the command object. The tags partly contain behaviour not applicable for this scenario and they are also not necessary. The default names for selecting the method that executes the command and the result and error handlers are like shown in the preceding example. You can overwrite these with the DynamicCommand tag, but this should only be necessary for adapting existing classes. In the following example we overwrite all three method names:

<DynamicCommand 
    type="{SaveUserCommand}" 
    selector="save"
    execute="saveUser"
    result="handleUser"
    error="handleFault"
/>

The tag also supports all of the attributes you know from the regular [MessageHandler] or [Command] tags:

<DynamicCommand 
    type="{SaveUserCommand}" 
    selector="save"
    scope="local"
    order="1"
/>

In the example above the command will only execute if the triggering message was dispatched by an object living in the same Context. The order attribute allows to specify the execution order in case multiple message handlers and commands are going to be invoked for a particular message. It does not specify the order for the result or error handler on the command object, these will always be invoked first, before any other result handlers declared on other objects.

Apart from the special semantics the DynamicCommand tag allows the same set of child tags like the regular Object tag:

<DynamicCommand type="{SaveUserCommand}" selector="save">
    <ConstructorArgs>
        <ObjectRef idRef="someDependency"/>
    </ConstructorArgs>
</DynamicCommand>

Of course the command object can be combined with separate result handler methods on other objects. If you are using the [CommandResult] tag for example it does not make any difference whether the command was executed by a single method annotated with the [Command] tag or whether it was executed by a command object declared with the <DynamicCommand> tag. So although such a command object has its own "private" result handlers, any other object in the system can still receive the result, too.

Command Object Lifecycle

Apart from grouping the executing method and the result handlers the DynamicCommand also introduces a special kind of lifecycle management for command objects. The creation of the object does not happen until a matching message is dispatched. It then becomes a container managed object just for the duration of the command execution. It will immediately be removed from the Context after the result or error handler have been invoked. But during its lifetime, it is a fully managed object, can have its dependencies injected, or even take part in messaging during the command execution. But that would be a rather rare case, most common usage scenario is probably the command object just receiving all the dependencies it needs to execute the command.

The default behaviour is that the container creates a new command instance for each matching message. So you can even have multiple commands executing in parallel without interfering with each other. This behaviour can be changed with the stateful attribute:

<DynamicCommand type="{SaveUserCommand}" selector="save" stateful="true"/>

Now the instance will still be lazily instantiated when the first matching message is heard. But it will then be kept as a container managed object, reusing the same instance for subsequent invocations. You may switch to this behaviour in case object creation and initialization is too expensive to be repeated all the time. For a stateful command you just have to make sure that you do not keep information for a single command execution in a private property, as multiple commands would then interfere with each other and overwrite that value.

6.11 Using Selectors

In the examples for the sections about MessageHandlers, MessageBindings and MessageInterceptors the matching methods or properties were always determined solely by the type (class) of the message. Sometimes that may not be sufficient if you dispatch the same message type in different scenarios or application states. In such a case you can refine the selection process with custom selectors.

If you are using events the type property of the Event class can serve as a selector:

[MessageHandler(selector="loginSuccess")]
public function handleLogin (message:LoginEvent) : void {
    [...]
}

[MessageHandler(selector="loginFailure")]
public function handleError (message:LoginEvent) : void {
    [...]
}

In the example above the handleLogin method will only be invoked when the type property of the LoginEvent instance has the value loginSuccess.

For custom message types that do not extend flash.events.Event there is no default selector property, but it can be easily declared with the [Selector] metadata tag on a property of the message class:

class LoginMessage {

    public var user:User;
    
    [Selector]
    public var role:String;
    
    [...]
}

Now you can select message handlers based on the role of the user that logged in:

Metadata Example

[MessageHandler(selector="admin")]
public function handleAdminLogin (message:LoginMessage) : void {

MXML Example

<Object type="{AdminLoginAction}">
    <MessageHandler method="handleAdminLogin" selector="admin"/> 
</Object>

XML Example

<object type="com.bookstore.actions.AdminLoginAction">
    <message-handler method="handleAdminLogin" selector="admin"/> 
</object>

6.12 Using Scopes

In Parsley 2.0 every child Context simply shared the message router with its parent Context. This way messages were always dispatched globally. An approach that may turn out to be too simplistic for large and modular applications where a loaded module, a window or a popup may wish to create its own local messaging context while still being able to inject objects from the root Context or dispatching selected messages globally. The scope feature introduced with version 2.1 offers that flexibility, allowing to dispatch into a subset of Contexts only.

Global and Local Scopes

In the new default scope setup for Parsley 2.1 there is a global scope that is created for each Context that does not have a parent (usually only the one root Context created at application startup) and then shared will all children of that Context (including grandchildren of course). Additionally each Context will create its own local scope which will not be shared with its children. The following diagram shows this default setup:

Since the global scope represents exactly the only option that was available in Parsley 2.0 it will always be the default scope for all configuration tags where no scope is explicitly specified. This way existing configuration files and metadata tags on classes that were used with version 2.0 will still work the same way.

[MessageHandler(selector="save")]
public function save (event:ProductEvent) : void {

The handler above listens to ProductEvents dispatched from any Context in the hierarchy. It listens to the global scope.

[MessageHandler(selector="save", scope="local")]
public function save (event:ProductEvent) : void {

Now the handler only listens to events dispatched from the same Context. Of course all tags for the various message receiver types accept a scope attribute, including MessageInterceptor and MessageErrorHandler.

For the sending side the default behaviour is different. For any [ManagedEvents] tag where no scope is explicitly specified the message will be dispatched through all scopes available for that particular Context. This way the receiving side can decide which scope it wants to listen to, allowing global and local receivers for the same message instance. For cases where you even want to restrict the sending side to a single scope there will also be a new scope attribute for the [ManagedEvents] tag:

[ManagedEvents("save,delete", scope="local")]

Custom Scopes

Finally you can also create your own custom scope when neither global nor local messaging is the right choice. This may be the case when you create a large AIR window for example. The root window component may create a Context with the root application Context as a parent but then also a few child Contexts for parts within that window. If you then want to setup a messaging scope for that window only you need a scope that spans multiple Contexts but still is not global. Since version 2.1 you can create a scope setup like in the following example diagram:

The window scope is a custom scope that exists side-by-side with the two default scopes. Now how do you instruct the framework to create that scope? This has to be done for the root Context of the scope, in this case the two root Contexts for the two window scopes. In MXML you can specify the scope like this:

<parsley:ContextBuilder>
    <parsley:FlexConfig type="{ServiceConfig}"/>
    <parsley:FlexConfig type="{ControllerConfig}"/>
    <parsley:XmlConfig file="logging.xml"/>
    <parsley:Scope name="window" inherited="true"/>
</parsley:ContextBuilder>

Or programmatically add the scope with the ContextBuilder DSL:

var viewRoot:DisplayObject = ...;

ContextBuilder.newSetup()
    .viewRoot(viewRoot)
    .scope("window")
    .newBuilder()
        .config(FlexConfig.forClass(ServiceConfig))
        .config(FlexConfig.forClass(ControllerConfig))
        .config(XmlConfig.forFile("logging.xml"))
        .build();

The name of the scope does not have to be unique as long as you make sure that two scopes with the same name do never overlap. This is convenient as it allows to define a message handler for the window scope without having to think about which window instance it belongs to:

[MessageHandler(selector="save", scope="window")]
public function save (event:ProductEvent) : void {

The second boolean parameter specifies whether the scope should be shared with child Contexts. So you can also create custom local scopes, although this is probably a rather unsual use case.

6.13 Using the Messaging API

In normal application code you should try to avoid to directly interact with the Parsley API to keep your classes decoupled from the framework. But in some edge cases or if you want to extend the framework or build another framework on top of it, you may want to register message handlers or bindings programmatically. The MessageReceiverRegistry interface contains the following methods for regristration:

function addTarget (target:MessageTarget) : void;

function addInterceptor (interceptor:MessageInterceptor) : void;

function addErrorHandler (handler:MessageErrorHandler) : void;

function addCommand (command:CommandTarget) : void;

function addCommandObserver (observer:CommandObserver) : void;

There are five categories of message receivers: a MessageTarget is a regular receiver, implementing classes include MessageHandler and MessageBinding. MessageInterceptor corresponds to the tag with the same name, MessageErrorHandler to the [MessageError] tag. A CommandTarget is a target that executes an asynchronous command and finally a CommandObserver listens to the result or error outcome of such a command. The interface contains five for methods for removing these five receiver types.

To get hold of a MessageReceiverRegistry instance you can inject a Context instance into your class. You then have to pick the registry for the scope you want your receivers to be applied to. In the following example we register a message handler for the global scope:

class SomeExoticClass {

    [Inject]
    public var context:Context;
    
    [Init]
    public function init () : void {
        var registry:MessageReceiverRegistry 
                = context.scopeManager.getScope(ScopeName.GLOBAL).messageReceivers;
        var target:MessageTarget 
                = new MessageHandler(Provider.forInstance(this), "onLogin");
        registry.addMessageTarget(target);    
    }
}

When you place an [Inject] metadata tag on a property of type Context Parsley will always inject the Context instance this class is managed in.

Finally you can also use the ScopeManager to dispatch messages:

context.scopeManager.dispatchMessage(new LoginMessage(user, "admin"));

When dispatching through the ScopeManager directly, the message will be dispatched through all scopes managed by this Context (by default only global and local scopes, but you can create your own). This way the receiving side can decide which scope to listen to. In rare cases you might want to limit the choice right on the sending side. In this case you have to dispatch through an individual scope:

var scope:Scope = context.scopeManager.getScope(ScopeName.LOCAL);
scope.dispatchMessage(new LoginMessage(user, "admin"));