One of the most prevalent actions in any Flash or Flex application is: Get stuff from the server back-end. Be it calling a remote object, loading a sub-application, module or resource file, all of these actions have one thing in common: They are asynchronous.
The Flex framework and Flash player seem to use different kinds of asynchronous patterns for different kinds of retrieval. There's the responder method, the event based method (all be it with an optional progress reporting mechanism), calls to a singleton that returns an IEventDispatcher instance or some other interface, etc.
The Spring Actionscript Operation API aims to facilitate a common wrapper for these different patterns in order to make using and combining them a little easier. The provided interfaces and base classes will give a developer the opportunity to easily write his own logic. In addition to this, Spring Actionscript already offers classes for the most common asynchronous tasks such as the loading of Flex modules, resource modules and remote object calls.
The Operation API is not an MVC or MVCS framework. The Spring Actionscript team believes that a framework approach is too rigid, instead it offers a set of low-level classes and interfaces that allows a developer to create a custom solution for each project/application he/she is working on. No application is the same, therefore Spring Actionscript chose to offer building blocks instead of a framework.
All the classes and interfaces described in this chapter can be found in the org.springextensions.actionscript.core package.
The Operation API, broadly speaking, has four concepts:
Operation: An asynchronous action.
Command: An action with deferred execution.
Service: A collection of related operations.
Task: A collection of commands that are executed using a control flow.
An operation represents any kind of asynchronous action and is described by the IOperation The IOperation interface describes an asynchronous operation. interface:
public interface IOperation extends IEventDispatcher {
function get result():*;
function get error():*;
function addCompleteListener(listener:Function, useCapture:Boolean = false, priority:int = 0, useWeakReference:Boolean = false):void;
function addErrorListener(listener:Function, useCapture:Boolean = false, priority:int = 0, useWeakReference:Boolean = false):void;
function removeCompleteListener(listener:Function, useCapture:Boolean = false):void;
function removeErrorListener(listener:Function, useCapture:Boolean = false):void;
}The most important properties of this interface are the result Invokes the <code>unregister()</code> method. and error properties, these will be assigned with the results of the asynchronous action. The names speak for themselves.
An IOperation The IOperation interface describes an asynchronous operation. implementation will always execute its action immediately after creation, all necessary parameters are therefore typically passed to the constructor of an implementation.
Operations that take long amounts of time to complete, such as downloading modules or other types of assets benefit from being able to notify the user of their progress. Typically a progress bar could be display this. For these types of operations there is the IProgressOperation Subinterface of <code>IOperation</code> that contains information about the progress of an operation. interface, which is a simple extension of IOperation The IOperation interface describes an asynchronous operation.:
public interface IProgressOperation extends IOperation {
function get progress():uint;
function get total():uint;
function addProgressListener(listener:Function, useCapture:Boolean = false, priority:int = 0, useWeakReference:Boolean = false):void;
function removeProgressListener(listener:Function, useCapture:Boolean = false):void;
}In some cases a collection of IOperations The IOperation interface describes an asynchronous operation. need to be run at once, yet each individual OperationEvent.COMPLETE event doesn't need to be listened for. All that is important is that all of the IOperations The IOperation interface describes an asynchronous operation. are completed successfully. For this situation the OperationQueue A queue of <code>IOperation</code> objects that dispatches an <code>OperationEvent.COMPLETE</code> event when all operations in the queue have completed (and dispatched a corresponding <code>OperationEvent.COMPLETE</code> event). class can help out. A simple usage of this class might be like this:
var queue:OperationQueue = new OperationQueue();
queue.addCompleteListener(handleQueueComplete);
queue.addOperation(new FirstOperation());
queue.addOperation(new SecondOperation());
queue.addOperation(new ThirdOperation());
queue.addOperation(new FourthOperation());And that's all, upon adding the operations the queue immediately starts running. So after all four operations have completed the specified handleQueueComplete method will be invoked and the application can go on about its business.
Here is a list of all the IOperation The IOperation interface describes an asynchronous operation. implementations for common usage scenarios that Spring Actionscript contains:
LoadPropertiesOperation Operation that loads a <code>Properties</code> object.
LoadPropertiesBatchOperation Operation that loads multiple external properties files in a batch.
LoadModuleOperation Asynchronous operation that loads a Flex module from a specified URL.
RemoteObjectOperation An <code>IOperation</code> that invokes a method on a remote object.
HTTPServiceOperation An <code>IOperation</code> that invokes a method on a HTTP webservice.
WebServiceOperation An <code>IOperation</code> that invokes a method on a SOAP based webservice.
A command is any kind of action whose execution is deferred. Only after invoking the command's execute() Executes the command. method will the action do its work. The basic ICommand Interface to be implemented by command classes. interface is very modest:
public interface ICommand {
function execute():*;
}This represents a synchronous command, so the logic that is executed by an implementation will be available immediately after the call to the execute() Executes the command. method, and typically be returned in the execute() Executes the command.'s result.
More interesting is the IAsyncCommand Describes an asynchronous command. interface which is actually just a combination of the ICommand Interface to be implemented by command classes. and IOperation The IOperation interface describes an asynchronous operation. interfaces:
public interface IAsyncCommand extends ICommand, IOperation {
}This interface allows a command to be constructed that will execute an asynchronous action. So contrary to an IOperation The IOperation interface describes an asynchronous operation. instance an IAsyncCommand Describes an asynchronous command. can be created, but its logic executed at a later moment.
Because the command is asynchronous the result Invokes the <code>unregister()</code> method. property will be null directly after the execute() Executes the command. method has been called, also the result of the execute() Executes the command. method will be either null or undefined. To retrieve the result of the IAsyncCommand Describes an asynchronous command., add listeners for the OperationEvent.COMPLETE and OperationEvent.ERROR events.
Just like IOperations The IOperation interface describes an asynchronous operation. its very well possible that a collection of ICommands Interface to be implemented by command classes. or IAsyncCommands Describes an asynchronous command. (or a mixed collection of them) needs to be executed. For this scenario the CompositeCommand Basic implementation of the <code>ICompositeCommand</code> that executes a list of <code>ICommand</code> instances that were added through the <code>addCommand()</code> method. class is ideal. It allows a list of ICommands Interface to be implemented by command classes. to be executed in parallel or sequence. The CompositeCommand Basic implementation of the <code>ICompositeCommand</code> that executes a list of <code>ICommand</code> instances that were added through the <code>addCommand()</code> method. class is an implementation of the ICompositeCommand Interface that needs to be implemented by command classes that are composed of multiple commands. interface:
public interface ICompositeCommand extends ICommand, IOperation {
function addCommand(command:ICommand):void;
function get numCommands():uint;
function get kind():ComposeiteCommandKind;
}A simple example of the usage of a CompositeCommand Basic implementation of the <code>ICompositeCommand</code> that executes a list of <code>ICommand</code> instances that were added through the <code>addCommand()</code> method. instance would be like this:
var compositeCommand:CompositeCommand = new CompositeCommand(ComposeiteCommandKind.SEQUENCE); compositeCommand.addCommand(new FirstCommand()); compositeCommand.addCommand(new SecondCommand()); compositeCommand.addCommand(new ThirdCommand()); compositeCommand.addCommand(new FourthCommand()); compositeCommand.addCompleteListener(handleCompositeCommandComplete); compositeCommand.addErrorListener(handleCompositeCommandError); compositeCommand.execute();
This will execute the FirstCommand instance, wait for it to complete, execute the SecondCommand instance, wait for it to complete, etc.
If all commands complete successfully the specified handleCompositeCommandComplete method will be invoked, if an error occurs the handleCompositeCommandError method is invoked instead.
The failOnFault property determines whether the CompositeCommand Basic implementation of the <code>ICompositeCommand</code> that executes a list of <code>ICommand</code> instances that were added through the <code>addCommand()</code> method. instance should stop executing its list of commands in the case of an error. Default this property is set to false. So all commands will be executed no matter what their result is.
See the section 'the composite-command XML shortcut' to find out about custom XML configuration for this class.
Having an ICommand Interface to be implemented by command classes. implementation of each and every IOperation The IOperation interface describes an asynchronous operation. in an application isn't always practical, therefore the GenericOperationCommand Generic <code>ICommand</code> implementation that can be used to wrap arbitrary <code>IOperation</code> or <code>IProgressOperation</code> implementations. might help out a little. The GenericOperationCommand Generic <code>ICommand</code> implementation that can be used to wrap arbitrary <code>IOperation</code> or <code>IProgressOperation</code> implementations. is nothing more than a simple IAsyncCommand Describes an asynchronous command. wrapper. All that a GenericOperationCommand Generic <code>ICommand</code> implementation that can be used to wrap arbitrary <code>IOperation</code> or <code>IProgressOperation</code> implementations. needs is the Class of an arbitrary IOperation The IOperation interface describes an asynchronous operation. and an optional list of constructor arguments. For example, here is how the LoadModuleOperation Asynchronous operation that loads a Flex module from a specified URL. would be wrapped:
var genericOperationCommand = new GenericOperationCommand(LoadModuleOperation, 'module.swf'); genericOperationCommand.addCompleteHandler(operationCompleteHandler); genericOperationCommand.execute();
Its not necessary to wrap an IOperation The IOperation interface describes an asynchronous operation. in a GenericOperationCommand Generic <code>ICommand</code> implementation that can be used to wrap arbitrary <code>IOperation</code> or <code>IProgressOperation</code> implementations. when adding it to a CompositeCommand Basic implementation of the <code>ICompositeCommand</code> that executes a list of <code>ICommand</code> instances that were added through the <code>addCommand()</code> method.. The CompositeCommand Basic implementation of the <code>ICompositeCommand</code> that executes a list of <code>ICommand</code> instances that were added through the <code>addCommand()</code> method. also has an addOperation() Adds a <code>Class</code> that is an <code>IOperation</code> implementation along with its optional cosntructor arguments. method which has this signature:
function addOperation(operationClass:Class, ...constructorArgs):ICompositeCommand;
Which is basically the same as the constructor for a GenericOperationCommand Generic <code>ICommand</code> implementation that can be used to wrap arbitrary <code>IOperation</code> or <code>IProgressOperation</code> implementations., so adding a LoadModuleOperation Asynchronous operation that loads a Flex module from a specified URL. to a CompositeCommand Basic implementation of the <code>ICompositeCommand</code> that executes a list of <code>ICommand</code> instances that were added through the <code>addCommand()</code> method. instance would work like this:
compositeCommand.addOperation(LoadModuleOperation, 'module.swf');
The OperationHandler Helper class that generically handles <code>IOperation</code> events and either routes their result or error data to a specified method or assigns them to a specified property on an object instance. is a helper class that generically handles IOperation The IOperation interface describes an asynchronous operation. events and either routes their result or error data to a specified method or assigns them to a specified property on an object instance.
Below are some examples of its usage.
public class MyPresentationModel {
private var _operationHandler:OperationHandler
public var products:Array;
public function MyPresentationModel(){
_operationHandler = new OperationHandler(this.errorHandler);
}
public function getProducts():void {
var operation:IOperation = serviceImplementation.getProducts();
_operationHandler.handleOperation(operation,null,this,"products");
}
protected function errorHandler(error:):void {
//implementation omitted
}
}
If the data returned from the IOperation needs some extra processing, specify an extra method for it like in this example:
public class MyPresentationModel {
private var _operationHandler:OperationHandler
public var products:ArrayCollection;
public function MyPresentationModel(){
_operationHandler = new OperationHandler(this.errorHandler);
}
public function getProducts():void {
var operation:IOperation = serviceImplementation.getProducts();
_operationHandler.handleOperation(operation,convertArray,this,"products");
}
protected function convertArray(input:Array):ArrayCollection {
return new ArrayCollection(input);
}
protected function errorHandler(error:):void {
//implementation omitted
}
}
It makes sense to encapsulate a number of operations in one interface. The easiest example would be the CRUD operations for a specific object.
Let's imagine a user service whose interface looks like this:
public interface IUserService {
function createUser():IOperation;
function updateUser(user:User):IOperation;
function deleteUser(user:User):IOperation;
}In a lot of cases the implementation for such a service will rely on a RemoteObject. For this situation Spring Actionscript offers the base class RemoteObjectService Service that invokes methods on a remote object and returns an <code>IOperation</code> for each of these calls.. So the implementation for the IUserService could inherit from this:
public class UserService extends RemoteObjectService implements IUserService {
public function UserService(remoteObject:RemoteObject) {
Assert.notNull(remoteObject,"remoteObject argument must not be null");
super(remoteObject);
}
public function createUser():IOperation {
return call('createUser');
}
public function updateUser(user:User):IOperation {
return call('updateUser',user);
}
public function deleteUser(user:User):IOperation {
return call('deleteUser',user);
}
}This service could be injected into a presentation model, a command, a supervising presenter, etc. All depending on the specific patterns used in an application.
Let's look at a command example:
public class CreateUserCommand extends AbstractOperation implements IAsyncCommand {
private var _userService:IUserService;
private var _applicationModel:IApplicationModel;
public function CreateUserCommand(userService:IUserService, applicationModel:IApplicationModel) {
Assert.notNull(userService,"userService argument must not be null");
Assert.notNull(applicationModel,"applicationModel argument must not be null");
_userService = userService;
_applicationModel = applicationModel;
}
public function execute():* {
var operation:IOperation = _userService.createUser();
operation.addCompleteListener(handleComplete);
operation.addErrorListener(handleError);
}
protected function handleComplete(event:OperationEvent):void {
_applicationModel.users.addItem(event.result as User);
dispatchCompleteEvent(event.result);
}
protected function handleError(event:OperationEvent):void {
dispatchErrorEvent(event.error);
}
}In this example the command receives a reference to an IUserService and IApplicationModel instance. In this example we assume the IApplicationModel instance has a property called users of type ArrayCollection. Once the IOperation The IOperation interface describes an asynchronous operation. that was received from the service completes it adds the result of the IOperation The IOperation interface describes an asynchronous operation. (which is a User object) to the user collection in the application model.
In order to support more complex execution of ICommand Interface to be implemented by command classes. collections there is finally the ITask Describes an object that is enable to execute a collection of <code>ICommands</code>, both in sequence and in parallel, including simple flowcontrol logic such as if, else, while and for. interface. This interface describes an object that not only can execute collections of ICommands Interface to be implemented by command classes. both in parallel and in sequence, but also does this with basic control flow functionality.
First let's take a quick look and the ITask Describes an object that is enable to execute a collection of <code>ICommands</code>, both in sequence and in parallel, including simple flowcontrol logic such as if, else, while and for. interface:
public interface ITask extends ICommand, IOperation {
function get context():Object;
function set context(value:Object):void;
function get parent():ITask;
function set parent(value:ITask):void;
function next(item:Object, ...constructorArgs):ITask;
function and(item:Object, ...constructorArgs):ITask;
function if_(condition:IConditionProvider=null, ifElseBlock:IIfElseBlock=null):IIfElseBlock;
function else_():IIfElseBlock;
function while_(condition:IConditionProvider=null, whileBlock:IWhileBlock=null):IWhileBlock;
function for_(count:uint, countProvider:ICountProvider=null, forBlock:IForBlock=null):IForBlock;
function exit():ITask;
function reset(doHardReset:Boolean = false):ITask;
function pause(duration:uint, pauseCommand:ICommand=null):ITask;
function end():ITask;
}First thing that is apparent in this interface is that almost each method returns an ITask Describes an object that is enable to execute a collection of <code>ICommands</code>, both in sequence and in parallel, including simple flowcontrol logic such as if, else, while and for. instance. This actually implies that this is a so-called fluent interface. Meaning that method calls to a single instance of an ITask Describes an object that is enable to execute a collection of <code>ICommands</code>, both in sequence and in parallel, including simple flowcontrol logic such as if, else, while and for. can be chained. For instance, if we want to use a Task Abstract base class for <code>IOperation</code> implementations. instance to execute a number of commands in parallel we can write this:
var task:Task = new Task().and(new FirstCommand()).and(new SecondCommand()).and(new ThirdCommand()).and(new FourthCommand());
task.addEventListener(TaskEvent.TASK_COMPLETE, handleTaskComplete);
task.execute();This is basically the same as an ICompositeCommand Basic implementation of the <code>ICompositeCommand</code> that executes a list of <code>ICommand</code> instances that were added through the <code>addCommand()</code> method. set to CompositeCommandKind.PARALLEL Determines that the <code>ICompositeCommand</code> will execute its collection of command all at the same time., so not that interesting. But the nice thing about a Task Abstract base class for <code>IOperation</code> implementations. is the opportunity to mix the different types of execution. So if we first want to execute a number of commands in parallel but after that a few in sequence, we can use the same Task Abstract base class for <code>IOperation</code> implementations. instance:
var task:Task = new Task().and(new FirstCommand()).and(new SecondCommand()).next(new ThirdCommand()).next(new FourthCommand()); task.addEventListener(TaskEvent.TASK_COMPLETE, handleTaskComplete); task.execute();
This will first execute FirstCommand and SecondCommand in parallel and afterwards execute ThirdCommand and FourthCommand in sequence.
The first argument for the next() Adds an <code>ICommand</code> that will be executed in sequence. and and() Adds an <code>ICommand</code> that will be executed in parallel. methods can be either an ICommand Interface to be implemented by command classes. implementation, or a Class that implements the IOperation The IOperation interface describes an asynchronous operation. interface along with an optional list of constructor arguments for the IOperation The IOperation interface describes an asynchronous operation. instantiation. So this can also be a valid invocation:
new Task().and(LoadModuleOperation, 'module.swf');Now let's have a look at some control flow elements, what if a certain command needs to be executed more than once? We can use a for_() Adds a repeating execution block which will be executed a specified number of times, determined by the specified <code>count</code> or <code>ICountProvider</code> parameters. invocation for this:
var task:Task = new Task();
task.for_(10)
.next(new FirstCommand())
.end();
task.addEventListener(TaskEvent.TASK_COMPLETE, handleTaskComplete);
task.execute();
This will execute the FirstCommand 10 times. Now, it won't always be possible to know the exact number of iterations at compile-time. Therefore its possible to pass an optional ICountProvider Describes an object that returns a count that can be used for flow control purposes. instance to the for_() Adds a repeating execution block which will be executed a specified number of times, determined by the specified <code>count</code> or <code>ICountProvider</code> parameters. method. The ICountProvider Describes an object that returns a count that can be used for flow control purposes. interface is quite small:
public interface ICountProvider {
function getCount():uint;
}Any implementations of this interface can perform their own kind of logic to determine the exact count, this implementation can also be an IOperation The IOperation interface describes an asynchronous operation. so the logic for retrieving the count may be asynchronous, internally the ForBlock Base class for <code>ITaskBlock</code> implementations. will take care of this.
An ICommand Interface to be implemented by command classes. may also be executed conditionally, for this use the if_() Adds a conditional execution block to the current <code>ITask</code> which will only be executed if the specified <code>IConditionProvider</code> return <code>true</code>. method and pass an IConditionProvider Describes an object that provides a <code>Boolean</code> value that can be used for flow control purposes. to it:
var task:Task = new Task();
task.if_(new MyConditionProvider())
.next(new FirstCommand())
.end();
task.addEventListener(TaskEvent.TASK_COMPLETE, handleTaskComplete);
task.execute();Now let's take a look at the IConditionProvider Describes an object that provides a <code>Boolean</code> value that can be used for flow control purposes. interface:
public interface IConditionProvider {
function getResult():Boolean;
}The result of the getResult() method determines whether the commands inside the if block will be executed or not. Again, an IConditionProvider Describes an object that provides a <code>Boolean</code> value that can be used for flow control purposes. implementation may also itself be an IOperation The IOperation interface describes an asynchronous operation., and therefore have its logic be executed in an asynchronous manner.
Of course, the if block may also have an else block:
var task:Task = new Task(); task.if_(new ConditionProvider()) .next(new FirstCommand()) .else_() .next(new SecondCommand()) .end(); task.addEventListener(TaskEvent.TASK_COMPLETE, handleTaskComplete); task.execute();
An IConditionProvider Describes an object that provides a <code>Boolean</code> value that can be used for flow control purposes. can also be used by a while block:
var task:Task = new Task(); task.while_(new MyConditionProvider()) .next(new FirstCommand()) .end(); task.addEventListener(TaskEvent.TASK_COMPLETE, handleTaskComplete); task.execute();
In this case the FirstCommand will be executed for as long as the IConditionProvider.getResult() method returns true.
In closing there are three last methods that an ITask Describes an object that is enable to execute a collection of <code>ICommands</code>, both in sequence and in parallel, including simple flowcontrol logic such as if, else, while and for. implementation offers:
Exit(): stops the execution of the entire task.
Reset(): restarts the execution of the current task.
Pause(): pauses the execution of the current task for a specified period.
Configuring a task in Spring Actionscript XML is of course the final piece of functionality that Spring Actionscript offers. There is a namespace handler to make task configuration a little easier. There's two things that need to be added to the context and configuration.
First of all add the namespace to the configuration:
<?xml version="1.0" encoding="utf-8"?> <objects xmlns="http://www.springactionscript.org/schema/objects" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:t="http://www.springactionscript.org/schema/task" xsi:schemaLocation=" http://www.springactionscript.org/schema/objects http://www.springactionscript.org/schema/objects/spring-actionscript-objects-1.0.xsd http://www.springactionscript.org/schema/task http://www.springactionscript.org/schema/util/spring-actionscript-task-1.1.xsd"> <!-- further markup ommitted --> </objects>
After that an instance of the TaskNamespaceHandler Converts specialized <code>Task</code> related markup to <code>ObjectDefinitions</code>. class needs to be added to the application context:
applicationContext.addNamespaceHandler(new TaskNamespaceHandler());
That's it, the application context is now ready to be able to parse task specific configuration XML.
Let's re-create the example where two commands are executed in parallel and two in sequence:
<t:task id="testTask" scope="prototype"> <t:and> <object id="command1" scope="prototype" class="classes.commands.FirstCommand"/> </t:and> <t:and> <object id="command2" scope="prototype" class="classes.commands.SecondCommand"/> </t:and> <t:next> <object id="command3" scope="prototype" class="classes.commands.ThirdCommand"/> </t:next> <t:next> <object id="command3" scope="prototype" class="classes.commands.FourthCommand"/> </t:next> </t:task>
Its also possible to declare the command objects outside of the task specific XML:
<object id="command1" scope="prototype" class="classes.commands.FirstCommand"/> <object id="command2" scope="prototype" class="classes.commands.SecondCommand"/> <object id="command3" scope="prototype" class="classes.commands.ThirdCommand"/> <object id="command3" scope="prototype" class="classes.commands.FourthCommand"/> <t:task id="testTask" scope="prototype"> <t:and command="command1"/> <t:and command="command2"/> <t:next command="command3"/> <t:next command="command4"/> </t:task>
A conditional execution could be configured like this:
<object id="command1" scope="prototype" class="classes.commands.FirstCommand"/> <object id="command2" scope="prototype" class="classes.commands.SecondCommand"/> <object id="myCondition" scope="prototype" class="classes.condition.MyConditionProvider"/> <t:task id="testTask" scope="prototype"> <t:if condition="myCondition"> <t:next command="command1"/> <t:else/> <t:next command="command2"/> </t:if> </t:task>
A for loop would look like this:
<object id="command1" scope="prototype" class="classes.commands.FirstCommand"/> <object id="command2" scope="prototype" class="classes.commands.SecondCommand"/> <t:task id="testTask" scope="prototype"> <t:for count="10"> <t:next command="command1"/> <t:next command="command2"/> </t:for> </t:task>
Or, when using an ICountProvider Describes an object that returns a count that can be used for flow control purposes.:
<object id="command1" scope="prototype" class="classes.commands.FirstCommand"/> <object id="command2" scope="prototype" class="classes.commands.SecondCommand"/> <object id="myCount" scope="prototype" class="classes.counts.MyCountProvider"/> <t:task id="testTask" scope="prototype"> <t:for count-provider="myCount"> <t:next command="command1"/> <t:next command="command2"/> </t:for> </t:task>
And finally, of course, the while loop:
<object id="command1" scope="prototype" class="classes.commands.FirstCommand"/> <object id="command2" scope="prototype" class="classes.commands.SecondCommand"/> <object id="myCondition" scope="prototype" class="classes.condition.MyConditionProvider"/> <t:task id="testTask" scope="prototype"> <t:while condition="myCondition"> <t:next command="command1"/> <t:next command="command2"/> </t:while> </t:task>
To add, for instance, a LoadModuleOperation Asynchronous operation that loads a Flex module from a specified URL. to a task, this XML mark up would normally be needed:
<t:task id="testTask" scope="prototype">
<t:and>
<object class="org.springextensions.actionscript.core.command.GenericOperationCommand">
<constructor-arg value="org.springextensions.actionscript.module.LoadModuleOperation" type="Class"/>
<constructor-arg value="modules/mymodule.swf"/>
</object>
</t:and>
//etc...
</t:task>This is quite verbose so Spring Actionscript offers XML shortcuts for a number of common operations:
Here's an example of how all these operations can be configured in XML for a task:
<t:task id="testTask" scope="prototype"> <t:next> <t:load-module url="modules/mymodule.swf" application-domain="appDomainObjectName" security-domain="secDomainObjectName"/> </t:next> <t:next> <t:load-properties-batch locations="props/properties1.properties,props/properties2.properties" ignore-resource-not-found="true" prevent-cache="false"/> </t:next> <t:next> <t:load-properties location="props/properties1.properties" prevent-cache="false"/> </t:next> <t:next> <t:load-resource-module url="resource.swf" update="false" application-domain="appDomainObjectName" security-domain="secDomainObjectName"/> </t:next> <t:next> <t:load-style-module url="styles.swf" update="false" application-domain="appDomainObjectName" security-domain="secDomainObjectName" flex-module-factory="moduleFactoryName"/> </t:next> <t:next> <t:load-url url="someAsset.png" data-format="binary"/> </t:next> <t:next> <t:load-url-stream url="someAsset.png"/> </t:next> </t:task>
The application domain attributes could be configured like this as well:
<t:load-resource-module url="resource.swf" update="false" application-domain="this.applicationDomain"/>This would assign the application domain that is associated with the application context that has consumed this configuration. See this section: 'Injecting the application context using the 'this' reference' for more information.
In that same vain the flex module factory for a LoadStyleModuleOperation could be configured like this:
<t:load-style-module url="styles.swf" update="false" application-domain="this.applicationDomain"
flex-module-factory="this.ownerModule.moduleFactory"/>A CompositeCommand can also be configured
using a bit of custom XML, instead of writing
this...
<object id="myCompositeCommand" class="org.springextensions.actionscript.core.command.CompositeCommand">
<property name="failOnFault" value="true"/>
<property name="kind">
<ref>
<util:constant static-field="org.springextensions.actionscript.core.command.CompositeCommandKind.PARALLEL.value"/>
</ref>
</property>
<method-invocation name="addCommand">
<arg>
<object id="myCommand1" class="..."/>
</arg>
</method-invocation>
<method-invocation name="addCommand">
<arg>
<object id="myCommand2" class="..."/>
</arg>
</method-invocation>
<!-- etc, etc -->
</object>...this rather more abbreviated form is possible using the Task namespace handler:
<t:composite-command fail-on-fault="true" kind="parallel"> <object id="myCommand1" class="..."/> <object id="myCommand2" class="..."/> </t:composite-command>