-
Notifications
You must be signed in to change notification settings - Fork 3
Aspect oriented programming
CFlow supports aspect oriented programming by making every task a potential join point. This means that you can attach one or more advice implementations to any task, which are run when the task executes. Although join-point models in other languages can be much more granular than this, it is still quite powerful and if you define your tasks carefully and keep them atomic, it is probably exactly what you need.
In CFlow, an advice is nothing more than a decorator for a task. It should implement the cflow.Task interface, but it is easier to just extend cflow.Advice. In the run() method you implement whatever functionality you need executed.
The run() method is responsible for the call to the run() method of the task it decorates; it is up to you if the actual task is executed at all, or, if needed, multiple times. So you can create 'before', 'after' and 'around' advice by placing the call to the decorated task at the appropriate spot.
An example of an 'around' advice:
component extends="cflow.Advice" {
public boolean function run(required Event event) {
transaction {
var success = super.run(arguments.event);
};
return success;
}
}
As you can see, this advice wraps a transaction around the code that is executed by the actual task. Since we're extending cflow.Advice, we can call super to run the task. If you don't want to extend this component, you have to implement cflow.Task. CFlow then expects your init method to accept one argument: the task to be decorated.
The run() method returns a boolean. If it is true, event execution proceeds. In most cases you will just want to return whatever the decorated task returns, like in the example.
Now that we have this in place, we can attach this advice to any task, like this for instance:
<invoke handler="save" advice="mapping.to.advice" />
The advice attribute accepts more than one advice. Specify multiple advice components in a comma-separated list. The order of the list determines the order of execution, so the first advice is run first. This means the first task is the last one to decorate the task.
It is possible that you need a particular advice all over the place, or you need to select an advice based on some condition. Debugging is one example of the second instance. Another way to enable aspects in CFlow, is to write your own Context component. The Context provides factory methods for all the different task components. It is quite easy to extend Context and override these methods so you can explicitly decorate the tasks yourself. In fact, the debug context does exactly that.
However, if you follow this route, and you want the debug information to remain available, there is currently no other way but to extend the debug context during development.
Decorating tasks with advice is the last thing that happens when the configuration is instantiated. This means that the debug context has no knowledge about that. Therefore, the debug output doesn't show anything about any advice. Of course, event.record() is available within the advice, and exceptions are rendered as usual.