From 848f3a51032c350b76f701273a61dbef6995bbfd Mon Sep 17 00:00:00 2001 From: Gary Russell Date: Wed, 30 Oct 2013 18:57:58 +0200 Subject: [PATCH] INT-3185 Fix Direct Handlers When Exposed as MBean When determining candidates for direct invocation, we need to use the target of any existing proxy (such as JMX Metrics). Copy ServiceActivatorDefaultFrameworkMethodTests to the JMX project and adjust to work with JMX Proxies. INT-3185: fix for AbstractSMHFBean#checkReuse JIRA: https://jira.springsource.org/browse/INT-3185 --- ...ractStandardMessageHandlerFactoryBean.java | 14 +- ...eActivatorDefaultFrameworkMethodTests.java | 2 +- ...torDefaultFrameworkMethodTests-context.xml | 65 ++++++ ...eActivatorDefaultFrameworkMethodTests.java | 202 ++++++++++++++++++ 4 files changed, 275 insertions(+), 8 deletions(-) create mode 100644 spring-integration-jmx/src/test/java/org/springframework/integration/jmx/ServiceActivatorDefaultFrameworkMethodTests-context.xml create mode 100644 spring-integration-jmx/src/test/java/org/springframework/integration/jmx/ServiceActivatorDefaultFrameworkMethodTests.java diff --git a/spring-integration-core/src/main/java/org/springframework/integration/config/AbstractStandardMessageHandlerFactoryBean.java b/spring-integration-core/src/main/java/org/springframework/integration/config/AbstractStandardMessageHandlerFactoryBean.java index 2c4a3f1b8b..a6b0605532 100644 --- a/spring-integration-core/src/main/java/org/springframework/integration/config/AbstractStandardMessageHandlerFactoryBean.java +++ b/spring-integration-core/src/main/java/org/springframework/integration/config/AbstractStandardMessageHandlerFactoryBean.java @@ -77,10 +77,10 @@ abstract class AbstractStandardMessageHandlerFactoryBean extends AbstractSimpleM if (this.targetObject != null) { Assert.state(this.expression == null, "The 'targetObject' and 'expression' properties are mutually exclusive."); - boolean targetIsDirectReplyProducingHandler = this.extractTypeIfPossible(targetObject, - AbstractReplyProducingMessageHandler.class) != null - && this.canBeUsedDirect( - (AbstractReplyProducingMessageHandler) targetObject) // give subclasses a say + AbstractReplyProducingMessageHandler actualHandler = this.extractTypeIfPossible(targetObject, + AbstractReplyProducingMessageHandler.class); + boolean targetIsDirectReplyProducingHandler = actualHandler != null + && this.canBeUsedDirect(actualHandler) // give subclasses a say && this.methodIsHandleMessageOrEmpty(this.targetMethodName); if (this.targetObject instanceof MessageProcessor) { handler = this.createMessageProcessingHandler((MessageProcessor) this.targetObject); @@ -89,9 +89,9 @@ abstract class AbstractStandardMessageHandlerFactoryBean extends AbstractSimpleM if (logger.isDebugEnabled()) { logger.debug("Wiring handler (" + beanName + ") directly into endpoint"); } + this.checkReuse(actualHandler); + this.postProcessReplyProducer(actualHandler); handler = (MessageHandler) targetObject; - this.checkReuse((AbstractReplyProducingMessageHandler) handler); - this.postProcessReplyProducer((AbstractReplyProducingMessageHandler) handler); } else { handler = this.createMethodInvokingHandler(this.targetObject, this.targetMethodName); @@ -120,7 +120,7 @@ abstract class AbstractStandardMessageHandlerFactoryBean extends AbstractSimpleM } private void checkReuse(AbstractReplyProducingMessageHandler replyHandler) { - Assert.isTrue(!referencedReplyProducers.contains(targetObject), + Assert.isTrue(!referencedReplyProducers.contains(replyHandler), "An AbstractReplyProducingMessageHandler may only be referenced once (" + replyHandler.getComponentName() + ") - use scope=\"prototype\""); referencedReplyProducers.add(replyHandler); 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 index 152b41f15e..4756857945 100644 --- 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 @@ -94,7 +94,7 @@ public class ServiceActivatorDefaultFrameworkMethodTests { } @Test - public void testNotOptimizedReplyingMessageHandler() { + public void testOptimizedReplyingMessageHandler() { QueueChannel replyChannel = new QueueChannel(); Message message = MessageBuilder.withPayload("test").setReplyChannel(replyChannel).build(); this.optimizedRefReplyingHandlerTestInputChannel.send(message); diff --git a/spring-integration-jmx/src/test/java/org/springframework/integration/jmx/ServiceActivatorDefaultFrameworkMethodTests-context.xml b/spring-integration-jmx/src/test/java/org/springframework/integration/jmx/ServiceActivatorDefaultFrameworkMethodTests-context.xml new file mode 100644 index 0000000000..a222fe42dc --- /dev/null +++ b/spring-integration-jmx/src/test/java/org/springframework/integration/jmx/ServiceActivatorDefaultFrameworkMethodTests-context.xml @@ -0,0 +1,65 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/spring-integration-jmx/src/test/java/org/springframework/integration/jmx/ServiceActivatorDefaultFrameworkMethodTests.java b/spring-integration-jmx/src/test/java/org/springframework/integration/jmx/ServiceActivatorDefaultFrameworkMethodTests.java new file mode 100644 index 0000000000..90ad33084d --- /dev/null +++ b/spring-integration-jmx/src/test/java/org/springframework/integration/jmx/ServiceActivatorDefaultFrameworkMethodTests.java @@ -0,0 +1,202 @@ +/* + * 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.jmx; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertSame; + +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.core.MessageHandler; +import org.springframework.integration.endpoint.EventDrivenConsumer; +import org.springframework.integration.handler.AbstractReplyProducingMessageHandler; +import org.springframework.integration.handler.MessageProcessor; +import org.springframework.integration.support.MessageBuilder; +import org.springframework.integration.test.util.TestUtils; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; + +/** + * See INT-1688 for background. + * + * @author Mark Fisher + * @author Artem Bilan + * @author Gary Russell + * @since 2.0.1 + */ +@ContextConfiguration +@RunWith(SpringJUnit4ClassRunner.class) +public class ServiceActivatorDefaultFrameworkMethodTests { + + @Autowired + private MessageChannel gatewayTestInputChannel; + + @Autowired + private MessageChannel replyingHandlerTestInputChannel; + + @Autowired + private MessageChannel optimizedRefReplyingHandlerTestInputChannel; + + @Autowired + private MessageChannel replyingHandlerWithStandardMethodTestInputChannel; + + @Autowired + private MessageChannel replyingHandlerWithOtherMethodTestInputChannel; + + @Autowired + private MessageChannel handlerTestInputChannel; + + @Autowired + private MessageChannel processorTestInputChannel; + + @Autowired + private EventDrivenConsumer processorTestService; + + @Autowired + private MessageProcessor testMessageProcessor; + + @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 testReplyingMessageHandler() { + QueueChannel replyChannel = new QueueChannel(); + Message message = MessageBuilder.withPayload("test").setReplyChannel(replyChannel).build(); + this.replyingHandlerTestInputChannel.send(message); + Message reply = replyChannel.receive(0); + assertEquals("TEST", reply.getPayload()); + assertEquals("replyingHandlerTestInputChannel,replyingHandlerTestService", reply.getHeaders().get("history").toString()); + StackTraceElement[] st = (StackTraceElement[]) reply.getHeaders().get("callStack"); + assertEquals("doDispatch", st[15].getMethodName()); // close to the metal + } + + @Test + public void testOptimizedReplyingMessageHandler() { + QueueChannel replyChannel = new QueueChannel(); + Message message = MessageBuilder.withPayload("test").setReplyChannel(replyChannel).build(); + this.optimizedRefReplyingHandlerTestInputChannel.send(message); + Message reply = replyChannel.receive(0); + assertEquals("TEST", reply.getPayload()); + assertEquals("optimizedRefReplyingHandlerTestInputChannel,optimizedRefReplyingHandlerTestService", + reply.getHeaders().get("history").toString()); + StackTraceElement[] st = (StackTraceElement[]) reply.getHeaders().get("callStack"); + assertEquals("doDispatch", st[15].getMethodName()); + } + + @Test + public void testReplyingMessageHandlerWithStandardMethod() { + QueueChannel replyChannel = new QueueChannel(); + Message message = MessageBuilder.withPayload("test").setReplyChannel(replyChannel).build(); + this.replyingHandlerWithStandardMethodTestInputChannel.send(message); + Message reply = replyChannel.receive(0); + assertEquals("TEST", reply.getPayload()); + assertEquals("replyingHandlerWithStandardMethodTestInputChannel,replyingHandlerWithStandardMethodTestService", reply.getHeaders().get("history").toString()); + StackTraceElement[] st = (StackTraceElement[]) reply.getHeaders().get("callStack"); + assertEquals("doDispatch", st[15].getMethodName()); // close to the metal + } + + @Test + public void testReplyingMessageHandlerWithOtherMethod() { + QueueChannel replyChannel = new QueueChannel(); + Message message = MessageBuilder.withPayload("test").setReplyChannel(replyChannel).build(); + this.replyingHandlerWithOtherMethodTestInputChannel.send(message); + Message reply = replyChannel.receive(0); + assertEquals("bar", reply.getPayload()); + assertEquals("replyingHandlerWithOtherMethodTestInputChannel,replyingHandlerWithOtherMethodTestService", 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); + } + +// INT-2399 + @Test + public void testMessageProcessor() { + Object processor = TestUtils.getPropertyValue(processorTestService, "handler.h.advised.targetSource.target.processor"); + assertSame(testMessageProcessor, processor); + + QueueChannel replyChannel = new QueueChannel(); + Message message = MessageBuilder.withPayload("bar").setReplyChannel(replyChannel).build(); + this.processorTestInputChannel.send(message); + Message reply = replyChannel.receive(0); + assertEquals("foo:bar", reply.getPayload()); + assertEquals("processorTestInputChannel,processorTestService", reply.getHeaders().get("history").toString()); + } + + private interface Foo { + + public String foo(String in); + + } + + @SuppressWarnings("unused") + private static class TestReplyingMessageHandler extends AbstractReplyProducingMessageHandler implements Foo { + + @Override + protected Object handleRequestMessage(Message requestMessage) { + Exception e = new RuntimeException(); + StackTraceElement[] st = e.getStackTrace(); + return MessageBuilder.withPayload(requestMessage.getPayload().toString().toUpperCase()) + .setHeader("callStack", st); + } + + public String foo(String in) { + return "bar"; + } + + } + + @SuppressWarnings("unused") + private static class TestMessageHandler implements MessageHandler { + + @Override + public void handleMessage(Message requestMessage) { + Exception e = new RuntimeException(); + StackTraceElement[] st = e.getStackTrace(); + assertEquals("doDispatch", st[28].getMethodName()); + } + } + + @SuppressWarnings("unused") + private static class TestMessageProcessor implements MessageProcessor { + + private String prefix; + + public void setPrefix(String prefix) { + this.prefix = prefix; + } + + public String processMessage(Message message) { + return prefix + ":" + message.getPayload(); + } + } + +}