public class ChatOperations { public void sendChatMessage(@Connection ChatConnection chatConnection, @FieldValues(value = WorkspacesValueProvider.class, targetSelectors = "routingInfo.workspace") @TypeResolver(ChatMessageResolver.class) @Content InputStream body) { // Do message sending } }
Field-Level Capabilities
Java SDK includes several features that support field-level capabilities for value providers and sample data. Using these features enables you to define value providers for parameter fields, or to use parameter fields as acting parameters for either a value provider or sample data.
These features are compatible with Mule 4.4.0, however, all Anypoint Connectors can use these features, regardless of their Mule version.
Defining a Value Provider for One Part of a Parameter
Define a value provider for a parameter field by using the
annotation @FieldValues
.
The @FieldValues
annotation requires:
-
value
Provides values for a field.
-
targetSelectors
Describes which part of a parameter contains the values supplied by the value provider. The DataWeave syntax defines this part of the annotation to describe the selector through nested fields, XML attributes, or fields that contain spaces in their names.
The following example shows an operation that includes values provided for
a parameter named body
, which is a JSON input stream:
The body
parameter has a field named routingInfo, which has a field named
workspace
. WorkspacesValueProvider is a value provider that uses the
connection and retrieves possible workspaces.
Defining a Value Provider for Several Parts of a Parameter
Parameters can have more than one part where values are provided.
The @FieldValues
annotation is a repeatable annotation, so parameters can be
annotated multiple times.
The following example shows an operation with a parameter named
body
, which is a JSON input stream that has two parts with value providers:
public class ChatOperations { public void sendChatMessage(@Connection ChatConnection chatConnection, @FieldValues(value = WorkspacesValueProvider.class, targetSelectors = "routingInfo.workspace") @FieldValues(value = ChannelIdValueProvider.class, targetSelectors = "channelId") @TypeResolver(ChatMessageResolver.class) @Content InputStream body) { // Do message sending } }
The parameter body
defines values for two fields (routingInfo.workspace and
channelId) that have values that are independent of each other.
Defining a Multilevel Value Provider for Several Parts of a Parameter
Use multilevel value providers for several parts of a parameter.
For example, imagine that you want the values for the channelId field to depend on the values for the routingInfo.workspace field.
To define this, the targetSelectors attribute from the @FieldValues
annotation
always receives an array of strings. If more than one string is given, a multilevel
value provider is defined. Each given field defines the value parts of the
value provider, the order in the array, and the order of the value part.
The following example shows how to define multilevel value providers for parameter parts:
public class ChatOperations { public void sendChatMessage(@Connection ChatConnection chatConnection, @FieldValues(value = ChatMultiLevelValueProvider.class, targetSelectors = {"workspace", "channelId"}) @TypeResolver(ChatMessageResolver.class) @Content InputStream body) {} }
The multilevel value provider is included like any other value provider.
The following is an example of what a multilevel value provider looks like:
public class ChatMultiLevelValueProvider implements ValueProvider { @Connection private ChatConnection chatConnection; @Override public Set<Value> resolve() { Set<Value> values = new HashSet<>(); List<String> workspaces = chatConnection.getWorkspaces(); workspaces.stream().forEach(workspace -> { ValueBuilder valueBuilder = newValue(workspace); chatConnection.getChannels(workspace).stream().forEach(channel -> valueBuilder.withChild(newValue(channel))); values.add(valueBuilder.build()); }); return values; } @Override public String getId() { return "Chat multilevel value provider"; } }
Defining a Value Provider for XML Attributes
Target selectors use the DataWeave syntax. If a parameter of an operation is an XML element, use this syntax to define a value provider for an attribute that is a part of that parameter.
The following example shows how the DataWeave syntax defines a value provider for an attribute:
public class ChatOperations { public void sendChatMessage(@Connection ChatConnection chatConnection, @FieldValues(value = ChannelIdValueProvider.class, targetSelectors = "message.target.@channelId") @TypeResolver(XmlChatMessageResolver.class) @Content InputStream body) {} }
In this example, the body
parameter is an XML element that has a root tag named
message, which has a target that has the channelId attribute and an
associated value provider.
Parameters Static Metadata
When resolving acting parameter values, always know the media types of the parameters from the component that executes the extraction expression and that gives the acting parameter its value.
Use the static metadata of the parameter to retrieve the media type of the parameter. For example, if the parameter is a JSON input stream but the static metadata does not reflect this, the parts of the parameter cannot be used as acting parameters.
Define Acting Parameters as Parts of Parameters
To define acting parameters as parts of parameters, components must define bindings for their references for value providers and sample data providers. These bindings enable an acting parameter of the provider to take the value of the evaluated DataWeave expression.
When no binding is defined for an acting parameter, the same implicit binding is applied. The name of the acting parameter must be the same as the name of the parameter in the component to take that parameter’s value.
Use the @Binding
annotation to define an acting parameter. The @FieldValues
annotation and the @SampleData
annotation takes an array of bindings
to define the values the acting parameter takes.
A defined binding declares the value that an acting parameter takes by providing an extraction expression. This extraction expression is a DataWeave expression that uses only selectors to retrieve the value for the acting parameter.
The extraction expression used for the extraction of the value of the acting parameter has some limitations:
-
It does not aggregate any data from the context. It is limited to access only the value of a field in the parameter.
-
The first part of the extraction expression corresponds only to the name of a parameter from the component.
-
The output media type is always
application/java
when executing the extraction expression to give values to the acting parameters of a value provider or sample data provider. This means that the provider does not expect other media types, such asapplication/json
orapplication/xml
.
The available context for the expression has the value of all the component’s parameters. The expression must start with the name of the parameter of the component. If the value comes from a part of the parameter, the expression follows the selector to the desired field.
The following example shows a parameter named body
that has a field with
values that depend on the acting parameter workspace
:
public class ChatOperations { public void sendChatMessage(@Connection ChatConnection chatConnection, @FieldValues(value = ChannelsValueProvider.class, targetSelectors = "channelId", bindings = {@Binding(actingParameter = "workspace", extractionExpression = "body.routingInfo.workspace")})) @TypeResolver(ChatMessageResolver.class) @Content InputStream body) { // Do message sending } }
This acting parameter is taken from the same body
parameter.
The following syntax shows the value provider used in this example:
public class ChannelsValueProvider implements ValueProvider { @Connection private ChatConnection chatConnection; @Parameter private String workspace; @Override public Set<Value> resolve() { return ValueBuilder.getValuesFor(chatConnection.getChannels(workspace)); } @Override public String getId() { return "Channels value provider"; } }
The workspace
acting parameter defined in the value provider is bound to the
extraction expression that takes the value of the field from the context
of the component.
Sample Data Example
The following example illustrates a @SampleData
annotation that binds an acting
parameter to the value of a field of a parameter in the component:
public class ChatOperations { @SampleData(value = ChatMessageSampleDatatProvider.class, bindings = {@Binding(actingParameter = "workspace", extractionExpression = "body.routingInfo.workspace")})) public Result<InputStream, ChatAttributes> retrieveChatMessage(@Connection ChatConnection chatConnection, @TypeResolver(ChatMessageResolver.class) @Content InputStream body) { // Do chat message retrieval } }
ChatMessageSampleDatatProvider contains a parameter named workspace
that provides the sample data. The value of this acting parameter
is taken from the extraction expression defined in the binding.