INT-2480: Add aggregate headers strategy
JIRA: https://jira.spring.io/browse/INT-2480 * Introduce `headers-function` option into the `aggregator` for merging and computing headers for the output message based on the completed group * Implement a `DefaultAggregateHeadersFunction` and use it in the `AbstractAggregatingMessageGroupProcessor` for default behavior with possible injection for any other implementation * Add `DelegatingMessageGroupProcessor` to wrap any other `MessageGroupProcessor` implementations with possible usage of the `headersFunction` if result is not a `Message` or `MessageBuilder` * Make `AbstractCorrelatingMessageHandler.getOutputProcessor()` as `public` rto give access to this option from the `AggregatorSpec` to be able to inject a `headersFunction` in Java DSL configuration * Add `AbstractIntegrationMessageBuilder.getHeader()` to get access to some underlying header avoiding extra `Map` in case of `getHeaders()` * Change a logic in the `AbstractMessageProducingHandler.produceOutput()` to consult a `reply` for the `replyChannel` as well `routingSlip` header if the `reply` is a `Message` or `MessageBuilder` * Introduce a `AbstractMessageProducingHandler.messageBuilderForReply()` and use it in `AbstractMessageSplitter` to avoid duplication * Validate a new functionality in tests * Fix `FileOutboundGatewayParserTests` to rely on the `TemporaryFolder` to clean up test files after using * JavaDocs for `DefaultAggregateHeadersFunction` * Some `router.adoc` polishing * Fix link to Reactor in the `router.adoc` * Add docs for new `Function<MessageGroup, Map<String, Object>>` strategy * Doc polishing.
This commit is contained in:
committed by
Gary Russell
parent
09c4f03d7c
commit
5a1846cfe5
@@ -67,6 +67,17 @@ public abstract class AbstractAggregatingMessageGroupProcessor
|
||||
----
|
||||
====
|
||||
|
||||
See `DefaultAggregatingMessageGroupProcessor`, `ExpressionEvaluatingMessageGroupProcessor` and `MethodInvokingMessageGroupProcessor` as out-of-the-box implementations of the `AbstractAggregatingMessageGroupProcessor`.
|
||||
|
||||
Starting with version 5.2, a `Function<MessageGroup, Map<String, Object>>` strategy is available for the `AbstractAggregatingMessageGroupProcessor` to merge and compute (aggregate) headers for an output message.
|
||||
The `DefaultAggregateHeadersFunction` implementation is available with logic that returns all headers that have no conflicts among the group; an absent header on one or more messages within the group is not considered a conflict.
|
||||
Conflicting headers are omitted.
|
||||
Along with the newly introduced `DelegatingMessageGroupProcessor`, this function is used for any arbitrary (non-`AbstractAggregatingMessageGroupProcessor`) `MessageGroupProcessor` implementation.
|
||||
Essentially, the framework injects a provided function into an `AbstractAggregatingMessageGroupProcessor` instance and wraps all other implementations into a `DelegatingMessageGroupProcessor`.
|
||||
The difference in logic between the `AbstractAggregatingMessageGroupProcessor` and the `DelegatingMessageGroupProcessor` that the latter doesn't compute headers in advance, before calling the delegate strategy, and doesn't invoke the function if the delegate returns a `Message` or `AbstractIntegrationMessageBuilder`.
|
||||
In that case, the framework assumes that the target implementation has taken care of producing a proper set of headers populated into the returned result.
|
||||
The `Function<MessageGroup, Map<String, Object>>` strategy is available as the `headers-function` reference attribute for XML configuration, as the `AggregatorSpec.headersFunction()` option for the Java DSL and as `AggregatorFactoryBean.setHeadersFunction()` for plain Java configuration.
|
||||
|
||||
The `CorrelationStrategy` is owned by the `AbstractCorrelatingMessageHandler` and has a default value based on the `IntegrationMessageHeaderAccessor.CORRELATION_ID` message header, as the following example shows:
|
||||
|
||||
====
|
||||
|
||||
@@ -1218,8 +1218,7 @@ NOTE: The `requestMessage` argument is always a `Message<?>`.
|
||||
Depending on context, the reply object may be a `Message<?>`, an `AbstractIntegrationMessageBuilder`, or an arbitrary application domain object (when, for example, it is returned by a POJO method invoked by a service activator).
|
||||
In the first two cases, the usual `Message` properties (`payload` and `headers`) are available when using SpEL (or a Java implementation).
|
||||
For an arbitrary domain object, these properties are not available.
|
||||
For this reason, be careful when you use routing slips in conjunction with POJO methods if the result is used to determine the
|
||||
next path.
|
||||
For this reason, be careful when you use routing slips in conjunction with POJO methods if the result is used to determine the next path.
|
||||
|
||||
IMPORTANT: If a routing slip is involved in a distributed environment, we recommend not using inline expressions for the Routing Slip `path`.
|
||||
This recommendation applies to distributed environments such as cross-JVM applications, using a `request-reply` through a message broker (such as<<./amqp.adoc#amqp,AMQP Support>> or <<./jms.adoc#jms,JMS Support>>), or using a persistent `MessageStore` (<<./message-store.adoc#message-store,Message Store>>) in the integration flow.
|
||||
@@ -1252,7 +1251,7 @@ The routing slip algorithm works as follows when an endpoint produces a reply an
|
||||
* If a returned bean is an instance of `MessageChannel`, it is used as the next `outputChannel` and the `routingSlipIndex` is incremented in the reply message header (the routing slip `path` entries remain unchanged).
|
||||
* If a returned bean is an instance of `RoutingSlipRouteStrategy` and its `getNextPath` does not return an empty `String`, that result is used as a bean name for the next `outputChannel`.
|
||||
The `routingSlipIndex` remains unchanged.
|
||||
* If `RoutingSlipRouteStrategy.getNextPath` returns an empty `String`, the `routingSlipIndex` is incremented and the `getOutputChannelFromRoutingSlip` is invoked recursively for the next Routing Slip `path` item.
|
||||
* If `RoutingSlipRouteStrategy.getNextPath` returns an empty `String` or `null`, the `routingSlipIndex` is incremented and the `getOutputChannelFromRoutingSlip` is invoked recursively for the next Routing Slip `path` item.
|
||||
* If the next routing slip `path` entry is not a `String`, it must be an instance of `RoutingSlipRouteStrategy`.
|
||||
* When the `routingSlipIndex` exceeds the size of the routing slip `path` list, the algorithm moves to the default behavior for the standard `replyChannel` header.
|
||||
|
||||
@@ -1265,7 +1264,7 @@ In addition to a bean name, the `RoutingSlipRouteStrategy` can return any `Messa
|
||||
This way, we can provide powerful dynamic routing logic when there is no way to predict which channel should be used.
|
||||
A `MessageChannel` can be created within the `RoutingSlipRouteStrategy` and returned.
|
||||
A `FixedSubscriberChannel` with an associated `MessageHandler` implementation is a good combination for such cases.
|
||||
For example, you can route to a https://github.com/reactor/reactor/wiki/Streams[reactor stream], as the following example shows:
|
||||
For example, you can route to a https://projectreactor.io/docs/core/release/reference/#getting-started[Reactive Streams], as the following example shows:
|
||||
|
||||
====
|
||||
[source,java]
|
||||
|
||||
@@ -56,6 +56,9 @@ See <<./splitter.adoc#splitter,Splitter>> for more information.
|
||||
The Control Bus can now handle `Pausable` (extension of `Lifecycle`) operations.
|
||||
See <<./control-bus.adoc#control-bus,Control Bus>> for more information.
|
||||
|
||||
The `Function<MessageGroup, Map<String, Object>>` strategy has been introduced for the aggregator component to merge and compute headers for output messages.
|
||||
See <<./aggregator.adoc#aggregator-api,Aggregator Programming Model>> for more information.
|
||||
|
||||
[[x5.2-amqp]]
|
||||
==== AMQP Changes
|
||||
|
||||
|
||||
Reference in New Issue
Block a user