/* * 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. * 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.aggregator; import static org.hamcrest.CoreMatchers.is; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertThat; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import static org.mockito.Mockito.when; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Iterator; import java.util.List; import java.util.Map; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.junit.MockitoJUnitRunner; import org.springframework.aop.framework.ProxyFactory; import org.springframework.core.convert.converter.Converter; import org.springframework.core.convert.support.DefaultConversionService; import org.springframework.core.convert.support.GenericConversionService; import org.springframework.integration.annotation.Aggregator; import org.springframework.integration.annotation.Payloads; import org.springframework.integration.channel.DirectChannel; import org.springframework.integration.channel.QueueChannel; import org.springframework.integration.endpoint.EventDrivenConsumer; import org.springframework.integration.store.MessageGroup; import org.springframework.integration.store.SimpleMessageGroup; import org.springframework.integration.support.MessageBuilder; import org.springframework.messaging.Message; import org.springframework.messaging.handler.annotation.Header; import org.springframework.messaging.handler.annotation.Headers; import org.springframework.messaging.support.GenericMessage; /** * @author Iwein Fuld * @author Dave Syer * @author Mark Fisher * @author Oleg Zhurakousky * @author Gunnar Hillert * @author Gary Russell * @author Artem Bilan */ @RunWith(MockitoJUnitRunner.class) public class MethodInvokingMessageGroupProcessorTests { private final List<Message<?>> messagesUpForProcessing = new ArrayList<Message<?>>(3); @Mock private MessageGroup messageGroupMock; @Before public void initializeMessagesUpForProcessing() { messagesUpForProcessing.add(MessageBuilder.withPayload(1).build()); messagesUpForProcessing.add(MessageBuilder.withPayload(2).build()); messagesUpForProcessing.add(MessageBuilder.withPayload(4).build()); } @Test public void shouldFindAnnotatedAggregatorMethod() throws Exception { @SuppressWarnings("unused") class AnnotatedAggregatorMethod { @Aggregator public Integer and(List<Integer> flags) { int result = 0; for (Integer flag : flags) { result = result | flag; } return result; } public String know(List<Integer> flags) { return "I'm not the one "; } } MessageGroupProcessor processor = new MethodInvokingMessageGroupProcessor(new AnnotatedAggregatorMethod()); when(messageGroupMock.getMessages()).thenReturn(messagesUpForProcessing); Object result = processor.processMessageGroup(messageGroupMock); assertThat(((Message<?>) result).getPayload(), is(7)); } @Test public void shouldFindSimpleAggregatorMethod() throws Exception { @SuppressWarnings("unused") class SimpleAggregator { public Integer and(List<Integer> flags) { int result = 0; for (Integer flag : flags) { result = result | flag; } return result; } } MessageGroupProcessor processor = new MethodInvokingMessageGroupProcessor(new SimpleAggregator()); when(messageGroupMock.getMessages()).thenReturn(messagesUpForProcessing); Object result = processor.processMessageGroup(messageGroupMock); assertThat(((Message<?>) result).getPayload(), is(7)); } @Test public void shouldFindSimpleAggregatorMethodForMessages() throws Exception { @SuppressWarnings("unused") class SimpleAggregator { public Integer and(List<Message<Integer>> flags) { int result = 0; for (Message<Integer> flag : flags) { result = result | flag.getPayload(); } return result; } } MessageGroupProcessor processor = new MethodInvokingMessageGroupProcessor(new SimpleAggregator()); when(messageGroupMock.getMessages()).thenReturn(messagesUpForProcessing); Object result = processor.processMessageGroup(messageGroupMock); assertThat(((Message<?>) result).getPayload(), is(7)); } @Test public void shouldFindListPayloads() throws Exception { @SuppressWarnings("unused") class SimpleAggregator { public String and(List<Integer> flags, @Header("foo") List<Integer> header) { List<Integer> result = new ArrayList<Integer>(); for (int flag : flags) { result.add(flag); } for (int flag : header) { result.add(flag); } return result.toString(); } } MessageGroupProcessor processor = new MethodInvokingMessageGroupProcessor(new SimpleAggregator()); messagesUpForProcessing.add(MessageBuilder.withPayload(3).setHeader("foo", Arrays.asList(101, 102)).build()); when(messageGroupMock.getMessages()).thenReturn(messagesUpForProcessing); Object result = processor.processMessageGroup(messageGroupMock); assertThat(((Message<?>) result).getPayload(), is("[1, 2, 4, 3, 101, 102]")); } @Test public void shouldFindAnnotatedPayloadsWithNoType() throws Exception { @SuppressWarnings("unused") class SimpleAggregator { public String and(@Payloads List<?> rawFlags, @Header("foo") List<Integer> header) { @SuppressWarnings("unchecked") List<Integer> flags = (List<Integer>) rawFlags; List<Integer> result = new ArrayList<Integer>(); for (int flag : flags) { result.add(flag); } for (int flag : header) { result.add(flag); } return result.toString(); } } MessageGroupProcessor processor = new MethodInvokingMessageGroupProcessor(new SimpleAggregator()); messagesUpForProcessing.add(MessageBuilder.withPayload(3).setHeader("foo", Arrays.asList(101, 102)).build()); when(messageGroupMock.getMessages()).thenReturn(messagesUpForProcessing); Object result = processor.processMessageGroup(messageGroupMock); assertThat(((Message<?>) result).getPayload(), is("[1, 2, 4, 3, 101, 102]")); } @Test public void shouldUseAnnotatedPayloads() throws Exception { @SuppressWarnings("unused") class SimpleAggregator { @Aggregator public String and(@Payloads List<Integer> flags) { List<Integer> result = new ArrayList<Integer>(); for (int flag : flags) { result.add(flag); } return result.toString(); } public String or(List<Integer> flags) { throw new UnsupportedOperationException("Not expected"); } } MessageGroupProcessor processor = new MethodInvokingMessageGroupProcessor(new SimpleAggregator()); when(messageGroupMock.getMessages()).thenReturn(messagesUpForProcessing); Object result = processor.processMessageGroup(messageGroupMock); assertThat(((Message<?>) result).getPayload(), is("[1, 2, 4]")); } @Test public void shouldFindSimpleAggregatorMethodWithCollection() throws Exception { @SuppressWarnings("unused") class SimpleAggregator { public Integer and(Collection<Integer> flags) { int result = 0; for (Integer flag : flags) { result = result | flag; } return result; } } MessageGroupProcessor processor = new MethodInvokingMessageGroupProcessor(new SimpleAggregator()); when(messageGroupMock.getMessages()).thenReturn(messagesUpForProcessing); Object result = processor.processMessageGroup(messageGroupMock); assertThat(((Message<?>) result).getPayload(), is(7)); } @Test public void shouldFindSimpleAggregatorMethodWithArray() throws Exception { @SuppressWarnings("unused") class SimpleAggregator { public Integer and(int[] flags) { int result = 0; for (int flag : flags) { result = result | flag; } return result; } } MessageGroupProcessor processor = new MethodInvokingMessageGroupProcessor(new SimpleAggregator()); when(messageGroupMock.getMessages()).thenReturn(messagesUpForProcessing); Object result = processor.processMessageGroup(messageGroupMock); assertThat(((Message<?>) result).getPayload(), is(7)); } @Test public void shouldFindSimpleAggregatorMethodWithIterator() throws Exception { @SuppressWarnings("unused") class SimpleAggregator { public Integer and(Iterator<Integer> flags) { int result = 0; while (flags.hasNext()) { result = result | flags.next(); } return result; } } MethodInvokingMessageGroupProcessor processor = new MethodInvokingMessageGroupProcessor(new SimpleAggregator()); GenericConversionService conversionService = new DefaultConversionService(); conversionService.addConverter(new Converter<ArrayList<?>, Iterator<?>>() { @Override public Iterator<?> convert(ArrayList<?> source) { return source.iterator(); } }); processor.setConversionService(conversionService); when(messageGroupMock.getMessages()).thenReturn(messagesUpForProcessing); Object result = processor.processMessageGroup(messageGroupMock); assertThat(((Message<?>) result).getPayload(), is(7)); } @Test public void shouldFindFittingMethodAmongMultipleUnannotated() { @SuppressWarnings("unused") class UnannotatedAggregator { public Integer and(List<Integer> flags) { int result = 0; for (Integer flag : flags) { result = result | flag; } return result; } public void voidMethodShouldBeIgnored(List<Integer> flags) { fail("this method should not be invoked"); } public String methodAcceptingNoCollectionShouldBeIgnored(String irrelevant) { fail("this method should not be invoked"); return null; } } MessageGroupProcessor processor = new MethodInvokingMessageGroupProcessor(new UnannotatedAggregator()); when(messageGroupMock.getMessages()).thenReturn(messagesUpForProcessing); Object result = processor.processMessageGroup(messageGroupMock); assertThat(((Message<?>) result).getPayload(), is(7)); } @Test public void shouldFindFittingMethodForIteratorOfMessages() { @SuppressWarnings("unused") class UnannotatedAggregator { public Iterator<?> and(Iterator<Message<?>> flags) { return flags; } public void voidMethodShouldBeIgnored(List<Integer> flags) { fail("this method should not be invoked"); } public String methodAcceptingNoCollectionShouldBeIgnored(String irrelevant) { fail("this method should not be invoked"); return null; } } MessageGroupProcessor processor = new MethodInvokingMessageGroupProcessor(new UnannotatedAggregator()); when(messageGroupMock.getMessages()).thenReturn(messagesUpForProcessing); Object result = processor.processMessageGroup(messageGroupMock); assertTrue(((Message<?>) result).getPayload() instanceof Iterator<?>); } @Test public void testTwoMethodsWithSameParameterTypesAmbiguous() { class AnnotatedParametersAggregator { @SuppressWarnings("unused") public Integer and(List<Integer> flags) { int result = 0; for (Integer flag : flags) { result = result | flag; } return result; } @SuppressWarnings("unused") public String listHeaderShouldBeIgnored(@Header List<Integer> flags) { fail("this method should not be invoked"); return ""; } } MessageGroupProcessor processor = new MethodInvokingMessageGroupProcessor(new AnnotatedParametersAggregator()); when(messageGroupMock.getMessages()).thenReturn(messagesUpForProcessing); Object result = processor.processMessageGroup(messageGroupMock); Object payload = ((Message<?>) result).getPayload(); assertTrue(payload instanceof Integer); assertEquals(7, payload); } @Test public void singleAnnotation() throws Exception { @SuppressWarnings("unused") class SingleAnnotationTestBean { @Aggregator public String method1(List<String> input) { return input.get(0); } public String method2(List<String> input) { return input.get(1); } } SingleAnnotationTestBean bean = new SingleAnnotationTestBean(); MethodInvokingMessageGroupProcessor aggregator = new MethodInvokingMessageGroupProcessor(bean); SimpleMessageGroup group = new SimpleMessageGroup("FOO"); group.add(new GenericMessage<String>("foo")); group.add(new GenericMessage<String>("bar")); assertEquals("foo", aggregator.aggregatePayloads(group, null)); } @Test public void testHeaderParameters() throws Exception { class SingleAnnotationTestBean { @Aggregator public String method1(List<String> input, @Header("foo") String foo) { return input.get(0) + foo; } } SingleAnnotationTestBean bean = new SingleAnnotationTestBean(); MethodInvokingMessageGroupProcessor aggregator = new MethodInvokingMessageGroupProcessor(bean); SimpleMessageGroup group = new SimpleMessageGroup("FOO"); group.add(MessageBuilder.withPayload("foo").setHeader("foo", "bar").build()); group.add(MessageBuilder.withPayload("bar").setHeader("foo", "bar").build()); assertEquals("foobar", aggregator.aggregatePayloads(group, aggregator.aggregateHeaders(group))); } @Test public void testHeadersParameters() throws Exception { class SingleAnnotationTestBean { @Aggregator public String method1(List<String> input, @Headers Map<String, ?> map) { return input.get(0) + map.get("foo"); } } SingleAnnotationTestBean bean = new SingleAnnotationTestBean(); MethodInvokingMessageGroupProcessor aggregator = new MethodInvokingMessageGroupProcessor(bean); SimpleMessageGroup group = new SimpleMessageGroup("FOO"); group.add(MessageBuilder.withPayload("foo").setHeader("foo", "bar").build()); group.add(MessageBuilder.withPayload("bar").setHeader("foo", "bar").build()); assertEquals("foobar", aggregator.aggregatePayloads(group, aggregator.aggregateHeaders(group))); } @Test(expected = IllegalArgumentException.class) public void multipleAnnotations() { class MultipleAnnotationTestBean { @Aggregator public String method1(List<String> input) { return input.get(0); } @Aggregator public String method2(List<String> input) { return input.get(0); } } MultipleAnnotationTestBean bean = new MultipleAnnotationTestBean(); new MethodInvokingMessageGroupProcessor(bean); } @Test public void noAnnotations() throws Exception { @SuppressWarnings("unused") class NoAnnotationTestBean { public String method1(List<String> input) { return input.get(0); } String method2(List<String> input) { return input.get(1); } } NoAnnotationTestBean bean = new NoAnnotationTestBean(); MethodInvokingMessageGroupProcessor aggregator = new MethodInvokingMessageGroupProcessor(bean); SimpleMessageGroup group = new SimpleMessageGroup("FOO"); group.add(new GenericMessage<String>("foo")); group.add(new GenericMessage<String>("bar")); assertEquals("foo", aggregator.aggregatePayloads(group, null)); } @Test(expected = IllegalArgumentException.class) public void multiplePublicMethods() { @SuppressWarnings("unused") class MultiplePublicMethodTestBean { public String upperCase(String s) { return s.toUpperCase(); } public String lowerCase(String s) { return s.toLowerCase(); } } MultiplePublicMethodTestBean bean = new MultiplePublicMethodTestBean(); new MethodInvokingMessageGroupProcessor(bean); } @Test(expected = IllegalStateException.class) public void noPublicMethods() { @SuppressWarnings("unused") class NoPublicMethodTestBean { String lowerCase(String s) { return s.toLowerCase(); } } NoPublicMethodTestBean bean = new NoPublicMethodTestBean(); new MethodInvokingMessageGroupProcessor(bean); } @Test public void jdkProxy() { DirectChannel input = new DirectChannel(); QueueChannel output = new QueueChannel(); GreetingService testBean = new GreetingBean(); ProxyFactory proxyFactory = new ProxyFactory(testBean); proxyFactory.setProxyTargetClass(true); testBean = (GreetingService) proxyFactory.getProxy(); MethodInvokingMessageGroupProcessor aggregator = new MethodInvokingMessageGroupProcessor(testBean); AggregatingMessageHandler handler = new AggregatingMessageHandler(aggregator); handler.setReleaseStrategy(new MessageCountReleaseStrategy()); handler.setOutputChannel(output); EventDrivenConsumer endpoint = new EventDrivenConsumer(input, handler); endpoint.start(); Message<?> message = MessageBuilder.withPayload("proxy").setCorrelationId("abc").build(); input.send(message); assertEquals("hello proxy", output.receive(0).getPayload()); } @Test public void cglibProxy() { DirectChannel input = new DirectChannel(); QueueChannel output = new QueueChannel(); GreetingService testBean = new GreetingBean(); ProxyFactory proxyFactory = new ProxyFactory(testBean); proxyFactory.setProxyTargetClass(true); testBean = (GreetingService) proxyFactory.getProxy(); MethodInvokingMessageGroupProcessor aggregator = new MethodInvokingMessageGroupProcessor(testBean); AggregatingMessageHandler handler = new AggregatingMessageHandler(aggregator); handler.setReleaseStrategy(new MessageCountReleaseStrategy()); handler.setOutputChannel(output); EventDrivenConsumer endpoint = new EventDrivenConsumer(input, handler); endpoint.start(); Message<?> message = MessageBuilder.withPayload("proxy").setCorrelationId("abc").build(); input.send(message); assertEquals("hello proxy", output.receive(0).getPayload()); } public interface GreetingService { String sayHello(List<String> names); } public static class GreetingBean implements GreetingService { private String greeting = "hello"; public void setGreeting(String greeting) { this.greeting = greeting; } @Override @Aggregator public String sayHello(List<String> names) { return greeting + " " + names.get(0); } } }