/* * 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.config.annotation; import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.instanceOf; import static org.junit.Assert.assertFalse; 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 static org.junit.Assert.fail; import java.util.ArrayList; import java.util.List; import java.util.concurrent.atomic.AtomicInteger; import javax.annotation.Resource; import org.hamcrest.Matchers; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.BeanCreationException; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.beans.factory.support.BeanDefinitionValidationException; import org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Profile; import org.springframework.expression.ExpressionParser; import org.springframework.expression.spel.standard.SpelExpressionParser; import org.springframework.integration.MessageRejectedException; import org.springframework.integration.aggregator.AggregatingMessageHandler; import org.springframework.integration.aggregator.ExpressionEvaluatingCorrelationStrategy; import org.springframework.integration.aggregator.ExpressionEvaluatingReleaseStrategy; import org.springframework.integration.aggregator.SimpleMessageGroupProcessor; import org.springframework.integration.annotation.BridgeFrom; import org.springframework.integration.annotation.BridgeTo; import org.springframework.integration.annotation.Filter; import org.springframework.integration.annotation.InboundChannelAdapter; import org.springframework.integration.annotation.Poller; import org.springframework.integration.annotation.Router; import org.springframework.integration.annotation.ServiceActivator; import org.springframework.integration.annotation.Splitter; import org.springframework.integration.annotation.Transformer; import org.springframework.integration.channel.DirectChannel; import org.springframework.integration.channel.QueueChannel; import org.springframework.integration.config.EnableIntegration; import org.springframework.integration.config.EnableMessageHistory; import org.springframework.integration.core.MessageSelector; import org.springframework.integration.core.MessageSource; import org.springframework.integration.endpoint.EventDrivenConsumer; import org.springframework.integration.endpoint.SourcePollingChannelAdapter; import org.springframework.integration.filter.ExpressionEvaluatingSelector; import org.springframework.integration.history.MessageHistory; import org.springframework.integration.splitter.DefaultMessageSplitter; import org.springframework.integration.transformer.ExpressionEvaluatingTransformer; import org.springframework.messaging.Message; import org.springframework.messaging.MessageChannel; import org.springframework.messaging.MessageHandler; import org.springframework.messaging.PollableChannel; import org.springframework.messaging.support.ErrorMessage; 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; /** * @author Artem Bilan * @author Gary Russell * @since 4.0 */ @ContextConfiguration(classes = MessagingAnnotationsWithBeanAnnotationTests.ContextConfiguration.class) @RunWith(SpringJUnit4ClassRunner.class) @DirtiesContext public class MessagingAnnotationsWithBeanAnnotationTests { @Autowired private SourcePollingChannelAdapter sourcePollingChannelAdapter; @Autowired private PollableChannel discardChannel; @Resource(name = "collector") private List<Message<?>> collector; @Autowired(required = false) @Qualifier("messagingAnnotationsWithBeanAnnotationTests.ContextConfiguration.skippedMessageHandler.serviceActivator") private EventDrivenConsumer skippedServiceActivator; @Autowired(required = false) @Qualifier("skippedMessageHandler") private MessageHandler skippedMessageHandler; @Autowired(required = false) @Qualifier("skippedChannel") private MessageChannel skippedChannel; @Autowired(required = false) @Qualifier("skippedChannel2") private MessageChannel skippedChannel2; @Autowired(required = false) @Qualifier("skippedMessageSource") private MessageSource<?> skippedMessageSource; @Autowired private PollableChannel counterErrorChannel; @Test public void testMessagingAnnotationsFlow() { this.sourcePollingChannelAdapter.start(); for (int i = 0; i < 10; i++) { Message<?> receive = this.discardChannel.receive(10000); assertNotNull(receive); assertTrue(((Integer) receive.getPayload()) % 2 == 0); receive = this.counterErrorChannel.receive(10000); assertNotNull(receive); assertThat(receive, instanceOf(ErrorMessage.class)); assertThat(receive.getPayload(), instanceOf(MessageRejectedException.class)); MessageRejectedException exception = (MessageRejectedException) receive.getPayload(); assertThat(exception.getMessage(), containsString("MessageFilter " + "'messagingAnnotationsWithBeanAnnotationTests.ContextConfiguration.filter.filter.handler'" + " rejected Message")); } for (Message<?> message : collector) { assertFalse(((Integer) message.getPayload()) % 2 == 0); MessageHistory messageHistory = MessageHistory.read(message); assertNotNull(messageHistory); String messageHistoryString = messageHistory.toString(); assertThat(messageHistoryString, Matchers.containsString("routerChannel")); assertThat(messageHistoryString, Matchers.containsString("filterChannel")); assertThat(messageHistoryString, Matchers.containsString("aggregatorChannel")); assertThat(messageHistoryString, Matchers.containsString("splitterChannel")); assertThat(messageHistoryString, Matchers.containsString("serviceChannel")); assertThat(messageHistoryString, Matchers.not(Matchers.containsString("discardChannel"))); } assertNull(this.skippedServiceActivator); assertNull(this.skippedMessageHandler); assertNull(this.skippedChannel); assertNull(this.skippedChannel2); assertNull(this.skippedMessageSource); } @Test public void testInvalidMessagingAnnotationsConfig() { try { new AnnotationConfigApplicationContext(InvalidContextConfiguration.class).close(); fail("BeanCreationException expected"); } catch (Exception e) { assertThat(e, instanceOf(BeanCreationException.class)); assertThat(e.getCause(), instanceOf(BeanDefinitionValidationException.class)); assertThat(e.getMessage(), containsString("The attribute causing the ambiguity is: [applySequence].")); } } @Configuration @EnableIntegration @EnableMessageHistory public static class ContextConfiguration { private static final ExpressionParser PARSER = new SpelExpressionParser(); @Bean public AtomicInteger counter() { return new AtomicInteger(); } @Bean @InboundChannelAdapter(value = "routerChannel", autoStartup = "false", poller = @Poller(fixedRate = "10", maxMessagesPerPoll = "1", errorChannel = "counterErrorChannel")) public MessageSource<Integer> counterMessageSource(final AtomicInteger counter) { return () -> new GenericMessage<>(counter.incrementAndGet()); } @Bean public PollableChannel counterErrorChannel() { return new QueueChannel(); } @Bean public MessageChannel routerChannel() { return new DirectChannel(); } @Bean @Router(inputChannel = "routerChannel", channelMappings = { "true=odd", "false=filter" }, suffix = "Channel") public MessageSelector router() { return new ExpressionEvaluatingSelector("payload % 2 == 0"); } @Bean @Transformer(inputChannel = "oddChannel", outputChannel = "filterChannel") public ExpressionEvaluatingTransformer oddTransformer() { return new ExpressionEvaluatingTransformer(PARSER.parseExpression("payload / 2")); } @Bean public MessageChannel filterChannel() { return new DirectChannel(); } @Bean @Filter(inputChannel = "filterChannel", outputChannel = "aggregatorChannel", discardChannel = "discardChannel", throwExceptionOnRejection = "true") public MessageSelector filter() { return new ExpressionEvaluatingSelector("payload % 2 != 0"); } @Bean public MessageChannel aggregatorChannel() { return new DirectChannel(); } @Bean @ServiceActivator(inputChannel = "aggregatorChannel") public MessageHandler aggregator() { AggregatingMessageHandler handler = new AggregatingMessageHandler(new SimpleMessageGroupProcessor()); handler.setCorrelationStrategy(new ExpressionEvaluatingCorrelationStrategy("1")); handler.setReleaseStrategy(new ExpressionEvaluatingReleaseStrategy("size() == 10")); handler.setOutputChannelName("splitterChannel"); return handler; } @Bean public MessageChannel splitterChannel() { return new DirectChannel(); } @Bean @Splitter(inputChannel = "splitterChannel") public MessageHandler splitter() { DefaultMessageSplitter defaultMessageSplitter = new DefaultMessageSplitter(); defaultMessageSplitter.setOutputChannelName("serviceChannel"); return defaultMessageSplitter; } @Bean public PollableChannel discardChannel() { return new QueueChannel(); } @Bean public List<Message<?>> collector() { return new ArrayList<Message<?>>(); } @Bean public MessageChannel serviceChannel() { return new DirectChannel(); } @Bean @ServiceActivator(inputChannel = "serviceChannel") public MessageHandler service() { final List<Message<?>> collector = this.collector(); return message -> collector.add(message); } @Bean @ServiceActivator(inputChannel = "skippedChannel") @Splitter(inputChannel = "skippedChannel2") @Router(inputChannel = "skippedChannel3") @Transformer(inputChannel = "skippedChannel4") @Filter(inputChannel = "skippedChannel5") @Profile("foo") public MessageHandler skippedMessageHandler() { return m -> { }; } @Bean @BridgeFrom("skippedChannel6") @Profile("foo") public MessageChannel skippedChannel1() { return new DirectChannel(); } @Bean @BridgeTo @Profile("foo") public MessageChannel skippedChannel2() { return new DirectChannel(); } @Bean @InboundChannelAdapter("serviceChannel") @Profile("foo") public MessageSource<?> skippedMessageSource() { return () -> new GenericMessage<>("foo"); } } @Configuration @EnableIntegration static class InvalidContextConfiguration { @Bean @Splitter(inputChannel = "splitterChannel", applySequence = "false") public MessageHandler splitter() { return new DefaultMessageSplitter(); } } }