15 Road Map for Versions 2.2 and 2.3

This chapter gives a quick overview over all the planned changes for the upcoming releases. It is also meant as a basis for discussion. You'll find links to the individual Jira tickets in most sections. If you have a comment specific for a particular feature it is preferred that you comment directly in Jira. If you have some general comments or want to propose a feature not listed here you can also use this thread in the forum that was created for feedback concerning the road map.

15.1 Version 2.2

After the 2.1 consolidation release it is time to add some new features. This release is fully backwards-compatible for regular application code and configuration, so you can move to the new features gradually. Only custom extensions may require some adjustments but not to the same extend as when going from 2.0 to 2.1.

These features are all implemented. There is a release candidate available, so you can try them out. You can download this version here.

This chapter only lists the major feature additions. There were also multiple smaller enhancements which are all listed in the Jira road map.

15.1.1 MXML Tags for Context Creation

Jira Ticket for Comments

The new version will allow to create Context instances with MXML tags:

<parsley:ContextBuilder config="{MyConfig}"/>

This is for the simple (but common) case where you only specify a single configuration class. For merging multiple classes or different configuration mechanisms, you can use child tags:

<parsley:ContextBuilder>
    <parsley:FlexConfig type="{ServiceConfig}"/>
    <parsley:FlexConfig type="{ControllerConfig}"/>
    <parsley:XmlConfig file="logging.xml"/>
</parsley:ContextBuilder>

There is also a new configuration mechanism that allows to specify instances that should be part of the container at runtime:

<parsley:ContextBuilder>
    <parsley:FlexConfig type="{ServiceConfig}"/>
    <parsley:FlexConfig type="{ControllerConfig}"/>
    <parsley:XmlConfig file="logging.xml"/>
    <parsley:RuntimeConfig instances="{[instance1, instance2]}"/>
</parsley:ContextBuilder>

If you need to specify id's then you could alternatively use nested child tags:

<parsley:ContextBuilder>
    <parsley:FlexConfig type="{ServiceConfig}"/>
    <parsley:FlexConfig type="{ControllerConfig}"/>
    <parsley:XmlConfig file="logging.xml"/>
    <parsley:RuntimeConfig>
        <parsley:Instance id="obj1" instance="{instance1}"/>    
        <parsley:Instance id="obj2" instance="{instance2}"/>    
        <parsley:Instance id="obj3" instance="{instance3}"/>    
    </parsley:RuntimeConfig>
</parsley:ContextBuilder>

The difference between objects added with the RuntimeConfig tag and DynamicObjects added at a later time is that the former are actually root object definitions which are injectable into other objects since they are specified at Context construction time. You can even use the regular <Object> tags inline now:

<parsley:ContextBuilder>
    <parsley:FlexConfig type="{ServiceConfig}"/>
    <parsley:FlexConfig type="{ControllerConfig}"/>
    <parsley:XmlConfig file="logging.xml"/>
    <parsley:RuntimeConfig>
        <parsley:Instance id="obj1" instance="{instance1}"/>    
        <parsley:Instance id="obj2" instance="{instance2}"/>    
        <parsley:Object id="obj3" type="{LoginInterceptor}"/> 
            <parsley:MessageInterceptor method="intercept" type="{LoginEvent}"/>
        </parsley:Object>    
    </parsley:RuntimeConfig>
</parsley:ContextBuilder>

Then you can also specify a custom scope to add to a Context with a simple child tag:

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

And finally all extensions that need to be initialized explicitly since they are optional, can be added as a child tag too:

<parsley:ContextBuilder>
    <parsley:FlexConfig type="{ServiceConfig}"/>
    <parsley:FlexConfig type="{ControllerConfig}"/>
    <parsley:XmlConfig file="logging.xml"/>
    <parsley:LoggingXmlSupport/>
</parsley:ContextBuilder>

It's recommended that authors of extensions also offer such a simple initialization tag. The class to be used as such a child tag only has to implement a very simple interface, ContextBuilderProcessor, with a single method:

function processBuilder (builder:CompositeContextBuilder) : void;

In the implementation you can then either add configuration processors or custom scopes to the specified builder or alternatively ignore it and install custom extensions with the GlobalFactoryRegistry instead or simply register custom metadata tags. But even if you do not need access to the builder it is beneficial to offer the extension initialization with such a tag, as it allows to clearly group all Parsley related initialization logic.

The tag also comes with a few optional parameters which you'll rarely use. They'll be shown here for the sake of completeness:

<parsley:ContextBuilder
    config="{MyConfig}"
    parent="{parentContext}"
    domain="{myDomain}"
    viewRoot="{someOtherComponent}"
    complete="init();"
    error="handleError(event);"
/>

The parent Context and ApplicationDomain can usually be determined automatically. If the Context hierarchy is attached to a view hierarchy, Parsley is able to magically connect these behind the scenes. The viewRoot usually is the document object the tag is placed in, but can alternatively be specified explicitly. And finally inline event handlers can be specified.

All in all these ContextBuilder tags will not introduce any new functionality. They are just syntactical sugar for all of you who prefer this declaration style over the old static entry point methods. Those will remain unchanged, they will be available as an alternative for Flex applications and are still needed for Flash applications.

15.1.2 View Configuration with MXML or XML

Jira Ticket for Comments

This new feature is inspired by the configuration mechanism used in Flicc. It allows to configure view components in MXML or XML and let the container apply the configuration to the view when it is wired to the Context. This is an optional feature. You can still configure view classes with Metadata exclusively like with previous versions. But the new MXML and XML support allows to define view configuration alongside your regular object configurations. It has the side effect that you can then define different configurations for individual instances of the same component. The configuration to apply to a wired component will be matched either by id or by type:

<View id="myPanel" type="{MyPanel}">
    <Property name="url" value="http://www.somewhere.com/"/>
    <MessageHandler method="execute"/>
</View>

As you see it is basically the same syntax like for regular object configurations. But the use of the View tag instructs Parsley not to attempt to create instances of that class but instead wait until any matching component gets dynamically wired to the Context and then applies the configuration.

Parsley will first attempt to match by id. This is either the name of the Flex component or alternatively an id specified explicitly in the Configure tag:

<parsley:Configure configId="someId"/>

If no match for the id is available then the framework will attempt to match by type, throwing an Error if it runs into ambiguous definitions. If no matching type is found Parsley will fall back to the old mechanism to only process metadata for that instance.

15.1.3 Support for Asynchronous Commands

There is now a new bunch of tags for messaging which 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 does not have a void return type like a regular message handler. Instead it has to specify a type that the framework can use to manage the 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. 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.

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

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

Or it can be the ResultEvent instead of the actual result value, 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 {

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. You can have a look at the AsyncTokenCommandFactory for example to see how easy it is to create new command types. The interface is quite simple:

public interface CommandFactory {

	function createCommand (returnValue:Object, 
			message:Object, selector:* = undefined) : Command;
	
}

The method takes the value returned by the method that executed the command (e.g. AsyncToken) and the message and selector that triggered the command. It has to return an instance of Command, in most cases creating a subclass of AbstractCommand to avoid any boilerplate code. The factory implementation finally has to be registered:

GlobalFactoryRegistry.instance.messageRouter
        .addCommandFactory(MyReturnType, new MyCommandFactory());

The signature for the addCommandFactory method is as follows:

function addCommandFactory (type:Class, factory:CommandFactory) : void;

It has to specify the return type of the command methods it wants to manage and then the factory instance itself.

Differences to Cairngorm 3 Commands

The command feature as it is now implemented has kind of a longer history. First it was originally added to the road map shortly after the 2.0 release. But in the meantime Adobe created the Cairngorm 3 Integration Library which picked up the concept and added a lot of great ideas not in the original road map, like letting the framework manage the outcome of an asynchronous operation with the [CommandResult] and [CommandError] tags. So many thanks for that great work! The major reason that it is now natively integrated into the Parsley framework is that first I think this is a rather core feature and not just an addition and second this turned out to be a feature which is somewhat cumbersome to implement just as an extension on top of the Parsley core. Adding some native hooks to the Parsley IOC Kernel to support this feature made the implementation cleaner and also allowed for some performance optimizations.

Now for everyone who already used this extension, here is the list of differences allowing for easy migration:

15.1.4 Short-lived Command Objects

Jira Ticket for Comments

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.

Differences to Cairngorm 3 Command Objects

Finally, like with the normal command methods, these command objects come with a few differences to their Cairngorm 3 counterpart:

15.1.5 Observing the Lifecycle of other Objects

Jira Ticket for Comments

This is a powerful new feature that opens some new doors for convenient ways to observe or modify other objects without the need to add something to their configuration. This might be particularly useful for short-lived objects like views or commands which might enter and leave the Context at any point in time and thus are not valid sources for regular injection points. By observing these instances you can still get hold of a reference to such a short-lived object.

[Observe]
public function enhance (panel:ShoppingCartPanel) : void;

Now this method will be invoked whenever a new instance of ShoppingCartPanel (or any subclass) has been fully configured.

The default without attributes like shown above is equivalent to:

[Observe(phase="postInit", scope="global")]

So you could also use a different phase of the lifecycle (like preDestroy to get notified when an object leaves the Context) and can also control the scope and listen only for matching types in the same Context with scope="local" for example. Scoping works the same way like scopes for messaging as explained in the messaging chapter.

With this mechanism you simply plug in some new class that contains such a tag and suddenly existing classes can be enhanced without the need to touch their configuration. This is somewhat analogous to the existing [Factory] tag which can be used to customize the object instantiation. With this tag you can customize the object configuration after it has been instantiated. In both cases you do not even depend on the Parsley API in any way.

15.2 Version 2.3

This version is not fully specified yet. Until work on this version starts the road map might still change due to user feedback. But these two features are the initial plan.

15.2.1 XML-Object-Mapper Configuration with Metadata

Jira Ticket for Comments

This enhancement will make the definition of custom configuration tags easier. With MXML or Metadata it was already quite easy, since at most a single line of code was needed to register such a tag. With XML tags the use of the XML-Object Mapper from Spicelib is required and might involve the need to programmatically specify subtle details of how the new XML tag maps to the configuration class.

The feature set as such will remain the same. The new mechanism will still be based on the existing PropertyMapperBuilder class, but the need to programmatically create such a builder will go away. First there will be sensible defaults. A property without metadata will automatically map to a corresponding XML attribute. You can then override the default for individual properties:

public class Person {

    [ChildTextNode]
    public var name:String;
    
    [ChildElement]
    public var address:Address;
    
}

There will be a corresponding metadata tag for all the features currently available in the PropertyMapperBuilder.

15.2.2 New Tag for Flex Bindings

Jira Ticket for Comments

This tag will allow to specify Flex Bindings with Metadata. This might be convenient for regular classes managed by the container, where you cannot create bindings with MXML. You can then create unidirectional or bidirectional bindings between properties of two managed objects:

[PropertyBinding(objectId="environment",property="defaultTimeout")]
public var defaultTimeout:uint;

The name PropertyBinding was chosen to distinguish this tag from the existing MessageBinding and ResourceBinding tags.