INT-4269: Add Stream & Flux Support for Splitter

JIRA: https://jira.spring.io/browse/INT-4269

* Check if `outputChannel` is `ReactiveStreamsSubscribableChannel`
to let back-pressure splitting
* Build `Flux` or `Iterator` depending in the `reactive` state
* Allow to get a size of the `iterator` if it is possible,
for example `XPathMessageSplitter`

Add tests

Fix raw type and unused import

* Add JavaDocs to the `AbstractMessageSplitter#obtainSizeIfPossible()`
* Add asserts for the `sequenceSize` populataiton in the `XPathMessageSplitter`
* Document `Stream` & `Flux` support in the splitter

Minor doc polishing
This commit is contained in:
Artem Bilan
2017-05-25 14:21:40 -04:00
committed by Gary Russell
parent a800d9683a
commit f112ecbb7e
8 changed files with 371 additions and 70 deletions

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2013 the original author or authors.
* Copyright 2002-2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -16,29 +16,40 @@
package org.springframework.integration.splitter;
import static org.junit.Assert.assertEquals;
import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.nullValue;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.stream.Stream;
import org.junit.Test;
import org.springframework.messaging.Message;
import org.reactivestreams.Subscriber;
import org.springframework.integration.IntegrationMessageHeaderAccessor;
import org.springframework.integration.channel.DirectChannel;
import org.springframework.integration.channel.FluxMessageChannel;
import org.springframework.integration.channel.QueueChannel;
import org.springframework.integration.endpoint.EventDrivenConsumer;
import org.springframework.integration.support.MessageBuilder;
import org.springframework.integration.test.util.TestUtils;
import org.springframework.messaging.Message;
import org.springframework.messaging.support.GenericMessage;
import reactor.core.publisher.Flux;
import reactor.test.StepVerifier;
/**
* @author Mark Fisher
* @author Iwein Fuld
* @author Gunnar Hillert
* @author Artem Bilan
*/
public class DefaultSplitterTests {
@@ -65,7 +76,7 @@ public class DefaultSplitterTests {
@Test
public void splitMessageWithCollectionPayload() throws Exception {
List<String> payload = Arrays.asList(new String[] { "x", "y", "z" });
List<String> payload = Arrays.asList("x", "y", "z");
Message<List<String>> message = MessageBuilder.withPayload(payload).build();
QueueChannel replyChannel = new QueueChannel();
DefaultMessageSplitter splitter = new DefaultMessageSplitter();
@@ -108,4 +119,112 @@ public class DefaultSplitterTests {
Message<?> output = replyChannel.receive(15);
assertThat(output, is(nullValue()));
}
@Test
public void splitStream() {
Message<?> message = new GenericMessage<>(
Stream.generate(Math::random)
.limit(10));
QueueChannel outputChannel = new QueueChannel();
DefaultMessageSplitter splitter = new DefaultMessageSplitter();
splitter.setOutputChannel(outputChannel);
splitter.handleMessage(message);
for (int i = 0; i < 10; i++) {
Message<?> reply = outputChannel.receive(0);
assertEquals(message.getHeaders().getId(), new IntegrationMessageHeaderAccessor(reply).getCorrelationId());
}
assertNull(outputChannel.receive(10));
}
@Test
public void splitFlux() {
Message<?> message = new GenericMessage<>(
Flux
.generate(() -> 0,
(state, sink) -> {
if (state == 10) {
sink.complete();
}
else {
sink.next(state);
}
return ++state;
}));
QueueChannel outputChannel = new QueueChannel();
DefaultMessageSplitter splitter = new DefaultMessageSplitter();
splitter.setOutputChannel(outputChannel);
splitter.handleMessage(message);
for (int i = 0; i < 10; i++) {
Message<?> reply = outputChannel.receive(0);
assertEquals(message.getHeaders().getId(), new IntegrationMessageHeaderAccessor(reply).getCorrelationId());
}
assertNull(outputChannel.receive(10));
}
@Test
public void splitArrayPayloadReactive() {
Message<?> message = new GenericMessage<>(new String[] { "x", "y", "z" });
FluxMessageChannel replyChannel = new FluxMessageChannel();
DefaultMessageSplitter splitter = new DefaultMessageSplitter();
splitter.setOutputChannel(replyChannel);
splitter.handleMessage(message);
Flux<String> testFlux =
Flux.from(replyChannel)
.map(Message::getPayload)
.cast(String.class);
StepVerifier.create(testFlux)
.expectNext("x", "y", "z")
.then(() ->
((Subscriber<?>) TestUtils.getPropertyValue(replyChannel, "subscribers", List.class).get(0))
.onComplete())
.verifyComplete();
}
@Test
public void splitStreamReactive() {
Message<?> message = new GenericMessage<>(Stream.of("x", "y", "z"));
FluxMessageChannel replyChannel = new FluxMessageChannel();
DefaultMessageSplitter splitter = new DefaultMessageSplitter();
splitter.setOutputChannel(replyChannel);
splitter.handleMessage(message);
Flux<String> testFlux =
Flux.from(replyChannel)
.map(Message::getPayload)
.cast(String.class);
StepVerifier.create(testFlux)
.expectNext("x", "y", "z")
.then(() ->
((Subscriber<?>) TestUtils.getPropertyValue(replyChannel, "subscribers", List.class).get(0))
.onComplete())
.verifyComplete();
}
@Test
public void splitFluxReactive() {
Message<?> message = new GenericMessage<>(Flux.just("x", "y", "z"));
FluxMessageChannel replyChannel = new FluxMessageChannel();
DefaultMessageSplitter splitter = new DefaultMessageSplitter();
splitter.setOutputChannel(replyChannel);
splitter.handleMessage(message);
Flux<String> testFlux =
Flux.from(replyChannel)
.map(Message::getPayload)
.cast(String.class);
StepVerifier.create(testFlux)
.expectNext("x", "y", "z")
.then(() ->
((Subscriber<?>) TestUtils.getPropertyValue(replyChannel, "subscribers", List.class).get(0))
.onComplete())
.verifyComplete();
}
}