/* * Copyright 2014-2016 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.routingslip; import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.instanceOf; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertThat; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import java.util.Arrays; import java.util.Collections; import java.util.Date; import java.util.List; import java.util.Map; import java.util.Properties; import java.util.concurrent.atomic.AtomicBoolean; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.integration.IntegrationMessageHeaderAccessor; import org.springframework.integration.annotation.BridgeTo; import org.springframework.integration.annotation.Transformer; import org.springframework.integration.channel.DirectChannel; import org.springframework.integration.channel.FixedSubscriberChannel; import org.springframework.integration.channel.QueueChannel; import org.springframework.integration.config.EnableIntegration; import org.springframework.integration.core.MessagingTemplate; import org.springframework.integration.history.MessageHistory; import org.springframework.integration.support.MessageBuilder; import org.springframework.integration.transformer.HeaderEnricher; import org.springframework.integration.transformer.support.RoutingSlipHeaderValueMessageProcessor; import org.springframework.messaging.Message; import org.springframework.messaging.MessageChannel; import org.springframework.messaging.MessagingException; import org.springframework.messaging.PollableChannel; import org.springframework.messaging.support.GenericMessage; import org.springframework.test.annotation.DirtiesContext; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import org.springframework.test.context.support.GenericXmlContextLoader; import reactor.core.publisher.Flux; /** * @author Artem Bilan * @since 4.1 */ @ContextConfiguration(loader = GenericXmlContextLoader.class) @RunWith(SpringJUnit4ClassRunner.class) @DirtiesContext public class RoutingSlipTests { @Autowired private MessageChannel input; @Autowired private MessageChannel routingSlipHeaderChannel; @Autowired private PollableChannel resultsChannel; @Autowired private MessageChannel invalidRoutingSlipChannel; @Test @SuppressWarnings("unchecked") public void testRoutingSlip() { PollableChannel replyChannel = new QueueChannel(); Message<List<String>> request = MessageBuilder.withPayload(Arrays.asList("test1", "test2")) .setReplyChannel(replyChannel) .setHeader("myRoutingSlipChannel", "channel5").build(); this.input.send(request); Message<?> reply = replyChannel.receive(10000); assertNotNull(reply); List<Message<?>> messages = (List<Message<?>>) reply.getPayload(); for (Message<?> message : messages) { Map<List<String>, Integer> routingSlip = message.getHeaders() .get(IntegrationMessageHeaderAccessor.ROUTING_SLIP, Map.class); assertEquals(routingSlip.keySet().iterator().next().size(), routingSlip.values().iterator().next().intValue()); MessageHistory messageHistory = MessageHistory.read(message); List<String> channelNames = Arrays.asList("input", "split", "process", "channel1", "channel2", "channel3", "channel4", "channel5", "aggregate"); for (Properties properties : messageHistory) { assertTrue(channelNames.contains(properties.getProperty("name"))); } } } @Test public void testDynamicRoutingSlipRoutStrategy() { this.routingSlipHeaderChannel.send(new GenericMessage<>("foo")); Message<?> result = this.resultsChannel.receive(10000); assertNotNull(result); assertEquals("FOO", result.getPayload()); this.routingSlipHeaderChannel.send(new GenericMessage<>(2)); result = this.resultsChannel.receive(10000); assertNotNull(result); assertEquals(4, result.getPayload()); } @Test public void testInvalidRoutingSlipRoutStrategy() { try { new RoutingSlipHeaderValueMessageProcessor(new Date()); fail("IllegalArgumentException expected"); } catch (Exception e) { assertThat(e, instanceOf(IllegalArgumentException.class)); assertThat(e.getMessage(), containsString("The RoutingSlip can contain " + "only bean names of MessageChannel or RoutingSlipRouteStrategy, " + "or MessageChannel and RoutingSlipRouteStrategy instances")); } try { this.invalidRoutingSlipChannel.send(new GenericMessage<>("foo")); fail("MessagingException expected"); } catch (Exception e) { assertThat(e, instanceOf(MessagingException.class)); assertThat(e.getMessage(), containsString("replyChannel must be a MessageChannel or String")); } } public static class TestRoutingSlipRoutePojo { final String[] channels = { "channel2", "channel3" }; private int i = 0; public String get(Message<?> requestMessage, Object reply) { try { return this.channels[i++]; } catch (Exception e) { return null; } } } public static class TestRoutingSlipRouteStrategy implements RoutingSlipRouteStrategy { private final AtomicBoolean invoked = new AtomicBoolean(); @Override public Object getNextPath(Message<?> requestMessage, Object reply) { return !invoked.getAndSet(true) ? "channel4" : null; } } @Configuration @EnableIntegration public static class RoutingSlipConfiguration { @Bean public MessagingTemplate messagingTemplate() { return new MessagingTemplate(); } @Bean public PollableChannel resultsChannel() { return new QueueChannel(); } @Bean public RoutingSlipRouteStrategy routeStrategy() { return (requestMessage, reply) -> requestMessage.getPayload() instanceof String ? new FixedSubscriberChannel(m -> Flux.just((String) m.getPayload()) .map(String::toUpperCase) .subscribe(v -> messagingTemplate() .convertAndSend(resultsChannel(), v))) : new FixedSubscriberChannel(m -> Flux.just((Integer) m.getPayload()) .map(v -> v * 2) .subscribe(v -> messagingTemplate() .convertAndSend(resultsChannel(), v))); } @Bean public MessageChannel routingSlipHeaderChannel() { return new DirectChannel(); } @Bean @BridgeTo public MessageChannel processChannel() { return new DirectChannel(); } @Bean @Transformer(inputChannel = "routingSlipHeaderChannel", outputChannel = "processChannel") public HeaderEnricher headerEnricher() { return new HeaderEnricher(Collections.singletonMap(IntegrationMessageHeaderAccessor.ROUTING_SLIP, new RoutingSlipHeaderValueMessageProcessor(routeStrategy()))); } @Bean public MessageChannel invalidRoutingSlipChannel() { return new DirectChannel(); } @Bean @Transformer(inputChannel = "invalidRoutingSlipChannel", outputChannel = "processChannel") public HeaderEnricher headerEnricher2() { return new HeaderEnricher(Collections.singletonMap(IntegrationMessageHeaderAccessor.ROUTING_SLIP, new RoutingSlipHeaderValueMessageProcessor((RoutingSlipRouteStrategy) (message, r) -> new Date()))); } } }