Java tutorial
/* * Copyright 2002-2013 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.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertSame; import java.lang.reflect.Method; import java.util.Map; import java.util.Properties; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.hamcrest.Description; import org.hamcrest.TypeSafeMatcher; import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; import org.springframework.expression.spel.SpelEvaluationException; import org.springframework.integration.Message; import org.springframework.integration.MessageHandlingException; import org.springframework.integration.annotation.Header; import org.springframework.integration.annotation.ServiceActivator; import org.springframework.integration.gateway.GatewayProxyFactoryBean; import org.springframework.integration.gateway.RequestReplyExchanger; import org.springframework.integration.message.GenericMessage; import org.springframework.integration.support.MessageBuilder; import org.springframework.integration.util.MessagingMethodInvokerHelper; /** * @author Mark Fisher * @author Marius Bogoevici * @author Oleg Zhurakousky * @author Dave Syer * @author Gary Russell * @author Gunnar Hillert */ @SuppressWarnings({ "rawtypes", "unchecked" }) public class MethodInvokingMessageProcessorTests { private static final Log logger = LogFactory.getLog(MethodInvokingMessageProcessorTests.class); @Rule public ExpectedException expected = ExpectedException.none(); @Test public void testHandlerInheritanceMethodImplInSuper() { class A { @SuppressWarnings("unused") public Message<String> myMethod(final Message<String> msg) { return MessageBuilder.fromMessage(msg).setHeader("A", "A").build(); } } class B extends A { } @SuppressWarnings("unused") class C extends B { } MethodInvokingMessageProcessor processor = new MethodInvokingMessageProcessor(new B(), "myMethod"); Message<?> message = (Message<?>) processor.processMessage(new GenericMessage<String>("")); assertEquals("A", message.getHeaders().get("A")); } @Test public void testHandlerInheritanceMethodImplInLatestSuper() { class A { @SuppressWarnings("unused") public Message<String> myMethod(Message<String> msg) { return MessageBuilder.fromMessage(msg).setHeader("A", "A").build(); } } class B extends A { @Override public Message<String> myMethod(Message<String> msg) { return MessageBuilder.fromMessage(msg).setHeader("B", "B").build(); } } @SuppressWarnings("unused") class C extends B { } MethodInvokingMessageProcessor processor = new MethodInvokingMessageProcessor(new B(), "myMethod"); Message<?> message = (Message<?>) processor.processMessage(new GenericMessage<String>("")); assertEquals("B", message.getHeaders().get("B")); } public void testHandlerInheritanceMethodImplInSubClass() { class A { @SuppressWarnings("unused") public Message<String> myMethod(Message<String> msg) { return MessageBuilder.fromMessage(msg).setHeader("A", "A").build(); } } class B extends A { @Override public Message<String> myMethod(Message<String> msg) { return MessageBuilder.fromMessage(msg).setHeader("B", "B").build(); } } class C extends B { @Override public Message<String> myMethod(Message<String> msg) { return MessageBuilder.fromMessage(msg).setHeader("C", "C").build(); } } MethodInvokingMessageProcessor processor = new MethodInvokingMessageProcessor(new C(), "myMethod"); Message<?> message = (Message<?>) processor.processMessage(new GenericMessage<String>("")); assertEquals("C", message.getHeaders().get("C")); } public void testHandlerInheritanceMethodImplInSubClassAndSuper() { class A { @SuppressWarnings("unused") public Message<String> myMethod(Message<String> msg) { return MessageBuilder.fromMessage(msg).setHeader("A", "A").build(); } } class B extends A { } class C extends B { @Override public Message<String> myMethod(Message<String> msg) { return MessageBuilder.fromMessage(msg).setHeader("C", "C").build(); } } MethodInvokingMessageProcessor processor = new MethodInvokingMessageProcessor(new C(), "myMethod"); Message<?> message = (Message<?>) processor.processMessage(new GenericMessage<String>("")); assertEquals("C", message.getHeaders().get("C")); } @Test public void payloadAsMethodParameterAndObjectAsReturnValue() { MethodInvokingMessageProcessor processor = new MethodInvokingMessageProcessor(new TestBean(), "acceptPayloadAndReturnObject"); Object result = processor.processMessage(new GenericMessage<String>("testing")); assertEquals("testing-1", result); } @Test public void testPayloadCoercedToString() { MethodInvokingMessageProcessor processor = new MethodInvokingMessageProcessor(new TestBean(), "acceptPayloadAndReturnObject"); Object result = processor.processMessage(new GenericMessage<Integer>(123456789)); assertEquals("123456789-1", result); } @Test public void payloadAsMethodParameterAndMessageAsReturnValue() { MethodInvokingMessageProcessor processor = new MethodInvokingMessageProcessor(new TestBean(), "acceptPayloadAndReturnMessage"); Message<?> result = (Message<?>) processor.processMessage(new GenericMessage<String>("testing")); assertEquals("testing-2", result.getPayload()); } @Test public void messageAsMethodParameterAndObjectAsReturnValue() { MethodInvokingMessageProcessor processor = new MethodInvokingMessageProcessor(new TestBean(), "acceptMessageAndReturnObject"); Object result = processor.processMessage(new GenericMessage<String>("testing")); assertEquals("testing-3", result); } @Test public void messageAsMethodParameterAndMessageAsReturnValue() { MethodInvokingMessageProcessor processor = new MethodInvokingMessageProcessor(new TestBean(), "acceptMessageAndReturnMessage"); Message<?> result = (Message<?>) processor.processMessage(new GenericMessage<String>("testing")); assertEquals("testing-4", result.getPayload()); } @Test public void messageSubclassAsMethodParameterAndMessageAsReturnValue() { MethodInvokingMessageProcessor processor = new MethodInvokingMessageProcessor(new TestBean(), "acceptMessageSubclassAndReturnMessage"); Message<?> result = (Message<?>) processor.processMessage(new GenericMessage<String>("testing")); assertEquals("testing-5", result.getPayload()); } @Test public void messageSubclassAsMethodParameterAndMessageSubclassAsReturnValue() { MethodInvokingMessageProcessor processor = new MethodInvokingMessageProcessor(new TestBean(), "acceptMessageSubclassAndReturnMessageSubclass"); Message<?> result = (Message<?>) processor.processMessage(new GenericMessage<String>("testing")); assertEquals("testing-6", result.getPayload()); } @Test public void payloadAndHeaderAnnotationMethodParametersAndObjectAsReturnValue() { MethodInvokingMessageProcessor processor = new MethodInvokingMessageProcessor(new TestBean(), "acceptPayloadAndHeaderAndReturnObject"); Message<?> request = MessageBuilder.withPayload("testing").setHeader("number", new Integer(123)).build(); Object result = processor.processMessage(request); assertEquals("testing-123", result); } @Test public void testVoidMethodsIncludedbyDefault() { MethodInvokingMessageProcessor processor = new MethodInvokingMessageProcessor(new TestBean(), "testVoidReturningMethods"); assertNull(processor.processMessage(MessageBuilder.withPayload("Something").build())); assertEquals(12, processor.processMessage(MessageBuilder.withPayload(12).build())); } @Test public void messageOnlyWithAnnotatedMethod() throws Exception { AnnotatedTestService service = new AnnotatedTestService(); Method method = service.getClass().getMethod("messageOnly", Message.class); MethodInvokingMessageProcessor processor = new MethodInvokingMessageProcessor(service, method); Object result = processor.processMessage(new GenericMessage<String>("foo")); assertEquals("foo", result); } @Test public void payloadWithAnnotatedMethod() throws Exception { AnnotatedTestService service = new AnnotatedTestService(); Method method = service.getClass().getMethod("integerMethod", Integer.class); MethodInvokingMessageProcessor processor = new MethodInvokingMessageProcessor(service, method); Object result = processor.processMessage(new GenericMessage<Integer>(new Integer(123))); assertEquals(new Integer(123), result); } @Test public void convertedPayloadWithAnnotatedMethod() throws Exception { AnnotatedTestService service = new AnnotatedTestService(); Method method = service.getClass().getMethod("integerMethod", Integer.class); MethodInvokingMessageProcessor processor = new MethodInvokingMessageProcessor(service, method); Object result = processor.processMessage(new GenericMessage<String>("456")); assertEquals(new Integer(456), result); } @Test(expected = MessageHandlingException.class) public void conversionFailureWithAnnotatedMethod() throws Exception { AnnotatedTestService service = new AnnotatedTestService(); Method method = service.getClass().getMethod("integerMethod", Integer.class); MethodInvokingMessageProcessor processor = new MethodInvokingMessageProcessor(service, method); processor.processMessage(new GenericMessage<String>("foo")); } @Test public void filterSelectsAnnotationMethodsOnly() { OverloadedMethodBean bean = new OverloadedMethodBean(); MethodInvokingMessageProcessor processor = new MethodInvokingMessageProcessor(bean, ServiceActivator.class); processor.processMessage(MessageBuilder.withPayload(123).build()); assertNotNull(bean.lastArg); assertEquals(String.class, bean.lastArg.getClass()); assertEquals("123", bean.lastArg); } @Test public void testProcessMessageBadExpression() throws Exception { // TODO: should this be MessageHandlingException or NumberFormatException? expected.expect(new ExceptionCauseMatcher(Exception.class)); AnnotatedTestService service = new AnnotatedTestService(); Method method = service.getClass().getMethod("integerMethod", Integer.class); MethodInvokingMessageProcessor processor = new MethodInvokingMessageProcessor(service, method); assertEquals("foo", processor.processMessage(new GenericMessage<String>("foo"))); } @Test public void testProcessMessageRuntimeException() throws Exception { expected.expect(new ExceptionCauseMatcher(UnsupportedOperationException.class)); TestErrorService service = new TestErrorService(); Method method = service.getClass().getMethod("error", String.class); MethodInvokingMessageProcessor processor = new MethodInvokingMessageProcessor(service, method); assertEquals("foo", processor.processMessage(new GenericMessage<String>("foo"))); } @Test public void testProcessMessageCheckedException() throws Exception { expected.expect(new ExceptionCauseMatcher(CheckedException.class)); TestErrorService service = new TestErrorService(); Method method = service.getClass().getMethod("checked", String.class); MethodInvokingMessageProcessor processor = new MethodInvokingMessageProcessor(service, method); assertEquals("foo", processor.processMessage(new GenericMessage<String>("foo"))); } @Test public void testProcessMessageMethodNotFound() throws Exception { expected.expect(new ExceptionCauseMatcher(SpelEvaluationException.class)); TestDifferentErrorService service = new TestDifferentErrorService(); Method method = TestErrorService.class.getMethod("checked", String.class); MethodInvokingMessageProcessor processor = new MethodInvokingMessageProcessor(service, method); processor.processMessage(new GenericMessage<String>("foo")); } @Test public void messageAndHeaderWithAnnotatedMethod() throws Exception { AnnotatedTestService service = new AnnotatedTestService(); Method method = service.getClass().getMethod("messageAndHeader", Message.class, Integer.class); MethodInvokingMessageProcessor processor = new MethodInvokingMessageProcessor(service, method); Message<String> message = MessageBuilder.withPayload("foo").setHeader("number", 42).build(); Object result = processor.processMessage(message); assertEquals("foo-42", result); } @Test public void multipleHeadersWithAnnotatedMethod() throws Exception { AnnotatedTestService service = new AnnotatedTestService(); Method method = service.getClass().getMethod("twoHeaders", String.class, Integer.class); MethodInvokingMessageProcessor processor = new MethodInvokingMessageProcessor(service, method); Message<String> message = MessageBuilder.withPayload("foo").setHeader("prop", "bar").setHeader("number", 42) .build(); Object result = processor.processMessage(message); assertEquals("bar-42", result); } @Test public void testOverloadedNonVoidReturningMethodsWithExactMatchForType() { AmbiguousMethodBean bean = new AmbiguousMethodBean(); MethodInvokingMessageProcessor processor = new MethodInvokingMessageProcessor(bean, "foo"); processor.processMessage(MessageBuilder.withPayload("true").build()); assertNotNull(bean.lastArg); assertEquals(String.class, bean.lastArg.getClass()); assertEquals("true", bean.lastArg); } @Test public void gatewayTest() throws Exception { GatewayProxyFactoryBean gwFactoryBean = new GatewayProxyFactoryBean(); gwFactoryBean.afterPropertiesSet(); Object target = gwFactoryBean.getObject(); // just instantiate a helper with a simple target; we're going to invoke getTargetClass with reflection MessagingMethodInvokerHelper helper = new MessagingMethodInvokerHelper(new TestErrorService(), "error", true); Method method = MessagingMethodInvokerHelper.class.getDeclaredMethod("getTargetClass", Object.class); method.setAccessible(true); Object result = method.invoke(helper, target); assertSame(RequestReplyExchanger.class, result); } private static class ExceptionCauseMatcher extends TypeSafeMatcher<Exception> { private Throwable cause; private final Class<? extends Exception> type; public ExceptionCauseMatcher(Class<? extends Exception> type) { this.type = type; } @Override public boolean matchesSafely(Exception item) { logger.debug(item); cause = item.getCause(); assertNotNull("There is no cause for " + item, cause); return type.isAssignableFrom(cause.getClass()); } public void describeTo(Description description) { description.appendText("cause to be ").appendValue(type).appendText("but was ").appendValue(cause); } } @SuppressWarnings("unused") private static class TestErrorService { public String error(String input) { throw new UnsupportedOperationException("Expected test exception"); } public String checked(String input) throws Exception { throw new CheckedException("Expected test exception"); } } @SuppressWarnings("unused") private static class TestDifferentErrorService { public String checked(String input) throws Exception { throw new CheckedException("Expected test exception"); } } @SuppressWarnings("serial") public static final class CheckedException extends Exception { public CheckedException(String string) { super(string); } } @SuppressWarnings("unused") private static class TestBean { public String acceptPayloadAndReturnObject(String s) { return s + "-1"; } public Message<?> acceptPayloadAndReturnMessage(String s) { return new GenericMessage<String>(s + "-2"); } public String acceptMessageAndReturnObject(Message<?> m) { return m.getPayload() + "-3"; } public Message<?> acceptMessageAndReturnMessage(Message<?> m) { return new GenericMessage<String>(m.getPayload() + "-4"); } public Message<?> acceptMessageSubclassAndReturnMessage(GenericMessage<String> m) { return new GenericMessage<String>(m.getPayload() + "-5"); } public GenericMessage<String> acceptMessageSubclassAndReturnMessageSubclass(GenericMessage<String> m) { return new GenericMessage<String>(m.getPayload() + "-6"); } public String acceptPayloadAndHeaderAndReturnObject(String s, @Header("number") Integer n) { return s + "-" + n; } public void testVoidReturningMethods(String s) { // do nothing } public int testVoidReturningMethods(int i) { return i; } } @SuppressWarnings("unused") private static class AnnotatedTestService { public String messageOnly(Message<?> message) { return (String) message.getPayload(); } public String messageAndHeader(Message<?> message, @Header("number") Integer num) { return (String) message.getPayload() + "-" + num.toString(); } public String twoHeaders(@Header String prop, @Header("number") Integer num) { return prop + "-" + num.toString(); } public Integer optionalHeader(@Header(required = false) Integer num) { return num; } public Integer requiredHeader(@Header(value = "num", required = true) Integer num) { return num; } public String optionalAndRequiredHeader(@Header(required = false) String prop, @Header(value = "num", required = true) Integer num) { return prop + num; } public Properties propertiesMethod(Properties properties) { return properties; } public Map mapMethod(Map map) { return map; } public Integer integerMethod(Integer i) { return i; } } /** * Method names create ambiguities, but the MethodResolver implementation should filter out based on the annotation * or the 'requiresReply' flag. */ @SuppressWarnings("unused") private static class AmbiguousMethodBean { private volatile Object lastArg = null; public void foo(boolean b) { this.lastArg = b; } public String foo(String s) { this.lastArg = s; return s; } public String foo(int i) { this.lastArg = i; return Integer.valueOf(i).toString(); } } /** * Method names create ambiguities, but the MethodResolver implementation should filter out based on the annotation * or the 'requiresReply' flag. */ @SuppressWarnings("unused") private static class OverloadedMethodBean { private volatile Object lastArg = null; public void foo(boolean b) { this.lastArg = b; } @ServiceActivator public String foo(String s) { this.lastArg = s; return s; } } }