/* * 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.handler; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import java.lang.reflect.Method; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Properties; import java.util.Set; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.junit.Assert; import org.junit.Test; import org.springframework.integration.support.MessageBuilder; import org.springframework.integration.test.util.TestUtils; import org.springframework.messaging.Message; import org.springframework.messaging.MessageHandlingException; import org.springframework.messaging.MessageHeaders; import org.springframework.messaging.MessagingException; import org.springframework.messaging.handler.annotation.Header; import org.springframework.messaging.handler.annotation.Headers; import org.springframework.messaging.handler.annotation.Payload; import org.springframework.messaging.support.GenericMessage; /** * @author Mark Fisher * @author Iwein Fuld * @author Oleg Zhurakousky * @author Gary Russell * @author Artem Bilan */ @SuppressWarnings({ "rawtypes", "unchecked" }) public class MethodInvokingMessageProcessorAnnotationTests { private final TestService testService = new TestService(); private final Employee employee = new Employee("oleg", "zhurakousky"); private static volatile int concurrencyFailures = 0; @Test public void multiThreadsUUIDToStringConversion() throws Exception { Method method = TestService.class.getMethod("headerId", String.class, String.class); final MethodInvokingMessageProcessor processor = new MethodInvokingMessageProcessor(testService, method); ExecutorService exec = Executors.newFixedThreadPool(100); processor.processMessage(new GenericMessage<String>("foo")); for (int i = 0; i < 100; i++) { exec.execute(new Runnable() { public void run() { Object result = processor.processMessage(new GenericMessage<String>("foo")); assertNotNull(result); } }); } exec.shutdown(); assertTrue(exec.awaitTermination(10, TimeUnit.SECONDS)); assertEquals(0, concurrencyFailures); } @Test public void optionalHeader() throws Exception { Method method = TestService.class.getMethod("optionalHeader", Integer.class); MethodInvokingMessageProcessor processor = new MethodInvokingMessageProcessor(testService, method); Object result = processor.processMessage(new GenericMessage<String>("foo")); assertNull(result); } @Test(expected = MessageHandlingException.class) public void requiredHeaderNotProvided() throws Exception { Method method = TestService.class.getMethod("requiredHeader", Integer.class); MethodInvokingMessageProcessor processor = new MethodInvokingMessageProcessor(testService, method); processor.processMessage(new GenericMessage<String>("foo")); } @Test(expected = MessageHandlingException.class) public void requiredHeaderNotProvidedOnSecondMessage() throws Exception { Method method = TestService.class.getMethod("requiredHeader", Integer.class); MethodInvokingMessageProcessor processor = new MethodInvokingMessageProcessor(testService, method); Message<String> messageWithHeader = MessageBuilder.withPayload("foo") .setHeader("num", 123).build(); GenericMessage<String> messageWithoutHeader = new GenericMessage<String>("foo"); processor.processMessage(messageWithHeader); processor.processMessage(messageWithoutHeader); } @Test public void fromMessageWithRequiredHeaderProvided() throws Exception { Method method = TestService.class.getMethod("requiredHeader", Integer.class); Message<String> message = MessageBuilder.withPayload("foo") .setHeader("num", 123).build(); MethodInvokingMessageProcessor processor = new MethodInvokingMessageProcessor(testService, method); Object result = processor.processMessage(message); assertEquals(123, result); } @Test(expected = MessageHandlingException.class) public void fromMessageWithOptionalAndRequiredHeaderAndOnlyOptionalHeaderProvided() throws Exception { Method method = TestService.class.getMethod("optionalAndRequiredHeader", String.class, Integer.class); MethodInvokingMessageProcessor processor = new MethodInvokingMessageProcessor(testService, method); Message<String> message = MessageBuilder.withPayload("foo") .setHeader("prop", "bar").build(); processor.processMessage(message); } @Test public void fromMessageWithOptionalAndRequiredHeaderAndOnlyRequiredHeaderProvided() throws Exception { Method method = TestService.class.getMethod("optionalAndRequiredHeader", String.class, Integer.class); MethodInvokingMessageProcessor processor = new MethodInvokingMessageProcessor(testService, method); Message<String> message = MessageBuilder.withPayload("foo") .setHeader("num", 123).build(); Object result = processor.processMessage(message); assertEquals("null123", result); } @Test public void fromMessageWithOptionalAndRequiredHeaderAndBothHeadersProvided() throws Exception { Method method = TestService.class.getMethod("optionalAndRequiredHeader", String.class, Integer.class); MethodInvokingMessageProcessor processor = new MethodInvokingMessageProcessor(testService, method); Message<String> message = MessageBuilder.withPayload("foo") .setHeader("num", 123) .setHeader("prop", "bar") .build(); Object result = processor.processMessage(message); assertEquals("bar123", result); } @Test public void fromMessageWithPropertiesMethodAndHeadersAnnotation() throws Exception { Method method = TestService.class.getMethod("propertiesHeaders", Properties.class); MethodInvokingMessageProcessor processor = new MethodInvokingMessageProcessor(testService, method); Message<String> message = MessageBuilder.withPayload("test") .setHeader("prop1", "foo").setHeader("prop2", "bar").build(); assertFalse(TestUtils.getPropertyValue(processor, "delegate.handlerMethod.spelOnly", Boolean.class)); for (int i = 0; i < 99; i++) { Object result = processor.processMessage(message); Properties props = (Properties) result; assertEquals("foo", props.getProperty("prop1")); assertEquals("bar", props.getProperty("prop2")); assertFalse(TestUtils.getPropertyValue(processor, "delegate.handlerMethod.spelOnly", Boolean.class)); } Object result = processor.processMessage(message); Properties props = (Properties) result; assertEquals("foo", props.getProperty("prop1")); assertEquals("bar", props.getProperty("prop2")); assertTrue(TestUtils.getPropertyValue(processor, "delegate.handlerMethod.spelOnly", Boolean.class)); } @Test public void fromMessageWithPropertiesAndObjectMethod() throws Exception { Method method = TestService.class.getMethod("propertiesHeadersAndPayload", Properties.class, Object.class); MethodInvokingMessageProcessor processor = new MethodInvokingMessageProcessor(testService, method); Message<String> message = MessageBuilder.withPayload("test") .setHeader("prop1", "foo").setHeader("prop2", "bar").build(); Object result = processor.processMessage(message); Properties props = (Properties) result; assertEquals("foo", props.getProperty("prop1")); assertEquals("bar", props.getProperty("prop2")); assertEquals("test", props.getProperty("payload")); } @Test public void fromMessageWithMapAndObjectMethod() throws Exception { Method method = TestService.class.getMethod("mapHeadersAndPayload", Map.class, Object.class); MethodInvokingMessageProcessor processor = new MethodInvokingMessageProcessor(testService, method); Message<String> message = MessageBuilder.withPayload("test") .setHeader("prop1", "foo").setHeader("prop2", "bar").build(); Map<?, ?> result = (Map<?, ?>) processor.processMessage(message); assertEquals(5, result.size()); assertTrue(result.containsKey(MessageHeaders.ID)); assertTrue(result.containsKey(MessageHeaders.TIMESTAMP)); assertEquals("foo", result.get("prop1")); assertEquals("bar", result.get("prop2")); assertEquals("test", result.get("payload")); } @Test public void fromMessageWithPropertiesMethodAndPropertiesPayload() throws Exception { Method method = TestService.class.getMethod("propertiesPayload", Properties.class); MethodInvokingMessageProcessor processor = new MethodInvokingMessageProcessor(testService, method); Properties payload = new Properties(); payload.setProperty("prop1", "foo"); payload.setProperty("prop2", "bar"); Message<Properties> message = MessageBuilder.withPayload(payload) .setHeader("prop1", "not").setHeader("prop2", "these").build(); Properties result = (Properties) processor.processMessage(message); assertEquals(2, result.size()); assertEquals("foo", result.getProperty("prop1")); assertEquals("bar", result.getProperty("prop2")); } @Test public void fromMessageWithMapMethodAndHeadersAnnotation() throws Exception { Method method = TestService.class.getMethod("mapHeaders", Map.class); MethodInvokingMessageProcessor processor = new MethodInvokingMessageProcessor(testService, method); Message<String> message = MessageBuilder.withPayload("test") .setHeader("attrib1", 123) .setHeader("attrib2", 456).build(); Map<String, Object> result = (Map<String, Object>) processor.processMessage(message); assertEquals(123, result.get("attrib1")); assertEquals(456, result.get("attrib2")); } @Test public void fromMessageWithMapMethodAndMapPayload() throws Exception { Method method = TestService.class.getMethod("mapPayload", Map.class); MethodInvokingMessageProcessor processor = new MethodInvokingMessageProcessor(testService, method); Map<String, Integer> payload = new HashMap<String, Integer>(); payload.put("attrib1", 88); payload.put("attrib2", 99); Message<Map<String, Integer>> message = MessageBuilder.withPayload(payload) .setHeader("attrib1", 123) .setHeader("attrib2", 456).build(); Map<String, Integer> result = (Map<String, Integer>) processor.processMessage(message); assertEquals(2, result.size()); assertEquals(new Integer(88), result.get("attrib1")); assertEquals(new Integer(99), result.get("attrib2")); } @Test public void headerAnnotationWithExpression() throws Exception { Message<?> message = this.getMessage(); Method method = TestService.class.getMethod("headerAnnotationWithExpression", String.class); MethodInvokingMessageProcessor processor = new MethodInvokingMessageProcessor(testService, method); Object result = processor.processMessage(message); Assert.assertEquals("monday", result); } @Test public void irrelevantAnnotation() throws Exception { Message<?> message = MessageBuilder.withPayload("foo").build(); Method method = TestService.class.getMethod("irrelevantAnnotation", String.class); MethodInvokingMessageProcessor processor = new MethodInvokingMessageProcessor(testService, method); Object result = processor.processMessage(message); assertEquals("foo", result); } @Test public void multipleAnnotatedArgs() throws Exception { Message<?> message = this.getMessage(); Method method = TestService.class.getMethod("multipleAnnotatedArguments", String.class, String.class, Employee.class, String.class, Map.class); MethodInvokingMessageProcessor processor = new MethodInvokingMessageProcessor(testService, method); Object[] parameters = (Object[]) processor.processMessage(message); Assert.assertNotNull(parameters); Assert.assertTrue(parameters.length == 5); Assert.assertTrue(parameters[0].equals("monday")); Assert.assertTrue(parameters[1].equals("September")); Assert.assertTrue(parameters[2].equals(employee)); Assert.assertTrue(parameters[3].equals("oleg")); Assert.assertTrue(parameters[4] instanceof Map); } @Test public void fromMessageToPayload() throws Exception { Method method = TestService.class.getMethod("mapOnly", Map.class); MethodInvokingMessageProcessor processor = new MethodInvokingMessageProcessor(testService, method); Message<Employee> message = MessageBuilder.withPayload(employee).setHeader("number", "jkl").build(); Object result = processor.processMessage(message); Assert.assertTrue(result instanceof Map); Assert.assertEquals("jkl", ((Map<?, ?>) result).get("number")); } @Test public void fromMessageToPayloadArg() throws Exception { Method method = TestService.class.getMethod("payloadAnnotationFirstName", String.class); MethodInvokingMessageProcessor processor = new MethodInvokingMessageProcessor(testService, method); Message<Employee> message = MessageBuilder.withPayload(employee).setHeader("number", "jkl").build(); Object result = processor.processMessage(message); Assert.assertTrue(result instanceof String); Assert.assertEquals("oleg", result); } @Test public void fromMessageToPayloadArgs() throws Exception { Method method = TestService.class.getMethod("payloadAnnotationFullName", String.class, String.class); MethodInvokingMessageProcessor processor = new MethodInvokingMessageProcessor(testService, method); Message<Employee> message = MessageBuilder.withPayload(employee).setHeader("number", "jkl").build(); Object result = processor.processMessage(message); Assert.assertEquals("oleg zhurakousky", result); } @Test public void fromMessageToPayloadArgsHeaderArgs() throws Exception { Method method = TestService.class.getMethod("payloadArgAndHeaderArg", String.class, String.class); MethodInvokingMessageProcessor processor = new MethodInvokingMessageProcessor(testService, method); Message<Employee> message = MessageBuilder.withPayload(employee).setHeader("day", "monday").build(); Object result = processor.processMessage(message); Assert.assertEquals("olegmonday", result); } @Test(expected = MessagingException.class) public void fromMessageInvalidMethodWithMultipleMappingAnnotations() throws Exception { Method method = MultipleMappingAnnotationTestBean.class.getMethod("test", String.class); MethodInvokingMessageProcessor processor = new MethodInvokingMessageProcessor(testService, method); Message<?> message = MessageBuilder.withPayload("payload").setHeader("foo", "bar").build(); processor.processMessage(message); } @Test public void fromMessageToHeadersWithExpressions() throws Exception { Method method = TestService.class.getMethod("headersWithExpressions", String.class, String.class); MethodInvokingMessageProcessor processor = new MethodInvokingMessageProcessor(testService, method); Employee employee = new Employee("John", "Doe"); Message<?> message = MessageBuilder.withPayload("payload").setHeader("emp", employee).build(); Object result = processor.processMessage(message); assertEquals("DOE, John", result); } @Test public void fromMessageToHyphenatedHeaderName() throws Exception { Method method = TestService.class.getMethod("headerNameWithHyphen", String.class); MethodInvokingMessageProcessor processor = new MethodInvokingMessageProcessor(testService, method); Message<?> message = MessageBuilder.withPayload("payload").setHeader("foo-bar", "abc").build(); Object result = processor.processMessage(message); assertEquals("ABC", result); } private Message<?> getMessage() { MessageBuilder<Employee> builder = MessageBuilder.withPayload(employee); builder.setHeader("day", "monday"); builder.setHeader("month", "September"); return builder.build(); } @SuppressWarnings("unused") private static class MultipleMappingAnnotationTestBean { public void test(@Payload("payload") @Header("foo") String s) { } } @SuppressWarnings("unused") private static class TestService { private final Log logger = LogFactory.getLog(this.getClass()); public Map<?, ?> mapOnly(Map<?, ?> map) { return map; } public String payloadAnnotationFirstName(@Payload("fname") String fname) { return fname; } public String payloadAnnotationFullName(@Payload("fname") String first, @Payload("lname") String last) { return first + " " + last; } public String payloadArgAndHeaderArg(@Payload("fname") String fname, @Header String day) { return fname + day; } public Integer optionalHeader(@Header(required = false) Integer num) { return num; } public Integer requiredHeader(@Header(value = "num", required = true) Integer num) { return num; } public String headersWithExpressions(@Header("emp.fname") String firstName, @Header("emp.lname.toUpperCase()") String lastName) { return lastName + ", " + firstName; } public String optionalAndRequiredHeader(@Header(required = false) String prop, @Header(value = "num", required = true) Integer num) { return prop + num; } public Properties propertiesPayload(Properties properties) { return properties; } public Properties propertiesHeaders(@Headers Properties properties) { return properties; } public Object propertiesHeadersAndPayload(Properties headers, Object payload) { headers.put("payload", payload); return headers; } public Map<?, ?> mapPayload(Map<?, ?> map) { return map; } public Map<?, ?> mapHeaders(@Headers Map<?, ?> map) { return map; } public Object mapHeadersAndPayload(Map<String, Object> headers, Object payload) { Map<String, Object> map = new HashMap<String, Object>(headers); map.put("payload", payload); return map; } public Integer integerMethod(Integer i) { return i; } public String headerAnnotationWithExpression(@Header("day") String value) { return value; } public Object[] multipleAnnotatedArguments(@Header("day") String argA, @Header("month") String argB, @Payload Employee payloadArg, @Payload("fname") String value, @Headers Map<?, ?> headers) { return new Object[] { argA, argB, payloadArg, value, headers }; } public String irrelevantAnnotation(@BogusAnnotation String value) { return value; } public String headerNameWithHyphen(@Header("foo-bar") String foobar) { return foobar.toUpperCase(); } Set<String> ids = Collections.synchronizedSet(new HashSet<String>()); public String headerId(String payload, @Header("id") String id) { logger.debug(id); if (ids.contains(id)) { concurrencyFailures++; } ids.add(id); return "foo"; } } public static class Employee { private String fname; private String lname; public Employee(String fname, String lname) { this.fname = fname; this.lname = lname; } public String getFname() { return fname; } public String getLname() { return lname; } } }