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.
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.
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.
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.
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:
AsyncToken return values
for the methods that execute the command. So it could only be used for managing RPC calls. In Parsley this now
has been generified allowing for any kind of return value as long as there is a CommandFactory that
knows how to handle it. Builtin support is available for AsyncToken, Cinnamon Remoting and the Spicelib
Task Framework, but custom support can be easily added like shown in the preceding section. selector attribute as the primary means of matching the target
method. This was inconsistent to the way how the Parsley Messaging Framework normally operates, so I aligned it.
It now uses the type (class) of the message as the primary selection criterion in a polymorphic way like all
existing types of messages receivers. It only uses the selector attribute as an optional, secondary
criterion. [CommandFault] tag to [CommandError] to align it with the existing
[MessageError] tag. The term fault refers to the RPC FaultEvent, but since the feature
is now generified anyway, this semantic coupling was no longer necessary. [CommandInProgress] tag to [CommandStatus]. Since the command feature
is now generified, I wanted to reserve the tag name [CommandProgress] for future use as it would
now also be possible to create command types which allow objects to monitor the progress of executing commands,
for example for showing a progress bar. Since the existing tag is only a boolean flag, the new name is a good fit. CommandStatus tag did not take into account that there might be
multiple matching commands executing in parallel. Although this might be a rather rare scenario, it makes the
feature more robust to consider this use case. Now the flag is true for one or more matching active commands,
and only false if there is no active command. CommandStatus tag only did its work in response to status changes of active
commands. It did nothing at the time the object containing that tag is instantiated. The Parsley version now
also injects the correct value at object creation time. Also covering rather rare use cases, but still making
the feature more robust. When the object is created while there are already active commands that match the
selection of the tag, it will immediately set to true. [CommandResult] or a [CommandFault] method. The Parsley version also allows for
0, 1 or 2 parameters, but the order is now fixed. The result must always be the first parameter and the
original message has to be the second. Only downside is that you now have to specify the result parameter
even if you are only interested in the original message. But the flexibility was difficult to implement natively
because the Cairngorm extension relied on a few assumptions which are actually not true for Parsley, like the
message always being of type flash.events.Event. 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:
[Command], [CommandResult]
or [CommandError] tags. First they are not necessary, since we can use "configuration by convention"
here and just look for a predefined set of method names, which can be overwritten if needed. Second the
semantics of the tags do not fully apply here anyway. For example the Cairngorm library used the [CommandResult]
tag for the internal result handler of the command object, but actually none of the multiple attributes of that tag
would make sense in that scenario. There is no need to match the method to a particular result, as the logic is simply:
invoke me as soon as the result of the command executed by this instance is available. And I think it could be potentially
confusing to tell people: hey, you can reuse these tags, but don't use any of their attributes in this scenario,
as they will be ignored anyway. DynamicCommand tag. 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.
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.
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.
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.