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
@@ -11,7 +11,12 @@
|
||||
<queue capacity="5" />
|
||||
</channel>
|
||||
|
||||
<aggregator ref="summer" method="sum" input-channel="input" output-channel="output" expression="">
|
||||
<beans:bean id="headersFunction"
|
||||
class="org.springframework.integration.aggregator.integration.AggregatorIntegrationTests"
|
||||
factory-method="firstMessageHeaders"/>
|
||||
|
||||
<aggregator ref="summer" method="sum" input-channel="input" output-channel="output" expression=""
|
||||
headers-function="headersFunction">
|
||||
<poller task-executor="executor" max-messages-per-poll="5" fixed-delay="20" />
|
||||
</aggregator>
|
||||
|
||||
|
||||
@@ -24,6 +24,7 @@ import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Queue;
|
||||
import java.util.function.Function;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
@@ -32,6 +33,7 @@ import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.integration.IntegrationMessageHeaderAccessor;
|
||||
import org.springframework.integration.channel.QueueChannel;
|
||||
import org.springframework.integration.store.MessageGroup;
|
||||
import org.springframework.integration.store.MessageGroupStore;
|
||||
import org.springframework.integration.test.util.TestUtils;
|
||||
import org.springframework.messaging.Message;
|
||||
@@ -92,6 +94,7 @@ public class AggregatorIntegrationTests {
|
||||
Message<?> receive = output.receive(10000);
|
||||
assertThat(receive).isNotNull();
|
||||
assertThat(receive.getPayload()).isEqualTo(1 + 2 + 3 + 4);
|
||||
assertThat(receive.getHeaders().get(IntegrationMessageHeaderAccessor.SEQUENCE_NUMBER)).isEqualTo(0);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -245,6 +248,18 @@ public class AggregatorIntegrationTests {
|
||||
}
|
||||
|
||||
// configured in context associated with this test
|
||||
private Map<String, Object> stubHeaders(int sequenceNumber, int sequenceSize, int correlationId) {
|
||||
Map<String, Object> headers = new HashMap<>();
|
||||
headers.put(IntegrationMessageHeaderAccessor.SEQUENCE_NUMBER, sequenceNumber);
|
||||
headers.put(IntegrationMessageHeaderAccessor.SEQUENCE_SIZE, sequenceSize);
|
||||
headers.put(IntegrationMessageHeaderAccessor.CORRELATION_ID, correlationId);
|
||||
return headers;
|
||||
}
|
||||
|
||||
public static Function<MessageGroup, Map<String, Object>> firstMessageHeaders() {
|
||||
return (messageGroup) -> messageGroup.getOne().getHeaders();
|
||||
}
|
||||
|
||||
public static class SummingAggregator {
|
||||
public Integer sum(List<Integer> numbers) {
|
||||
int result = 0;
|
||||
@@ -255,12 +270,6 @@ public class AggregatorIntegrationTests {
|
||||
}
|
||||
}
|
||||
|
||||
private Map<String, Object> stubHeaders(int sequenceNumber, int sequenceSize, int correlationId) {
|
||||
Map<String, Object> headers = new HashMap<>();
|
||||
headers.put(IntegrationMessageHeaderAccessor.SEQUENCE_NUMBER, sequenceNumber);
|
||||
headers.put(IntegrationMessageHeaderAccessor.SEQUENCE_SIZE, sequenceSize);
|
||||
headers.put(IntegrationMessageHeaderAccessor.CORRELATION_ID, correlationId);
|
||||
return headers;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -83,7 +83,7 @@
|
||||
<channel id="fooChannel"/>
|
||||
|
||||
<header-enricher input-channel="routingSlipInput">
|
||||
<routing-slip value="fooChannel; barExpression; bazRoutingSlip"/>
|
||||
<routing-slip value="request.headers.replyChannel; fooChannel; barExpression; bazRoutingSlip"/>
|
||||
</header-enricher>
|
||||
|
||||
<header-enricher input-channel="payloadExpressionInput">
|
||||
|
||||
@@ -270,9 +270,10 @@ public class HeaderEnricherTests {
|
||||
@SuppressWarnings("unchecked")
|
||||
List<Object> routingSlipPath = (List<Object>) ((Map<?, ?>) routingSlip).keySet().iterator().next();
|
||||
|
||||
assertThat(routingSlipPath.get(0)).isEqualTo("fooChannel");
|
||||
assertThat(routingSlipPath.get(1)).isInstanceOf(ExpressionEvaluatingRoutingSlipRouteStrategy.class);
|
||||
assertThat(routingSlipPath.get(2)).isEqualTo("bazRoutingSlip");
|
||||
assertThat(routingSlipPath.get(0)).isInstanceOf(ExpressionEvaluatingRoutingSlipRouteStrategy.class);
|
||||
assertThat(routingSlipPath.get(1)).isEqualTo("fooChannel");
|
||||
assertThat(routingSlipPath.get(2)).isInstanceOf(ExpressionEvaluatingRoutingSlipRouteStrategy.class);
|
||||
assertThat(routingSlipPath.get(3)).isEqualTo("bazRoutingSlip");
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -20,6 +20,7 @@ import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.IntStream;
|
||||
@@ -141,9 +142,10 @@ public class CorrelationHandlerTests {
|
||||
public void testSubscriberAggregateFlow() {
|
||||
this.subscriberAggregateFlowInput.send(new GenericMessage<>("test"));
|
||||
|
||||
Message<?> receive1 = this.subscriberAggregateResult.receive(10000);
|
||||
assertThat(receive1).isNotNull();
|
||||
assertThat(receive1.getPayload()).isEqualTo("Hello World!");
|
||||
Message<?> receive = this.subscriberAggregateResult.receive(10000);
|
||||
assertThat(receive).isNotNull();
|
||||
assertThat(receive.getPayload()).isEqualTo("Hello World!");
|
||||
assertThat(receive.getHeaders().get("foo")).isEqualTo("bar");
|
||||
}
|
||||
|
||||
|
||||
@@ -274,10 +276,13 @@ public class CorrelationHandlerTests {
|
||||
@Bean
|
||||
public IntegrationFlow publishSubscribeAggregateFlow() {
|
||||
return flow -> flow
|
||||
.aggregate(a -> a.outputProcessor(g -> g.getMessages()
|
||||
.stream()
|
||||
.map(m -> (String) m.getPayload())
|
||||
.collect(Collectors.joining(" "))))
|
||||
.aggregate(a -> a
|
||||
.outputProcessor((group) -> group
|
||||
.getMessages()
|
||||
.stream()
|
||||
.map(m -> (String) m.getPayload())
|
||||
.collect(Collectors.joining(" ")))
|
||||
.headersFunction((group) -> Collections.singletonMap("foo", "bar")))
|
||||
.channel(MessageChannels.queue("subscriberAggregateResult"));
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user