@Extension(name = "Docs")
@Operations(LogDecorator.class)
public class DocsModule {
}
Creating Scopes with Mule SDK
Scopes are similar to Operations, but their execution includes the execution of other child Operations. This means that a Scope is basically an operation that receives one or more arguments that are simple Parameters along with a single Chain
component and the CompletionCallback
.
A Chain
represents the child Operations that you declare inside the Scope and provide an abstraction to execute them.
Declaring a Scope
You can see the basic structure of a Scope with this decorator example. It has the Chain
to be executed and the CompletionCallback
to invoke once the
scope execution is done (yes, all Scopes are declared as Non-Blocking Operations):
public void logDecorator(Chain operations,
CompletionCallback<Object, Object> callback) {
LOGGER.debug("Invoking child operations")
operations.process(
result -> {
LOGGER.debug(result.getOutput());
callback.success(result);
},
(error, previous) -> {
LOGGER.error(error.getMessage());
callback.error(error);
});
}
Of course, if you receive a Chain
of Operation, you need to execute
them in some way, and that is what the process(successCallback, errorCallback)
does.
With it, the example executes the Chain
in a non-blocking fashion and listens for the results in case of success or error. Here, the successCallback
is called with the execution result when a successful execution is completed by all the Operations in the Chain
. The errorCallback
will be called if an error occurs during the execution of any child Operation, with both the error and the last successful result as arguments.
Here is how you use the scope in a Mule app:
<flow name="logDecoratorSampleFlow">
<docs:log-decorator>
<http:request config-ref="config" path="/" method="GET"/>
</docs:log-decorator>
</flow>
This executes the http:request
child operation wrapped by the LOGGER
invocations.
Any number of Operations can be executed inside the scope, including other scopes or routers.
Adding Parameters
Scopes can receive other simple @Parameters
and @ParameterGroups
. They are simple because Scopes are not allowed to receive complex parameters that
are declared as child elements. For example, if you want to customize the log level of the decorator, you will be able to receive that as a Parameter:
public void logDecorator(@Optional(defaultValue="true") boolean debug,
Chain operations,
CompletionCallback<Object, Object> callback) {
if (debug){
LOGGER.debug("Invoking child operations")
}
operations.process(
result -> {
Object message = result.getOutput();
if (debug){
LOGGER.debug(message);
} else {
LOGGER.info(message);
}
callback.success(result);
},
(error, previous) -> {
LOGGER.error(error.getMessage());
callback.error(error);
});
}
With a minimal change in the Scope’s API:
<flow name="logDecoratorSampleFlow">
<docs:log-decorator debug="false">
<http:request config-ref="config" path="/" method="GET"/>
</docs:log-decorator>
</flow>
By default, a Chain
is executed using the Message that was received by the container
Scope, but other definitions of the process
method allow the user of the scope to customize the payload
and attributes
that will be used to execute the Chain
.
Using Stereotypes
Stereotypes
can be used to limit the Operation that are allowed to exist inside a Chain
, which enforces consistency in its usage. Only the Operations that have the given Stereotypes
can be declared inside the Scope.
public void assertAll(@AllowedStereotypes(AssertionStereotype.class) Chain assertions,
@Optional boolean failOnError,
CompletionCallback<Boolean, Void> callback) {
assertions.process(
result -> {
callback.success(Result.<Boolean, Void>builder().output(true).build());
},
(error, previous) -> {
if (failOnError){
callback.error(error);
} else {
callback.success(Result.<Boolean, Void>builder().output(false).build());
}
});
}
<flow name="logDecoratorSampleFlow">
<docs:assert-all>
<docs:not-null value="#[payload]">
<docs:not-empty-collection value="#[payload]">
</docs:assert-all>
</flow>
Config-less and Connection-less
Scopes have some restrictions that differentiate them from Operations. By definition, Scopes are not allowed to depend on or receive a particular Configuration or Connection.
Only One Chain
The Chain
component is key in the definition of a Scope, so it is required to have one and only one Chain
argument.