/*
* Copyright 2016-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.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.integration.dsl.correlation;
import static org.hamcrest.Matchers.instanceOf;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
import java.util.stream.Collectors;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.DependsOn;
import org.springframework.integration.IntegrationMessageHeaderAccessor;
import org.springframework.integration.aggregator.HeaderAttributeCorrelationStrategy;
import org.springframework.integration.channel.QueueChannel;
import org.springframework.integration.config.EnableIntegration;
import org.springframework.integration.dsl.IntegrationFlow;
import org.springframework.integration.dsl.IntegrationFlows;
import org.springframework.integration.dsl.channel.MessageChannels;
import org.springframework.integration.handler.MessageTriggerAction;
import org.springframework.integration.support.MessageBuilder;
import org.springframework.messaging.Message;
import org.springframework.messaging.MessageChannel;
import org.springframework.messaging.PollableChannel;
import org.springframework.messaging.support.GenericMessage;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.context.junit4.SpringRunner;
/**
* @author Artem Bilan
* @author Gary Russell
*
* @since 5.0
*/
@RunWith(SpringRunner.class)
@DirtiesContext
public class CorrelationHandlerTests {
private static final String BARRIER = "barrier";
@Autowired
@Qualifier("splitResequenceFlow.input")
private MessageChannel splitInput;
@Autowired
@Qualifier("splitAggregateInput")
private MessageChannel splitAggregateInput;
@Autowired
@Qualifier("publishSubscribeFlow.input")
private MessageChannel subscriberAggregateFlowInput;
@Autowired
private PollableChannel subscriberAggregateResult;
@Autowired
@Qualifier("barrierFlow.input")
private MessageChannel barrierFlowInput;
@Autowired
private PollableChannel barrierResults;
@Autowired
private PollableChannel releaseChannel;
@Test
public void testSplitterResequencer() {
QueueChannel replyChannel = new QueueChannel();
this.splitInput.send(MessageBuilder.withPayload("")
.setReplyChannel(replyChannel)
.setHeader("foo", "bar")
.build());
for (int i = 0; i < 12; i++) {
Message<?> receive = replyChannel.receive(2000);
assertNotNull(receive);
assertFalse(receive.getHeaders().containsKey("foo"));
assertTrue(receive.getHeaders().containsKey("FOO"));
assertEquals("BAR", receive.getHeaders().get("FOO"));
assertEquals(i + 1, receive.getPayload());
}
}
@Test
public void testSplitterAggregator() {
List<Character> payload = Arrays.asList('a', 'b', 'c', 'd', 'e');
QueueChannel replyChannel = new QueueChannel();
this.splitAggregateInput.send(MessageBuilder.withPayload(payload)
.setReplyChannel(replyChannel)
.build());
Message<?> receive = replyChannel.receive(2000);
assertNotNull(receive);
assertThat(receive.getPayload(), instanceOf(List.class));
@SuppressWarnings("unchecked")
List<Object> result = (List<Object>) receive.getPayload();
for (int i = 0; i < payload.size(); i++) {
assertEquals(payload.get(i), result.get(i));
}
}
@Test
public void testSubscriberAggregateFlow() {
this.subscriberAggregateFlowInput.send(new GenericMessage<>("test"));
Message<?> receive1 = this.subscriberAggregateResult.receive(10000);
assertNotNull(receive1);
assertEquals("Hello World!", receive1.getPayload());
}
@Test
public void testBarrier() {
Message<?> releasing = MessageBuilder.withPayload("bar").setHeader(BARRIER, "foo").build();
this.releaseChannel.send(releasing);
Message<?> suspending = MessageBuilder.withPayload("foo").setHeader(BARRIER, "foo").build();
this.barrierFlowInput.send(suspending);
Message<?> out = this.barrierResults.receive(10000);
assertNotNull(out);
assertEquals("bar", out.getPayload());
}
@Configuration
@EnableIntegration
public static class ContextConfiguration {
@Bean
public Executor taskExecutor() {
return Executors.newCachedThreadPool();
}
@Bean
public TestSplitterPojo testSplitterData() {
List<String> first = new ArrayList<>();
first.add("1,2,3");
first.add("4,5,6");
List<String> second = new ArrayList<>();
second.add("7,8,9");
second.add("10,11,12");
return new TestSplitterPojo(first, second);
}
@Bean
public IntegrationFlow splitResequenceFlow() {
return f -> f.enrichHeaders(s -> s.header("FOO", "BAR"))
.split("testSplitterData", "buildList", c -> c.applySequence(false))
.channel(MessageChannels.executor(taskExecutor()))
.split(Message.class, Message<?>::getPayload, c -> c.applySequence(false))
.channel(MessageChannels.executor(taskExecutor()))
.split(s -> s
.applySequence(false)
.delimiters(","))
.channel(MessageChannels.executor(taskExecutor()))
.<String, Integer>transform(Integer::parseInt)
.enrichHeaders(h ->
h.headerFunction(IntegrationMessageHeaderAccessor.SEQUENCE_NUMBER, Message::getPayload))
.resequence(r -> r.releasePartialSequences(true).correlationExpression("'foo'"))
.headerFilter("foo", false);
}
@Bean
public IntegrationFlow splitAggregateFlow() {
return IntegrationFlows.from("splitAggregateInput", true)
.split()
.channel(MessageChannels.executor(taskExecutor()))
.resequence()
.aggregate()
.get();
}
@Bean
public IntegrationFlow publishSubscribeFlow() {
return flow -> flow
.publishSubscribeChannel(s -> s
.applySequence(true)
.subscribe(f -> f
.handle((p, h) -> "Hello")
.channel("publishSubscribeAggregateFlow.input"))
.subscribe(f -> f
.handle((p, h) -> "World!")
.channel("publishSubscribeAggregateFlow.input"))
);
}
@Bean
public IntegrationFlow publishSubscribeAggregateFlow() {
return flow -> flow
.aggregate(a -> a.outputProcessor(g -> g.getMessages()
.stream()
.map(m -> (String) m.getPayload())
.collect(Collectors.joining(" "))))
.channel(MessageChannels.queue("subscriberAggregateResult"));
}
@Bean
public IntegrationFlow barrierFlow() {
return f -> f
.barrier(10000, b -> b
.correlationStrategy(new HeaderAttributeCorrelationStrategy(BARRIER))
.outputProcessor(g ->
g.getMessages()
.stream()
.skip(1)
.findFirst()
.get()))
.channel(MessageChannels.queue("barrierResults"));
}
@Bean
@DependsOn("barrierFlow")
public IntegrationFlow releaseBarrierFlow(MessageTriggerAction barrierTriggerAction) {
return IntegrationFlows.from(MessageChannels.queue("releaseChannel"))
.trigger(barrierTriggerAction,
e -> e.poller(p -> p.fixedDelay(100)))
.get();
}
}
private static final class TestSplitterPojo {
final List<String> first;
final List<String> second;
TestSplitterPojo(List<String> first, List<String> second) {
this.first = first;
this.second = second;
}
@SuppressWarnings("unused")
public List<String> getFirst() {
return first;
}
@SuppressWarnings("unused")
public List<String> getSecond() {
return second;
}
@SuppressWarnings("unused")
public List<List<String>> buildList() {
return Arrays.asList(this.first, this.second);
}
}
}