<file:write path="hello.txt" overwrite="true" content="#[payload]" />
Content Parameters
Consider the following operation (pseudocode):
Notice that the parameters that compose operations often play different roles.
Behavior Parameters
These parameters manage settings that determine how the operation is going to behave. In the example above, the overwrite
parameter configures the action to take if the file already exists. The path
parameter indicates where to write the content. Both parameters configure the operation, but because this is a Write operation,
they do not represent the main concept, which is the data to be written.
Some operations are exclusively formed by behavior parameters, for example:
<file:copy from="somePath" to="anotherPath" />
Both parameters are behavioral because the operation does not take any content. The content is in the file that is copied.
Content Parameters
If you consider the explanation of behavior parameters above, the definition of a content parameter is obvious. In the file:write
example above, the content is a @Content
parameter.
Content parameters have the following characteristics:
-
They must accept expressions. Both SUPPORTS_EXPRESSIONS and EXPRESSION_REQUIRED are supported, but compilation will fail if
@Expression(NOT_SUPPORTED)
is used. -
Each content parameter can embed its own DataWeave script to generate the content.
-
Content parameters always translate to the DSL as a text element, precisely to enable the embedded DataWeave script.
Consider the file:write
operation again, this time with content parameters. Suppose that you are using this operation in a flow, and the message payload is JSON that you want to store as XML.
<file:write path="myFile.xml">
<file:content>
<![CDATA[#[
%dw 2.0
output application/xml
---
rootElement: payload
]
]]>
</file:content>
</file:write>
In the module’s code, the content
parameter is marked with the @Content
annotation:
public void write(String path, @Content InputStream content) {
// write code
}
Primary Content Parameter
In some cases, an operation has many content parameters, for example:
<http:request path="/my/api">
<http:request-builder>
<http:body>
#[body..]
</http:body>
<http:uri-params>
#[uri-params …]
</http:uri-params>
<http:headers>
#[you get the picture..]
</http:headers>
</http:request-builder>
</http:request>
As you can see, you can have as many content parameters as you want, which means that the @Content
annotation can be used on more than one method argument.
However, the body
parameter is still more important than the rest. Although headers are also part of the content sent in the HTTP request, those headers are complementary to the body that is sent. So, when an operation has more than one content parameter, one of them has to be marked as the primary content by using @Content(primary = true)
.
The primary content parameter has all of the same characteristics as the regular content parameters, plus these:
-
They are automatically made optional.
-
They automatically default to
#[payload]
.
These two features are automatically added to the parameter by the runtime.
Going back to the file:write example in which there was only one content parameter: Is it primary? YES. When an operation has only one content parameter, the SDK will automatically consider it as primary. That means that the content parameter in the file:write operation is automatically made optional and defaults to #[payload] . This helps to enforce consistency across modules.
|
Changing the Default of a Primary Content
There are edge cases in which a primary content should default to something other than the payload. This tends to happen more when the operation has only one content parameter, and that parameter is not always needed. For example, consider the Database connector. The input parameters of a Select query are primary content, but not all queries require input parameters. So the real default should be an empty map.
You can do that by combining the @Content
and @Optional
annotations. For example, the following sets the default to an empty map:
public List<Map> select(@Text String sql,
@Optional(defaultValue="#[{}]") @Content Map<String, Object> inputParameters) {
// select
}
You can also set the content parameter as optional, with no default:
public List<Map> select(@Text String sql,
@Optional @Content Map<String, Object> inputParameters) {
// select
}
Finally, you can combine the @Content
annotation with @NullSafe
, for example:
public List<Map> select(@Text String sql,
@Optional @NullSafe @Content Map<String, Object> inputParameters) {
// select
}
The first and third examples are equivalent. However, the @NullSafe option is preferred because it gives the module’s user a better experience. Having #[{}] as an explicit default might be confusing for inexperienced Mule users.
|
Embedding Content Parameters in Parameter Groups
The content parameters in the http:request
operation above are contained in an element called request-builder
. For usability reasons, the author of the connector chose to group all the request-related attributes into an enclosing object. This is supported
by the SDK in the following manner:
public void request(String path, @ParameterGroup(showInDsl=true) HttpRequestBuilder requestBuilder) {
// request
}
As you can see, there are no content parameters here. However, if you look inside the HttpRequestBuilder
class, you see @Content
annotations:
public class HttpRequestBuilder {
@Parameter
@Content(primary = true)
private InputStream body;
@Parameter
@Content
@NullSafe
private Map<String, String> uriParams;
@Parameter
@Content
@NullSafe
private Map<String, String> uriParams;
}
Removing the @ParameterGroup annotation from the HttpRequestBuilder argument in the sample request operation will result in a compilation error. @Content is not allowed in complex types.
|