The Command Framework is a general abstraction for synchronous and asynchronous operations. It aims at simplifying and streamlining the creation of asynchronous operations and grouping them for sequential or parallel execution.
Many operations in Flex and Flash operations are asynchronous, as AS3 is not multithreaded and there are no blocking calls. Most of these operations come with their own APIs and their own particular set of events that mark completion or failure of its execution. Chaining several of these operations together usually requires a lot of plumbing that distracts from the actual business logic. With Spicelib Commands it is easier to keep each operation in a clean self-contained unit and easily chain them together with a convenient, fluent builder API.
The Commands Framework is a replacement for the Task Framework that was part of Spicelib 1 and 2. It is intended to be simpler to use and more powerful at the same time. The Task Framework is discontinued and no longer part of Spicelib 3.
The implementation of a command follows simple naming conventions. It does not require to extend a framework base class or implement a framework interface. There are two major reasons for this design decision. The first is to keep command implementations very simple and lightweight and useful even when used without the framework. The second is to keep the method signatures flexible to allow to pass data and callbacks to the execute method, as you'll see in the examples. The only downside is a negligible loss in type-safety.
The style of command implementation shown in this section is just the default. The documentation focuses on this style as it is very straightforward and should be sufficient for most use cases. If you are picky about details, prefer to code against interfaces or base classes instead, have special requirements or are a framework developer who needs additional functionality you can read 21.5 Extending the Framework for an overview over alternatives.
A synchronous command may look as simple as this:
public class SimpleCommand {
public function execute (): void {
trace("I execute, Therefore I am");
}
}
The only naming convention a synchronous command needs to adhere to, is to have a public method called execute. It may have parameters (see 21.3.4 Passing Data to the Execute Method) or a return type (see 21.1.5 Producing a Result), but both are optional.
An asynchronous command has to accept a callback function either as a method parameter or a public property. It invokes the callback method to signal command completion.
Callback as a Method Parameter
Any parameter in the execute method that is of type Function is interpreted as being the callback for an asynchronous command. The ordering does not matter, if you also want the framework pass data from previous commands the callback may be at any position in the parameter list:
public class MyAsyncCommand {
public function execute (callback: Function): void {
callback(true);
}
}
In the example above the actual execution is synchronous, since the callback is invoked immediately. This is legal, as it allows to implement commands with transparent caching for example (if the result is already available, return it immediately, otherwise call some service asynchronously). See the next section for an example for such a command.
Callback as a Public Property
When you start an asynchronous operation, you usually want to keep the callback as a reference and invoke it later. You can store it in a property manually when receiving it as a method parameter in the execute method, but in most cases it is more convenient to get it injected into a property:
public class MyAsyncCommand {
public var callback:Function;
public function execute (): void {
// rest of the implementation omitted for clarity:
var result:Object = getResultFromCache();
if (result) {
callback(result);
}
else {
getService().fetchData(resultHandler, errorHandler);
}
}
private function resultHandler (result: Object): void {
callback(result);
}
private function errorHandler (error: ErrorEvent): void {
callback(error);
}
}
The naming convention requires that the public property is called callback. In the example above the callback gets invoked immediately when cached data is available, otherwise the result and error handlers invoke it later. As you see they both just pass the result or error instance to the callback, see the next section to understand how the framework interprets these parameters.
How to Invoke the Callback
The callback invocation is interpreted differently depending on whether you pass a parameter and what type it is of. Each asynchronous command can have three different final states after execution, and they are interpreted by the callback as follows:
Error
or ErrorEvent
to the callback. Per default only Error
or ErrorEvent
instances are interpreted as an error outcome, but the list
can be extended with custom error types:
LightCommandAdapter.addErrorType(FaultEvent);
Here we add the Flex FaultEvent
as an error type. Since Spicelib Commands is a pure AS3 library it does not know
about Flex classes. The LightCommandAdapter
is the class that adapts the type of command demonstrated in this chapter
to the framework interfaces.
When the command does not produce a result, it is recommended to simply pass true
to the callback, as no parameter
is interpreted as cancellation.
Invoking the callback with more than one parameter is illegal and leads to an error. A function reference is quite weak
in terms of type-safety, but it allows to keep the command implementation decoupled from the framework APIs. Later releases
might alternatively allow the injection of a concrete type (e.g. CommandCallback
) with a more explicit, typed API.
If you want to signal an error condition instead of successful completion you can do that for synchronous and asynchronous commands in two different ways:
Synchronous Commands
public class FaultyCommand {
public function execute (): void {
throw new Error("Sorry, I do not function properly");
}
}
A synchronous command may simply throw an error. If it is executed in a sequence or flow this will lead to the configured error handling and potentially cancel the sequence or flow with an error event.
Asynchronous Commands
public class FaultyCommand {
public var callback:Function;
public function execute (): void {
getService().fetchData(resultHandler, errorHandler);
}
[...]
private function errorHandler (error: ErrorEvent): void {
callback(error);
}
}
For an asynchronous command an error condition can be signalled by passing a parameter type which is interpreted as an error like already shown in the previous section.
An asynchronous command can support cancellation. There are two ways to cancel a command: from within by invoking
the callback without parameters, like already shown in preceding sections, or from the outside when the command has
a method called cancel
.
Cancellation from within the Command
public class MyCancellableCommand {
public var callback:Function;
public function execute (): void {
callback(); // invocation without parameters interpreted as cancellation
}
}
Cancellation from the Outside
public class MyCancellableCommand {
public var callback:Function;
public function execute (): void {
// start operation
}
public function cancel (): void {
// cancel operation
}
}
Here the command adds a method called cancel
. If the command is part of a sequence or flow which
gets cancelled, the framework will invoke the cancel
method so that you can stop whatever asynchronous
operation you started. When the active command in a sequence does not have a cancel method, the entire sequence
cannot be cancelled and invocation of its cancel method leads to an error. Still, adding a cancel method is
entirely optional.
When a command gets cancelled from the outside there is no need to invoke the callback to signal cancellation.
Note that such a cancel method is only meant to be called by the framework. Invoking it directly in your application code does not have any effect as the framework cannot intercept these calls.
Any command (both synchronous and asynchronous) can produce a result. This is particularly useful as the result value can get injected into subsequent commands (either to their constructor or their execute method) and also to result handlers you add to command executors (see the next section).
Synchronous Commands
A synchronous command can produce a result through a return value:
public class CommandWithResult {
public function execute (): String {
return "Hello World!";
}
}
The result can be of any type.
Asynchronous Commands
An asynchronous command can produce a result through passing a value to the callback function:
public class MyAsyncCommand {
public var callback:Function;
public function execute (): void {
getService().fetchData(resultHandler);
}
private function resultHandler (result: Object): void {
callback(result);
}
}
Again, the result can be of any type, except for the few interpreted as an error (like Error
and ErrorEvent
).
The framework comes with a fluent API to create commands, add result and error handlers, specify timeouts and execute them. For a single command this is usually not particularly useful, as you could as well just instantiate and invoke them yourself. But even for a single command it allows to wrap features like timeouts around your command without the need to deal with this type of functionality in the command implementation.
Commands.wrap(new MySimpleCommand())
.timeout(10000)
.result(resultHandler)
.error(errorHandler)
.execute();
private function resultHandler (result: Object): void {
[...]
}
private function errorHandler (cause: Object): void {
[...]
}
The real power of this API though is grouping commands for sequential or parallel execution or to command flows where subsequent commands are determined by dynamic links that interpret the result of the preceding command. You can find examples for how to execute these command types in 21.2 Command Groups and 21.4 Command Flows.
The real power behind the concept of abstracting asynchronous operations becomes apparent when you want to execute more than just one command. Spicelib Commands allow for grouping commands for parallel or sequential execution. There is also an advanced grouping mode called command flows that uses dynamic decision points between commands. See 21.4 Command Flows for details. This chapter only covers sequential and parallel execution.
A simple sequence with 2 commands can be created and executed like this:
Commands
.asSequence()
.add(new LoginCommand())
.add(new LoadUserProfileCommand())
.lastResult(resultHandler)
.error(errorHandler)
.execute();
private function resultHandler (result: UserProfile): void {
[...]
}
private function errorHandler (failure: CommandFailure): void {
trace("Command " + failure.target + " failed, cause: " + failure.cause);
}
The fluent API allows to declare the commands and handlers of the group and execute it in one statement.
The error handler always has to accept a parameter of type CommandFailure
. This is a type that neither
extends Error
nor ErrorEvent
. It allows to inspect the target command in the group that failed
as well as the cause (usually an instance of type Error
nor ErrorEvent
, but potentially something
else).
The result handler above is only interested in the last result produced in that sequence. This might be quite common for sequences, in particular in cases where the result of previous commands get injected into and processed by subsequent commands (see 21.3 Passing Data to Commands for details).
Alternatively all results produced by the sequence may get inspected:
Commands
.asSequence()
.add(new LoadContactsCommand())
.add(new LoadUserProfileCommand())
.allResults(resultHandler)
.execute();
private function resultHandler (result: CommandData): void {
trace("Contacts: " + result.getObject(Contacts));
trace("Profile: " + result.getObject(UserProfile));
}
Less common than sequences, but still quite handy to have when needed.
The syntax is identical expect for calling inParallel
instead of asSequence
:
Commands
.inParallel()
.add(new LoadContactsCommand())
.add(new LoadUserProfileCommand())
.allResults(resultHandler)
.error(errorHandler)
.execute();
The rules for result and error handlers are the same as for sequences.
Instead of passing existing instances to add
, you can alternatively just
specify the command class. This way the instantiation will be deferred until the command actually gets used.
This might be useful for flows (where some commands may never get executed) or when you want to pass results
of preceding commands to the constructor of a subsequent one, which is only possible if the framework creates
the instance for you.
Commands
.asSequence()
.create(LoginCommand)
.create(LoadUserProfileCommand)
.lastResult(resultHandler)
.error(errorHandler)
.execute();
Command Groups allow for the same set of optional features like the methods for executing a single command:
Commands
.asSequence()
.delay(1000)
.add(new LoginCommand())
.add(new LoadUserProfileCommand())
.timeout(30000)
.lastResult(resultHandler)
.error(errorHandler)
.execute();
In case of sequences the delay can be placed anywhere in the sequence, before the first or between two commands.
Sometimes you may want to use the API for dealing with a single command to define something
specific to that instance and then add it as part of a sequence or flow. In this case you can simply
call build
in the end instead of execute
, which just gives you a command instance
with those extra features applied, but without actually executing it. You can then add it to any
group of commands:
var login:Command = Commands
.wrap(new LoginCommand())
.timeout(30000)
.result(someHandlerOnlyForThisCommand)
.build();
Commands
.asSequence()
.add(login)
.add(new LoadUserProfileCommand())
.lastResult(resultHandler)
.error(errorHandler)
.execute();
Often all commands executed in a sequence are self-contained and do not need to know each other. But sometimes the result of one command is needed when executing one of the subsequent commands. Preferrably this happens without the subsequent command needing any kind of knowledge about the type of the command that produced the result or any of its implementation details. This section provides an overview over the available options for passing results in a decoupled way.
When the commands executed in a sequence are all relatively close in a sense that they belong to the same functional area of the application, the most straightforward way is often to use a shared application-specific model instance that multiple commands have access to:
var model:LoginModel = new LoginModel();
Commands
.asSequence()
.add(new LoginCommand(model))
.add(new LoadUserProfileCommand(model))
.lastResult(resultHandler)
.error(errorHandler)
.execute();
Here the model gets passed to the constructor of both command instances. The first one can pass the result to the model before dispatching the complete event, so that the second instance has access to it when it gets executed.
For objects from the same functional area where decoupling commands from each other is not a concern, this might sometimes be the best approach, as it is very easy to use and does not require any help from the framework.
When you want to keep commands decoupled in cases where the tasks they perform are largely unrelated, you can rely on the framework to pass the results for you. There are multiple different approaches available as described below. Note that although this is based on some sort of injection, it does not require an IOC container. This section still only describes the capabilities of the standalone Spicelib Commands project.
Any result from a command that has already been completed as part of a sequence or flow can be passed to the constructor of a subsequent command:
function MyCommand (user:User, config:XML) {
this.user = user;
this.config = config;
}
The logic for finding the result to inject is fairly simple: the framework looks for the result by type (reflecting on the constructor argument types), and if there is more than one matching type picks the one that was added last. This should already cover most real-world requirements. If you need more complex result lookup capabilities, you can still do explicit lookups as described further below.
If no matching result is found, the sequence will abort with an error, unless you marked the parameter as optional:
function MyCommand (user:User, config:XML = null) {
It should be obvious that constructor injection can only work when you pass the type
of the command to the create
method instead of an existing instance, as you need
to leave it up to the framework to instantiate the command for you in this case:
Commands
.asSequence()
.create(LoginCommand)
.create(LoadUserProfileCommand)
.create(MyCommand)
.lastResult(resultHandler)
.error(errorHandler)
.execute();
It is recommended to only use constructor injection when you target Flash Player 10.1 or newer,
as older players had a nasty reflection bug that always reported *
as the parameter type
when you reflected before the VM created the first instance of that class.
Alternatively results from preceding commands can also get injected into the execute method:
public function execute (user: User, config: XML): void {
[...]
}
The rules are the same as for constructor injection: the framework looks for the result by type (reflecting on the parameter types), and if there is more than one matching type picks the one that was added last.
Obviously this type of injection also works for existing command instances:
Commands
.asSequence()
.add(LoginCommand)
.add(LoadUserProfileCommand)
.add(MyCommand)
.lastResult(resultHandler)
.error(errorHandler)
.execute();
Sometimes you want to be flexible. You might want to expect a User instance as a parameter of the execute method, and in some cases it might come from a preceding login command, while in other cases it might already be available. In the latter case you can manually pass data when wiring up the commands:
var user:User = ...;
Commands
.asSequence()
.add(LoadUserProfileCommand)
.add(MyCommand)
.data(user)
.lastResult(resultHandler)
.error(errorHandler)
.execute();
The outcome is the same as in previous examples where the User instance was produced by a login command. The data method in the builder API can be invoked multiple times.
Command Flows add the concept of decision points to define a dynamic sequence of commands. The command builder API offers several declarative means of defining decision points based on result type, value or property value to cover the most common scenarios as well as a way to add a custom link instance in case some other type of decision logic is required. A command link simply determines the next command to execute based on the result of the previous command.
Let's show a simple example where you want to execute the command to load the admin console of your application only when the user that just logged in is indeed an administrator. This example assumes that the instance returned from the server is different depending on the role of the user:
var profileLoader:Command = new ProfileLoaderCommand("some/serviceUrl");
var flow:CommandFlowBuilder = Commands.asFlow();
flow.add(new LoginCommand())
.linkResultType(AdminUser).toCommandType(LoadAdminConsoleCommand)
.linkResultType(User).toCommandInstance(profileLoader);
flow.create(LoadAdminConsoleCommand)
.linkAllResults().toCommandInstance(profileLoader);
flow.timeout(30000)
.lastResult(resultHandler)
.error(errorHandler)
.execute();
We use the linkResultType
method to branch to different command types based on the type of the result
produced by the LoginCommand
.
You may not always have commands that finish with a different result type for all possible outcomes. In these cases you can alternatively link by value and not by class and use something like String constants that the commands may set as a result:
var profileLoader:Command = new ProfileLoaderCommand("some/serviceUrl");
var flow:CommandFlowBuilder = Commands.asFlow();
flow.add(new LoginCommand())
.linkResultValue(MyConstants.ADMIN_LOGIN).toCommandType(LoadAdminConsoleCommand)
.linkResultValue(MyConstants.USER_LOGIN).toCommandInstance(profileLoader);
flow.create(LoadAdminConsoleCommand)
.linkAllResults().toCommandInstance(profileLoader);
flow.add(profileLoader)
.linkAllResults().toFlowEnd();
flow.timeout(30000)
.lastResult(resultHandler)
.error(errorHandler)
.execute();
If the result type is always the same, but carries a property value that can be used to determine the next command, the following syntax can be used:
var profileLoader:Command = new ProfileLoaderCommand("some/serviceUrl");
var flow:CommandFlowBuilder = Commands.asFlow();
flow.add(new LoginCommand())
.linkResultProperty("isAdmin", true).toCommandType(LoadAdminConsoleCommand)
.linkResultProperty("isAdmin", false).toCommandInstance(profileLoader);
flow.create(LoadAdminConsoleCommand)
.linkAllResults().toCommandInstance(profileLoader);
flow.add(profileLoader)
.linkAllResults().toFlowEnd();
flow.timeout(30000)
.lastResult(resultHandler)
.error(errorHandler)
.execute();
Here the login command will always return an instance of User
, but the isAdmin
property
lets us know whether she has the admin role:
public class User {
public var isAdmin: Boolean;
[...]
}
Like already shown in all preceding examples, a link can explicitly point to the end of the flow:
flow.add(profileLoader)
.linkAllResults().toFlowEnd();
Since the default behaviour of a command flow in case no link matches the result is to cancel the flow, the end of the flow has to be specified explicitly to reach successful completion of the flow.
Whenever none of the specified links match it is interpreted as cancellation of the flow. Alternatively you can also specify a catch-all link like this:
flow.add(new LoginCommand())
.linkResultType(AdminUser).toCommandType(LoadAdminConsoleCommand)
.linkResultType(User).toCommandInstance(profileLoader);
.linkAllResults().toCommandType(InitGuestModeCommand);
Here the final link is only processed if the first two links both do not match the results.
The examples above covered a more declarative way of linking commands that should be fine for many real world scenarios. Nevertheless sometimes you'd require custom logic. The best way to add a very simple condition is an inline function:
flow.add(new LoginCommand())
.linkFunction(function (result: CommandResult, processor: CommandLinkProcessor): void {
if (event.result is User && User(event.result).loginCount < 3) {
processor.executeCommand(create(ShowNewUserDashboardCommand));
}
else {
processor.executeCommand(create(LoadUserProfileCommand));
}
});
private function create (commandType: Class): Command {
return Commands.create(commandType).build();
}
The signature of the method is the same as for the link
method in the CommandLink
interface. Instead of specifying the next command to execute you can alternatively trigger the successfull
completion of the flow, an Error or flow cancellation, using the methods of the CommandLinkProcessor
instance.
Finally, if the logic is more complex and it is justified to extract it into a separate class
you can also use any implementation of the CommandLink
interface:
flow.add(new LoginCommand()).link(new MyCustomLink());
The linkFunction
and link
methods produce the only type of links that
even get processed when the preceding command finished with an Error. This way even different
error conditions can be linked if required. The other declarative ways of linking like the
linkResultType
or linkResultProperty
methods are only considered when
the preceding command completed successfully.
The preceding sections primarily covered the basic functionality of the library. It should be sufficient for most real world scenarios. This chapter gives an overview over available extension points. It is only relevant if you want to tweak Spicelib Commands to your needs or want to integrate it into another framework (like an IOC container for example).
The two major extension points are command adapters and the lifecycle hooks.
Adapters usually serve one of the following two purposes:
To integrate a new adapter the following steps must be performed:
CommandAdapter
interface CommandAdapterFactory
interface The remaining sections explain these steps.
Implementing the CommandAdapter interface
To make your life easier you can extend the AbstractSuspendableCommand
base class
that covers some of the basic plumbing and then implement the CommandAdapter
interface
on top of it.
Your adapter then must override the doExecute
method to execute the target method
and then invoke the protected complete
or error
methods when the target command
finished its execution. If your adapter supports cancellable or suspendable commands, it is also
supposed to override the doCancel
and/or doSuspend
and doResume
methods.
The default command type of the library is itself based on an adapter.
If you want to study the implementation as an example you can browse the code
of the LightCommandAdapter
class.
Implementing the CommandAdapterFactory interface
This class is responsible to actually create new adapter instances based on an already existing target command instance. The interface is simple:
public interface CommandAdapterFactory {
function createAdapter (instance:Object, domain:ApplicationDomain = null) : CommandAdapter;
}
Whenever a command instance that does not already implement one of the Command interfaces is added to one of the executors created by the various command builder APIs, the framework consults all available command adapter factories to try to turn the command instance into an adapter instance. The adapter factories will be executed one after the other until one of them returns an adapter (Chain of Responsibility pattern). In case your adapter factory does not "recognize" the provided instance it should return null to signal to the framework that it should ask the next adapter. If no factory feels responsible for a command instance, an error will be thrown.
Registering a CommandAdapterFactory
Finally the factory must be registered with the framework:
CommandAdapterFactories
.addFactory(new MyFactory())
.order(1);
The order determines which factory gets asked in which order in case there are multiple factories registered.
A result processor can be registered to process certain types of results. This allows to transparently modify or transform these result types without the need of the command itself being aware of this kind of processing.
A result processor itself must be implemented as a command. The class can then get registered centrally, causing the framework to create a new instance of that result processor command for each matching result produced by some other command.
This section will show the AsyncToken
support from Parsley as
an example. The Spicelib Commands library does not know about AsyncTokens as it does
not depend on the Flex SDK and can be used in pure Flash applications, too.
A result processor for an AsyncToken works around the fact that the moment a command returns an AsyncToken the actual result is not available yet. It removes the need for plumbing around Responders inside the command itself, if we move this taks to an external result processor.
The implementation of this processor looks like this:
public class AsyncTokenResultProcessor {
private var active: Boolean;
private var callback: Function;
public function execute (token: AsyncToken, callback: Function): void {
this.callback = callback;
active = true;
token.addResponder(new Responder(result, fault));
}
private function result (event: ResultEvent): void {
if (!active) return;
callback(event.result);
active = false;
}
private function fault (event: FaultEvent): void {
if (!active) return;
callback(event.fault);
active = false;
}
public function cancel (): void {
active = false;
}
}
The AsyncToken produced by any other command will get passed to the execute method of this processor. It then adds a responder and waits for either a result or a fault to be returned. In both cases the result or the fault are then simply passed to the callback.
To register this processor only one more line of code is needed (must be executed before the first command is started):
ResultProcessors.forResultType(AsyncToken).processorType(AsyncTokenResultProcessor);
In this special case we also must tell the framework that an instance of Fault
signals an error condition (again Spicelib does not depend on the Flex SDK, so it does not
know about Faults):
LightCommandAdapter.addErrorType(Fault);
Without this registration, Faults would simply be interpreted as successful results.
With all these pieces in place, a command based on an AsyncToken can then look as simple as this:
public class GetUserListCommand {
private var service: RemoteObject;
function GetUserListCommand (service: RemoteObject) {
this.service = service;
}
public function execute (): AsyncToken {
return service.getUserList();
}
}
The result processor would kick in as soon as this command returns the AsyncToken and treat the result produced of this processor as the final result, not the AsyncToken.
Note that if you are using Spicelib Commands in Parsley and add the parsley-flex.swc, the result processor shown above will be registered automatically.
Similarly you could create result processors for URLLoader
objects or other
asynchronously executing objects.
But result processors can also be project-specific, processing or transforming command results centrally.
Another extension point provided by the library are the lifecycle hooks. They are particularly useful when you want to integrate the Spicelib Commands into a different kind of framework, like an IOC container for example. The Parsley framework is using these hooks in version 3 for the redesigned command support. But other containers can integrate in the same way.
For integration into a container, usually the following functionality is desirable:
To make this work the CommandExecutor
interface contains a method
called prepare that allows to pass customized implementations of the CommandData
and CommandLifecycle
interfaces down to the executed commands:
public interface CommandExecutor extends SuspendableCommand {
function prepare (lifecycle:CommandLifecycle, data:CommandData) : void;
[...]
}
All built-in executors like those for executing command flows or sequences implement
this interface. When the prepare method is invoked before executing the flow or sequence
the specified CommandLifecycle
and CommandData
instances will get passed
down to all individual commands.
The CommandLifecycle Interface
The interface looks like this:
public interface CommandLifecycle {
function createInstance (type:Class, data:CommandData) : Object;
function beforeExecution (command:Object, data:CommandData) : void;
function afterCompletion (command:Object, result:CommandResult) : void;
}
It is responsible for creating command instances, and it may execute additional logic
before and after execution of the command. The beforeExecution
and afterExecution
methods are the hooks that can be used for adding and removing commands to a container.
The CommandData Interface
Finally the CommandData interface can be used to integrate the simple injection features of Spicelib Commands (that allow for injection of results produced by preceding commands) with the injection facility of a container.
public interface CommandData {
function getObject (type:Class = null) : Object;
function getAllObjects (type:Class = null) : Array;
}
These methods will get invoked by the framework when an execute method
or a command constructor expects a specific type to get injected. Your custom
CommandData
implementation will only get invoked when the framework
cannot find a matching type itself (e.g. from the results of preceding commands).
This allows for seamless integration.
Putting it all together
Once you have your custom CommandLifecycle
and CommandData
implementations you may want to create a nice builder API so that your users do not
need to care about these low-level details. This builder API might be similar to the
one built into Spicelib. It would allow to create, group and execute commands
and under the hood it would silently create instances of the lifecylce and data
implementations and pass them to the prepare
method of your executor.