From 5d668aaa8cbef3ea036677031abe798ac8d16089 Mon Sep 17 00:00:00 2001 From: Mark Fisher Date: Wed, 15 Dec 2010 11:32:31 -0500 Subject: [PATCH] INT-1688 added fallback to resolution of framework methods in service-activator: MessageHandler or RequestReplyExchanger as created by Gateway proxies. --- .../util/MessagingMethodInvokerHelper.java | 44 +++++++++-- ...torDefaultFrameworkMethodTests-context.xml | 28 +++++++ ...eActivatorDefaultFrameworkMethodTests.java | 78 +++++++++++++++++++ 3 files changed, 142 insertions(+), 8 deletions(-) create mode 100644 spring-integration-core/src/test/java/org/springframework/integration/handler/ServiceActivatorDefaultFrameworkMethodTests-context.xml create mode 100644 spring-integration-core/src/test/java/org/springframework/integration/handler/ServiceActivatorDefaultFrameworkMethodTests.java diff --git a/spring-integration-core/src/main/java/org/springframework/integration/util/MessagingMethodInvokerHelper.java b/spring-integration-core/src/main/java/org/springframework/integration/util/MessagingMethodInvokerHelper.java index 4cef507bb9..874b3c8d0d 100644 --- a/spring-integration-core/src/main/java/org/springframework/integration/util/MessagingMethodInvokerHelper.java +++ b/spring-integration-core/src/main/java/org/springframework/integration/util/MessagingMethodInvokerHelper.java @@ -33,6 +33,7 @@ import java.util.concurrent.atomic.AtomicReference; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; + import org.springframework.aop.support.AopUtils; import org.springframework.core.LocalVariableTableParameterNameDiscoverer; import org.springframework.core.MethodParameter; @@ -51,6 +52,7 @@ import org.springframework.integration.annotation.Header; import org.springframework.integration.annotation.Headers; import org.springframework.integration.annotation.Payload; import org.springframework.integration.annotation.Payloads; +import org.springframework.integration.annotation.ServiceActivator; import org.springframework.util.Assert; import org.springframework.util.ReflectionUtils; import org.springframework.util.ReflectionUtils.MethodCallback; @@ -317,6 +319,28 @@ public class MessagingMethodInvokerHelper extends AbstractExpressionEvaluator if (!candidateMethods.isEmpty()) { return candidateMethods; } + if ((fallbackMethods.isEmpty() || ambiguousFallbackType.get() != null) && ServiceActivator.class.equals(annotationType)) { + // a Service Activator can fallback to either MessageHandler.handleMessage(m) or RequestReplyExchanger.exchange(m) + List frameworkMethods = new ArrayList(); + Class[] allInterfaces = org.springframework.util.ClassUtils.getAllInterfacesForClass(targetClass); + for (Class iface : allInterfaces) { + try { + if ("org.springframework.integration.gateway.RequestReplyExchanger".equals(iface.getName())) { + frameworkMethods.add(targetClass.getMethod("exchange", Message.class)); + } + else if ("org.springframework.integration.core.MessageHandler".equals(iface.getName()) && !requiresReply) { + frameworkMethods.add(targetClass.getMethod("handleMessage", Message.class)); + } + } + catch (Exception e) { + // should never happen (but would fall through to errors below) + } + } + if (frameworkMethods.size() == 1) { + HandlerMethod handlerMethod = new HandlerMethod(frameworkMethods.get(0), canProcessMessageList); + return Collections., HandlerMethod>singletonMap(Object.class, handlerMethod); + } + } Assert.notEmpty(fallbackMethods, "Target object of type [" + this.targetObject.getClass() + "] has no eligible methods for handling Messages."); Assert.isNull(ambiguousFallbackType.get(), "Found ambiguous parameter type [" + ambiguousFallbackType @@ -370,6 +394,7 @@ public class MessagingMethodInvokerHelper extends AbstractExpressionEvaluator return (method.getName().equals("clone") && method.getParameterTypes().length == 0); } + /** * Helper class for generating and exposing metadata for a candidate handler method. The metadata includes the SpEL * expression and the expected payload type. @@ -380,30 +405,33 @@ public class MessagingMethodInvokerHelper extends AbstractExpressionEvaluator private static final ParameterNameDiscoverer PARAMETER_NAME_DISCOVERER = new LocalVariableTableParameterNameDiscoverer(); - private final Method method; - - private final Expression expression; - - private volatile TypeDescriptor targetParameterType; - private static final TypeDescriptor messageTypeDescriptor = TypeDescriptor.valueOf(Message.class); - private static final TypeDescriptor messageListTypeDescriptor = new TypeDescriptor(ReflectionUtils.findField( - HandlerMethod.class, "dummyMessages")); + private static final TypeDescriptor messageListTypeDescriptor = new TypeDescriptor( + ReflectionUtils.findField(HandlerMethod.class, "dummyMessages")); private static final TypeDescriptor messageArrayTypeDescriptor = TypeDescriptor.valueOf(Message[].class); @SuppressWarnings("unused") private static final List> dummyMessages = Collections.emptyList(); + + private final Method method; + + private final Expression expression; + + private volatile TypeDescriptor targetParameterType; + private final boolean canProcessMessageList; + HandlerMethod(Method method, boolean canProcessMessageList) { this.method = method; this.canProcessMessageList = canProcessMessageList; this.expression = this.generateExpression(method); } + Expression getExpression() { return this.expression; } diff --git a/spring-integration-core/src/test/java/org/springframework/integration/handler/ServiceActivatorDefaultFrameworkMethodTests-context.xml b/spring-integration-core/src/test/java/org/springframework/integration/handler/ServiceActivatorDefaultFrameworkMethodTests-context.xml new file mode 100644 index 0000000000..26ccf6b3bc --- /dev/null +++ b/spring-integration-core/src/test/java/org/springframework/integration/handler/ServiceActivatorDefaultFrameworkMethodTests-context.xml @@ -0,0 +1,28 @@ + + + + + + + + + + + + + + + + + + + + + + diff --git a/spring-integration-core/src/test/java/org/springframework/integration/handler/ServiceActivatorDefaultFrameworkMethodTests.java b/spring-integration-core/src/test/java/org/springframework/integration/handler/ServiceActivatorDefaultFrameworkMethodTests.java new file mode 100644 index 0000000000..be20df2b1c --- /dev/null +++ b/spring-integration-core/src/test/java/org/springframework/integration/handler/ServiceActivatorDefaultFrameworkMethodTests.java @@ -0,0 +1,78 @@ +/* + * Copyright 2002-2010 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 org.junit.Test; +import org.junit.runner.RunWith; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.integration.Message; +import org.springframework.integration.MessageChannel; +import org.springframework.integration.channel.QueueChannel; +import org.springframework.integration.handler.AbstractReplyProducingMessageHandler; +import org.springframework.integration.support.MessageBuilder; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; + +/** + * See INT-1688 for background. + * + * @author Mark Fisher + * @since 2.0.1 + */ +@ContextConfiguration +@RunWith(SpringJUnit4ClassRunner.class) +public class ServiceActivatorDefaultFrameworkMethodTests { + + @Autowired + private MessageChannel gatewayTestInputChannel; + + @Autowired + private MessageChannel handlerTestInputChannel; + + @Test + public void testGateway() { + QueueChannel replyChannel = new QueueChannel(); + Message message = MessageBuilder.withPayload("test").setReplyChannel(replyChannel).build(); + this.gatewayTestInputChannel.send(message); + Message reply = replyChannel.receive(0); + assertEquals("gatewayTestInputChannel,gatewayTestService,gateway,requestChannel,bridge,replyChannel", reply.getHeaders().get("history").toString()); + } + + @Test + public void testMessageHandler() { + QueueChannel replyChannel = new QueueChannel(); + Message message = MessageBuilder.withPayload("test").setReplyChannel(replyChannel).build(); + this.handlerTestInputChannel.send(message); + Message reply = replyChannel.receive(0); + assertEquals("TEST", reply.getPayload()); + assertEquals("handlerTestInputChannel,handlerTestService,testMessageHandler", reply.getHeaders().get("history").toString()); + } + + + @SuppressWarnings("unused") + private static class TestMessageHandler extends AbstractReplyProducingMessageHandler { + + @Override + protected Object handleRequestMessage(Message requestMessage) { + return requestMessage.getPayload().toString().toUpperCase(); + } + } + +}